Merge lp:~dimitern/juju-core/290-lp-1240667-cloud-tools-priority into lp:~go-bot/juju-core/trunk

Proposed by Dimiter Naydenov
Status: Merged
Approved by: Dimiter Naydenov
Approved revision: no longer in the source branch.
Merged at revision: 2312
Proposed branch: lp:~dimitern/juju-core/290-lp-1240667-cloud-tools-priority
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 344 lines (+149/-22)
7 files modified
cloudinit/cloudinit.go (+37/-3)
cloudinit/cloudinit_test.go (+41/-2)
cloudinit/options.go (+19/-2)
cloudinit/sshinit/configure.go (+12/-1)
cloudinit/sshinit/configure_test.go (+12/-2)
environs/cloudinit/cloudinit.go (+21/-9)
environs/cloudinit/cloudinit_test.go (+7/-3)
To merge this branch: bzr merge lp:~dimitern/juju-core/290-lp-1240667-cloud-tools-priority
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+205816@code.launchpad.net

Commit message

Fixed bug #1240667: pin priority for cloud-tools

This introduces several new calls in cloudinit,
which deal with apt sources, their preferences
(/etc/apt/preferences.d/) and packages that we
install from there.

Basically, when deploying a precise machine that
needs to install mongodb-server package, we add
the cloud-tools pocket, but by default this will
add it as an apt source with a higher priority
than the main archive. As a consequence, charms
that try to install packages from main, which
are also in the cloud-tools pocket get the latter,
rather than the former (i.e. the described problem
with python-django's version 1.5 in cloud-tools vs.
1.14 in main, which breaks openstack-dashboard charm).

Now, when adding the cloud-tools archive we also
change its apt preferences, so that its priority
as an apt source is lower than main, which means
when installing mongodb-server during cloudinit
we need to explicitly specity --target-release
'precise-updates/cloud-tools' to pick the version
from cloud-tools.

Live tested on EC2 and works as expected.

https://codereview.appspot.com/61410051/

R=gz, rogpeppe

Description of the change

Fixed bug #1240667: pin priority for cloud-tools

This introduces several new calls in cloudinit,
which deal with apt sources, their preferences
(/etc/apt/preferences.d/) and packages that we
install from there.

Basically, when deploying a precise machine that
needs to install mongodb-server package, we add
the cloud-tools pocket, but by default this will
add it as an apt source with a higher priority
than the main archive. As a consequence, charms
that try to install packages from main, which
are also in the cloud-tools pocket get the latter,
rather than the former (i.e. the described problem
with python-django's version 1.5 in cloud-tools vs.
1.14 in main, which breaks openstack-dashboard charm).

Now, when adding the cloud-tools archive we also
change its apt preferences, so that its priority
as an apt source is lower than main, which means
when installing mongodb-server during cloudinit
we need to explicitly specity --target-release
'precise-updates/cloud-tools' to pick the version
from cloud-tools.

Live tested on EC2 and works as expected.

https://codereview.appspot.com/61410051/

To post a comment you must log in.
Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Reviewers: mp+205816_code.launchpad.net,

Message:
Please take a look.

Description:
Fixed bug #1240667: pin priority for cloud-tools

This introduces several new calls in cloudinit,
which deal with apt sources, their preferences
(/etc/apt/preferences.d/) and packages that we
install from there.

Basically, when deploying a precise machine that
needs to install mongodb-server package, we add
the cloud-tools pocket, but by default this will
add it as an apt source with a higher priority
than the main archive. As a consequence, charms
that try to install packages from main, which
are also in the cloud-tools pocket get the latter,
rather than the former (i.e. the described problem
with python-django's version 1.5 in cloud-tools vs.
1.14 in main, which breaks openstack-dashboard charm).

Now, when adding the cloud-tools archive we also
change its apt preferences, so that its priority
as an apt source is lower than main, which means
when installing mongodb-server during cloudinit
we need to explicitly specity --target-release
'precise-updates/cloud-tools' to pick the version
from cloud-tools.

Live tested on EC2 and works as expected.

https://code.launchpad.net/~dimitern/juju-core/290-lp-1240667-cloud-tools-priority/+merge/205816

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/61410051/

Affected files (+151, -22 lines):
   A [revision details]
   M cloudinit/cloudinit.go
   M cloudinit/cloudinit_test.go
   M cloudinit/options.go
   M cloudinit/sshinit/configure.go
   M cloudinit/sshinit/configure_test.go
   M environs/cloudinit/cloudinit.go
   M environs/cloudinit/cloudinit_test.go

Revision history for this message
Martin Packman (gz) wrote :

LGTM if we get sign-off from smoser.

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go
File cloudinit/options.go (right):

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go#newcode134
cloudinit/options.go:134: cfg.AddPackage(fmt.Sprintf("--target-release
'%s' '%s'", targetRelease, packageName))
This is the only dodgy bit of the operation, I'm not sure if we should
really rely on cloud-init passing through flags on the packages key like
this, but given our tie to a specific older cloud-init on precise, as it
happens to work we can probably get away with it. Let's check with
Scott.

https://codereview.appspot.com/61410051/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM modulo mgz's concerns and some suggestions below.

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go
File cloudinit/options.go (right):

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go#newcode17
cloudinit/options.go:17: // AptPreferencesTemplate defines the format
used to create an
I don't really like using printf format strings as poor-man's
templates, particularly when the data is already in a struct,
and we have text/template.

I'd do something like:

var aptPreferences = template.Must(template.New("").Parse(
`Explanation: {{.Explanation}}
Package: {{.Package}}
Pin: {{.Pin}}
Pin-Priority: {{.PinPriority}}
`))

Then below, you can do:

if prefs != nil {
     var buf bytes.Buffer
     err := aptPreferencesTemplate.Execute(&buf, prefs)
     if err != nil {
         panic(err)
     }
     cfg.AddFile(prefs.Path, buf.String(), 0644)
}

then the template is easier to read and easier to change
in the future.

Also, rather than exporting the template, you could
define a method on AptPreferences to return the
file contents:

e.g.

// FileContents returns file contents suitable
// for setting up the given apt preferences.
func (prefs *AptPreferences) FileContents() string

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go#newcode134
cloudinit/options.go:134: cfg.AddPackage(fmt.Sprintf("--target-release
'%s' '%s'", targetRelease, packageName))
+1 to mgz's remarks.
Also:

cfg.AddPackage(fmt.Sprintf("--target-release %s %s",
utils.ShQuote(targetRelease), utils.ShQuote(packageName)))

https://codereview.appspot.com/61410051/diff/1/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/61410051/diff/1/environs/cloudinit/cloudinit.go#newcode213
environs/cloudinit/cloudinit.go:213: c.AddFile(noncefile,
cfg.MachineNonce, 0644)
+1 to this drive-by, assuming it's tested.

https://codereview.appspot.com/61410051/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Please take a look.

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go
File cloudinit/options.go (right):

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go#newcode17
cloudinit/options.go:17: // AptPreferencesTemplate defines the format
used to create an
On 2014/02/11 17:31:54, rog wrote:
> I don't really like using printf format strings as poor-man's
> templates, particularly when the data is already in a struct,
> and we have text/template.

> I'd do something like:

> var aptPreferences = template.Must(template.New("").Parse(
> `Explanation: {{.Explanation}}
> Package: {{.Package}}
> Pin: {{.Pin}}
> Pin-Priority: {{.PinPriority}}
> `))

> Then below, you can do:

> if prefs != nil {
> var buf bytes.Buffer
> err := aptPreferencesTemplate.Execute(&buf, prefs)
> if err != nil {
> panic(err)
> }
> cfg.AddFile(prefs.Path, buf.String(), 0644)
> }

> then the template is easier to read and easier to change
> in the future.

> Also, rather than exporting the template, you could
> define a method on AptPreferences to return the
> file contents:

> e.g.

> // FileContents returns file contents suitable
> // for setting up the given apt preferences.
> func (prefs *AptPreferences) FileContents() string

Good point, done!

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go#newcode134
cloudinit/options.go:134: cfg.AddPackage(fmt.Sprintf("--target-release
'%s' '%s'", targetRelease, packageName))
On 2014/02/11 17:05:01, gz wrote:
> This is the only dodgy bit of the operation, I'm not sure if we should
really
> rely on cloud-init passing through flags on the packages key like
this, but
> given our tie to a specific older cloud-init on precise, as it happens
to work
> we can probably get away with it. Let's check with Scott.

As discussed on IRC with smoser, this is fine and I tested it to do the
right thing. It's much simpler than passing an entire certificate file
with rigid formatting through cloudinit. I added tests for all newly
generated code.

https://codereview.appspot.com/61410051/diff/1/cloudinit/options.go#newcode134
cloudinit/options.go:134: cfg.AddPackage(fmt.Sprintf("--target-release
'%s' '%s'", targetRelease, packageName))
On 2014/02/11 17:31:54, rog wrote:
> +1 to mgz's remarks.
> Also:

> cfg.AddPackage(fmt.Sprintf("--target-release %s %s",
> utils.ShQuote(targetRelease), utils.ShQuote(packageName)))

I don't want to shquote these here, because they weren't escaped before
and they get escaped at rendering time, somewhat differently in
cloudinit and sshinit, after doing some checks.

https://codereview.appspot.com/61410051/diff/1/environs/cloudinit/cloudinit.go
File environs/cloudinit/cloudinit.go (right):

https://codereview.appspot.com/61410051/diff/1/environs/cloudinit/cloudinit.go#newcode213
environs/cloudinit/cloudinit.go:213: c.AddFile(noncefile,
cfg.MachineNonce, 0644)
On 2014/02/11 17:31:54, rog wrote:
> +1 to this drive-by, assuming it's tested.

Yep, it is - works fine.

https://codereview.appspot.com/61410051/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/61410051/diff/20001/cloudinit/options.go
File cloudinit/options.go (right):

https://codereview.appspot.com/61410051/diff/20001/cloudinit/options.go#newcode91
cloudinit/options.go:91: cfg.AddFile(prefs.Path, prefs.FileContents(),
0644)
Did you live test both add-machine and bootstrap?

I don't see how this could have worked as expected in a live test of
add-machine (which uses cloud-init all the way, as opposed to cloud-init
+ "sshinit"). AddFile is implemented in terms of runcmd; runcmd runs
after packages are installed.

I'm really not keen on this approach at all. I think environs/cloudinit
should be modified to add a bootcmd in ConfigureBasic. Bootcmds are run
before packages are installed.

https://codereview.appspot.com/61410051/diff/20001/cloudinit/sshinit/configure.go
File cloudinit/sshinit/configure.go (right):

https://codereview.appspot.com/61410051/diff/20001/cloudinit/sshinit/configure.go#newcode144
cloudinit/sshinit/configure.go:144: if src.Prefs != nil {
Is there a good reason to duplicate this logic here and in
cloudinit/options.go? Anything that's not core cloud-init functionality
should really belong in environs/cloudinit.

https://codereview.appspot.com/61410051/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

https://codereview.appspot.com/61410051/diff/20001/cloudinit/options.go
File cloudinit/options.go (right):

https://codereview.appspot.com/61410051/diff/20001/cloudinit/options.go#newcode91
cloudinit/options.go:91: cfg.AddFile(prefs.Path, prefs.FileContents(),
0644)
On 2014/02/12 01:25:42, axw wrote:
> Did you live test both add-machine and bootstrap?

> I don't see how this could have worked as expected in a live test of
add-machine
> (which uses cloud-init all the way, as opposed to cloud-init +
"sshinit").
> AddFile is implemented in terms of runcmd; runcmd runs after packages
are
> installed.

> I'm really not keen on this approach at all. I think
environs/cloudinit should
> be modified to add a bootcmd in ConfigureBasic. Bootcmds are run
before packages
> are installed.

It works all the same - the repository is added and with it the prefs
file gets created. Just tested it carefully again with add-machine and a
fresh precise VM.

About the approach, since it fixes the bug, which I was aiming to do,
it's adequate, if not the best one, I agree. If you feel strongly that
it should be changed, please propose a follow-up?

https://codereview.appspot.com/61410051/diff/20001/cloudinit/sshinit/configure.go
File cloudinit/sshinit/configure.go (right):

https://codereview.appspot.com/61410051/diff/20001/cloudinit/sshinit/configure.go#newcode144
cloudinit/sshinit/configure.go:144: if src.Prefs != nil {
On 2014/02/12 01:25:42, axw wrote:
> Is there a good reason to duplicate this logic here and in
cloudinit/options.go?
> Anything that's not core cloud-init functionality should really belong
in
> environs/cloudinit.

This is due to the fact that runcmds are run after everything else, as
you mentioned. So I had to pull it out here in order for it to work.

https://codereview.appspot.com/61410051/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cloudinit/cloudinit.go'
2--- cloudinit/cloudinit.go 2013-11-26 12:24:48 +0000
3+++ cloudinit/cloudinit.go 2014-02-11 20:01:44 +0000
4@@ -7,6 +7,9 @@
5 package cloudinit
6
7 import (
8+ "bytes"
9+ "text/template"
10+
11 yaml "launchpad.net/goyaml"
12 )
13
14@@ -38,10 +41,41 @@
15 }
16
17 // AptSource is an apt(8) source, comprising a source location,
18-// with an optional Key.
19+// with an optional Key, and optional apt_preferences(5).
20 type AptSource struct {
21- Source string `yaml:"source"`
22- Key string `yaml:"key,omitempty"`
23+ Source string `yaml:"source"`
24+ Key string `yaml:"key,omitempty"`
25+ Prefs *AptPreferences `yaml:"-"`
26+}
27+
28+// AptPreferences is a set of apt_preferences(5) compatible
29+// preferences for an apt source. It can be used to override the
30+// default priority for the source. Path where the file will be
31+// created (usually in /etc/apt/preferences.d/).
32+type AptPreferences struct {
33+ Path string
34+ Explanation string
35+ Package string
36+ Pin string
37+ PinPriority int
38+}
39+
40+// FileContents generates an apt_preferences(5) file from the fields
41+// in prefs.
42+func (prefs *AptPreferences) FileContents() string {
43+ const prefTemplate = `
44+Explanation: {{.Explanation}}
45+Package: {{.Package}}
46+Pin: {{.Pin}}
47+Pin-Priority: {{.PinPriority}}
48+`
49+ var buf bytes.Buffer
50+ t := template.Must(template.New("").Parse(prefTemplate[1:]))
51+ err := t.Execute(&buf, prefs)
52+ if err != nil {
53+ panic(err)
54+ }
55+ return buf.String()
56 }
57
58 // command represents a shell command.
59
60=== modified file 'cloudinit/cloudinit_test.go'
61--- cloudinit/cloudinit_test.go 2013-12-16 07:20:01 +0000
62+++ cloudinit/cloudinit_test.go 2014-02-11 20:01:44 +0000
63@@ -177,7 +177,35 @@
64 "AptSources",
65 "apt_sources:\n- source: keyName\n key: someKey\n",
66 func(cfg *cloudinit.Config) {
67- cfg.AddAptSource("keyName", "someKey")
68+ cfg.AddAptSource("keyName", "someKey", nil)
69+ },
70+ },
71+ {
72+ "AptSources with preferences",
73+ `apt_sources:
74+- source: keyName
75+ key: someKey
76+runcmd:
77+- install -D -m 644 /dev/null '/some/path'
78+- 'printf ''%s\n'' ''Explanation: test
79+
80+ Package: *
81+
82+ Pin: release n=series
83+
84+ Pin-Priority: 123
85+
86+ '' > ''/some/path'''
87+`,
88+ func(cfg *cloudinit.Config) {
89+ prefs := &cloudinit.AptPreferences{
90+ Path: "/some/path",
91+ Explanation: "test",
92+ Package: "*",
93+ Pin: "release n=series",
94+ PinPriority: 123,
95+ }
96+ cfg.AddAptSource("keyName", "someKey", prefs)
97 },
98 },
99 {
100@@ -189,6 +217,13 @@
101 },
102 },
103 {
104+ "Packages with --target-release",
105+ "packages:\n- --target-release 'precise-updates/cloud-tools' 'mongodb-server'\n",
106+ func(cfg *cloudinit.Config) {
107+ cfg.AddPackageFromTargetRelease("mongodb-server", "precise-updates/cloud-tools")
108+ },
109+ },
110+ {
111 "BootCmd",
112 "bootcmd:\n- ls > /dev\n- - ls\n - '>with space'\n",
113 func(cfg *cloudinit.Config) {
114@@ -276,7 +311,11 @@
115 c.Assert(cfg.Packages(), gc.HasLen, 0)
116 cfg.AddPackage("a b c")
117 cfg.AddPackage("d!")
118- c.Assert(cfg.Packages(), gc.DeepEquals, []string{"a b c", "d!"})
119+ expectedPackages := []string{"a b c", "d!"}
120+ c.Assert(cfg.Packages(), gc.DeepEquals, expectedPackages)
121+ cfg.AddPackageFromTargetRelease("package", "series")
122+ expectedPackages = append(expectedPackages, "--target-release 'series' 'package'")
123+ c.Assert(cfg.Packages(), gc.DeepEquals, expectedPackages)
124 }
125
126 func (S) TestSetOutput(c *gc.C) {
127
128=== modified file 'cloudinit/options.go'
129--- cloudinit/options.go 2013-12-20 00:30:15 +0000
130+++ cloudinit/options.go 2014-02-11 20:01:44 +0000
131@@ -10,6 +10,10 @@
132 "launchpad.net/juju-core/utils/ssh"
133 )
134
135+// CloudToolsPrefsPath defines the default location of
136+// apt_preferences(5) file for the cloud-tools pocket.
137+const CloudToolsPrefsPath = "/etc/apt/preferences.d/50-cloud-tools"
138+
139 // SetAttr sets an arbitrary attribute in the cloudinit config.
140 // If value is nil the attribute will be deleted; otherwise
141 // the value will be marshalled according to the rules
142@@ -73,13 +77,19 @@
143
144 // AddAptSource adds an apt source. The key holds the
145 // public key of the source, in the form expected by apt-key(8).
146-func (cfg *Config) AddAptSource(name, key string) {
147+func (cfg *Config) AddAptSource(name, key string, prefs *AptPreferences) {
148 src, _ := cfg.attrs["apt_sources"].([]*AptSource)
149 cfg.attrs["apt_sources"] = append(src,
150 &AptSource{
151 Source: name,
152 Key: key,
153- })
154+ Prefs: prefs,
155+ },
156+ )
157+ if prefs != nil {
158+ // Create the apt preferences file.
159+ cfg.AddFile(prefs.Path, prefs.FileContents(), 0644)
160+ }
161 }
162
163 // AptSources returns the apt sources added with AddAptSource.
164@@ -102,6 +112,13 @@
165 cfg.attrs["packages"] = append(cfg.Packages(), name)
166 }
167
168+// AddPackageFromTargetRelease adds a package to be installed using
169+// the given release, passed to apt-get with the --target-release
170+// argument.
171+func (cfg *Config) AddPackageFromTargetRelease(packageName, targetRelease string) {
172+ cfg.AddPackage(fmt.Sprintf("--target-release '%s' '%s'", targetRelease, packageName))
173+}
174+
175 // Packages returns a list of packages that will be
176 // installed on first boot.
177 func (cfg *Config) Packages() []string {
178
179=== modified file 'cloudinit/sshinit/configure.go'
180--- cloudinit/sshinit/configure.go 2014-01-28 04:58:43 +0000
181+++ cloudinit/sshinit/configure.go 2014-02-11 20:01:44 +0000
182@@ -141,6 +141,12 @@
183 }
184 cmds = append(cmds, cloudinit.LogProgressCmd("Adding apt repository: %s", src.Source))
185 cmds = append(cmds, "add-apt-repository -y "+utils.ShQuote(src.Source))
186+ if src.Prefs != nil {
187+ path := utils.ShQuote(src.Prefs.Path)
188+ contents := utils.ShQuote(src.Prefs.FileContents())
189+ cmds = append(cmds, "install -D -m 644 /dev/null "+path)
190+ cmds = append(cmds, `printf '%s\n' `+contents+` > `+path)
191+ }
192 }
193 if len(cfg.AptSources()) > 0 || cfg.AptUpdate() {
194 cmds = append(cmds, cloudinit.LogProgressCmd("Running apt-get update"))
195@@ -152,7 +158,12 @@
196 }
197 for _, pkg := range cfg.Packages() {
198 cmds = append(cmds, cloudinit.LogProgressCmd("Installing package: %s", pkg))
199- cmd := fmt.Sprintf(aptget+"install %s", utils.ShQuote(pkg))
200+ if !strings.Contains(pkg, "--target-release") {
201+ // We only need to shquote the package name if it does not
202+ // contain additional arguments.
203+ pkg = utils.ShQuote(pkg)
204+ }
205+ cmd := fmt.Sprintf(aptget+"install %s", pkg)
206 cmds = append(cmds, cmd)
207 }
208 if len(cmds) > 0 {
209
210=== modified file 'cloudinit/sshinit/configure_test.go'
211--- cloudinit/sshinit/configure_test.go 2014-01-16 07:10:50 +0000
212+++ cloudinit/sshinit/configure_test.go 2014-02-11 20:01:44 +0000
213@@ -103,6 +103,16 @@
214 checkIff(gc.Matches, needsCloudTools),
215 "(.|\n)*add-apt-repository.*cloud-tools(.|\n)*",
216 )
217+ c.Assert(
218+ script,
219+ checkIff(gc.Matches, needsCloudTools),
220+ "(.|\n)*Pin: release n=precise-updates/cloud-tools\nPin-Priority: 400(.|\n)*",
221+ )
222+ c.Assert(
223+ script,
224+ checkIff(gc.Matches, needsCloudTools),
225+ "(.|\n)*install -D -m 644 /dev/null '/etc/apt/preferences.d/50-cloud-tools'(.|\n)*",
226+ )
227
228 // Only Quantal requires the PPA (for mongo).
229 needsJujuPPA := series == "quantal"
230@@ -143,7 +153,7 @@
231 cfg.SetAptUpdate(true)
232 assertScriptMatches(c, cfg, aptGetUpdatePattern, true)
233 cfg.SetAptUpdate(false)
234- cfg.AddAptSource("source", "key")
235+ cfg.AddAptSource("source", "key", nil)
236 assertScriptMatches(c, cfg, aptGetUpdatePattern, true)
237 }
238
239@@ -152,7 +162,7 @@
240 aptGetUpgradePattern := aptgetRegexp + "upgrade(.|\n)*"
241 cfg := cloudinit.New()
242 cfg.SetAptUpdate(true)
243- cfg.AddAptSource("source", "key")
244+ cfg.AddAptSource("source", "key", nil)
245 assertScriptMatches(c, cfg, aptGetUpgradePattern, false)
246 cfg.SetAptUpgrade(true)
247 assertScriptMatches(c, cfg, aptGetUpgradePattern, true)
248
249=== modified file 'environs/cloudinit/cloudinit.go'
250--- environs/cloudinit/cloudinit.go 2014-02-05 15:41:44 +0000
251+++ environs/cloudinit/cloudinit.go 2014-02-11 20:01:44 +0000
252@@ -209,11 +209,8 @@
253 // Note: this must be the last runcmd we do in ConfigureBasic, as
254 // the presence of the nonce file is used to gate the remainder
255 // of synchronous bootstrap.
256- noncefile := shquote(path.Join(cfg.DataDir, NonceFile))
257- c.AddScripts(
258- fmt.Sprintf("install -D -m %o /dev/null %s", 0644, noncefile),
259- fmt.Sprintf(`printf '%%s\n' %s > %s`, shquote(cfg.MachineNonce), noncefile),
260- )
261+ noncefile := path.Join(cfg.DataDir, NonceFile)
262+ c.AddFile(noncefile, cfg.MachineNonce, 0644)
263 return nil
264 }
265
266@@ -348,9 +345,17 @@
267
268 if cfg.NeedMongoPPA() {
269 const key = "" // key is loaded from PPA
270- c.AddAptSource("ppa:juju/stable", key)
271- }
272- c.AddPackage("mongodb-server")
273+ c.AddAptSource("ppa:juju/stable", key, nil)
274+ }
275+ if cfg.Tools.Version.Series == "precise" {
276+ // In precise we add the cloud-tools pocket and
277+ // pin it with a lower priority, so we need to
278+ // explicitly specify the target release when
279+ // installing mongodb-server from there.
280+ c.AddPackageFromTargetRelease("mongodb-server", "precise-updates/cloud-tools")
281+ } else {
282+ c.AddPackage("mongodb-server")
283+ }
284 }
285 certKey := string(cfg.StateServerCert) + string(cfg.StateServerKey)
286 c.AddFile(cfg.dataFile("server.pem"), certKey, 0600)
287@@ -601,7 +606,14 @@
288 }
289 const url = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
290 name := fmt.Sprintf("deb %s %s-updates/cloud-tools main", url, series)
291- c.AddAptSource(name, CanonicalCloudArchiveSigningKey)
292+ prefs := &cloudinit.AptPreferences{
293+ Path: cloudinit.CloudToolsPrefsPath,
294+ Explanation: "Pin with lower priority, not to interfere with charms",
295+ Package: "*",
296+ Pin: fmt.Sprintf("release n=%s-updates/cloud-tools", series),
297+ PinPriority: 400,
298+ }
299+ c.AddAptSource(name, CanonicalCloudArchiveSigningKey, prefs)
300 }
301
302 func (cfg *MachineConfig) NeedMongoPPA() bool {
303
304=== modified file 'environs/cloudinit/cloudinit_test.go'
305--- environs/cloudinit/cloudinit_test.go 2014-01-30 21:20:10 +0000
306+++ environs/cloudinit/cloudinit_test.go 2014-02-11 20:01:44 +0000
307@@ -119,6 +119,8 @@
308 printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-0/format'
309 install -m 600 /dev/null '/var/lib/juju/agents/machine-0/agent\.conf'
310 printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-0/agent\.conf'
311+install -D -m 644 /dev/null '/etc/apt/preferences\.d/50-cloud-tools'
312+printf '%s\\n' '.*' > '/etc/apt/preferences\.d/50-cloud-tools'
313 install -D -m 600 /dev/null '/var/lib/juju/system-identity'
314 printf '%s\\n' '.*' > '/var/lib/juju/system-identity'
315 install -D -m 600 /dev/null '/var/lib/juju/server\.pem'
316@@ -559,7 +561,7 @@
317 }
318 }
319
320-// CheckPackage checks that the cloudinit will or won't install the given
321+// checkPackage checks that the cloudinit will or won't install the given
322 // package, depending on the value of match.
323 func checkPackage(c *gc.C, x map[interface{}]interface{}, pkg string, match bool) {
324 pkgs0 := x["packages"]
325@@ -575,7 +577,9 @@
326 found := false
327 for _, p0 := range pkgs {
328 p := p0.(string)
329- if p == pkg {
330+ hasTargetRelease := strings.Contains(p, "--target-release")
331+ hasQuotedPkg := strings.Contains(p, "'"+pkg+"'")
332+ if p == pkg || (hasTargetRelease && hasQuotedPkg) {
333 found = true
334 }
335 }
336@@ -587,7 +591,7 @@
337 }
338 }
339
340-// CheckAptSources checks that the cloudinit will or won't install the given
341+// checkAptSources checks that the cloudinit will or won't install the given
342 // source, depending on the value of match.
343 func checkAptSource(c *gc.C, x map[interface{}]interface{}, source, key string, match bool) {
344 sources0 := x["apt_sources"]

Subscribers

People subscribed via source and target branches

to status/vote changes: