Merge lp:~axwalk/juju-core/provisioningscript-client-api into lp:~go-bot/juju-core/trunk
- provisioningscript-client-api
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Andrew Wilkins |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2216 |
Proposed branch: | lp:~axwalk/juju-core/provisioningscript-client-api |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
549 lines (+197/-69) 11 files modified
cloudinit/sshinit/configure.go (+3/-3) cloudinit/sshinit/configure_test.go (+5/-4) cloudinit/sshinit/export_test.go (+6/-0) cloudinit/sshinit/suite_test.go (+1/-1) environs/manual/provisioner.go (+3/-43) environs/manual/provisioner_test.go (+3/-2) state/api/client.go (+15/-3) state/api/params/params.go (+14/-2) state/apiserver/client/client.go (+28/-1) state/apiserver/client/client_test.go (+67/-4) state/statecmd/machineconfig.go (+52/-6) |
To merge this branch: | bzr merge lp:~axwalk/juju-core/provisioningscript-client-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+201893@code.launchpad.net |
Commit message
ProvisioningScript client API
This is a new client API that, given the ID and nonce
of a machine injected into state, returns a script
that can be executed on that machine to install and
configure a Juju machine agent.
The existing MachineConfig client API call has been
modified: it was taking a series and arch. Series is
guaranteed to be in state.Machine; the arch is not,
but we make it a requirement for calling MachineConfig.
In the only place where MachineConfig is already called,
this is true. ProvisioningScript is expected to be
used similarly.
I have tested "live" by temporarily modifying
environs/
and then running it via utils/ssh.Command.
Description of the change
ProvisioningScript client API
This is a new client API that, given the ID and nonce
of a machine injected into state, returns a script
that can be executed on that machine to install and
configure a Juju machine agent.
The existing MachineConfig client API call has been
modified: it was taking a series and arch. Series is
guaranteed to be in state.Machine; the arch is not,
but we make it a requirement for calling MachineConfig.
In the only place where MachineConfig is already called,
this is true. ProvisioningScript is expected to be
used similarly.
I have tested "live" by temporarily modifying
environs/
and then running it via utils/ssh.Command.
Andrew Wilkins (axwalk) wrote : | # |
Roger Peppe (rogpeppe) wrote : | # |
LGTM
https:/
File state/statecmd/
https:/
state/statecmd/
FinishMachineCo
nonce, dataDir string) (*cloudinit.
This seems fine, but I don't entirely understand its rationale. Perhaps
you could explain why this needs to be separate from MachineConfig?
Dimiter Naydenov (dimitern) wrote : | # |
Looking good, apart from some grumbles below.
https:/
File state/api/client.go (right):
https:/
state/api/
I think it's a bit better like this:
err = ...
if err != nil {
return "", err
}
return result.Script, nil
(like other similar methods).
https:/
File state/api/
https:/
state/api/
I'm not sure we can remove these fields just yet (1.16 compatibility).
https:/
File state/api/
https:/
state/api/
s/ProvisioningS
well)
https:/
File state/apiserver
https:/
state/apiserver
"launchpad.
I'd prefer corecloudinit for consistency sake.
https:/
File state/apiserver
https:/
state/apiserver
"launchpad.
corecloudinit please
https:/
state/apiserver
I'd like to see a test about using ProvisioningScript against 1.16 API
server, to make sure we're handling it gracefully when it's not
implemented.
https:/
File state/statecmd/
https:/
state/statecmd/
not set for %s", machine.Tag())
s/for %s/for %q/ ?
https:/
state/statecmd/
FinishMachineCo
nonce, dataDir string) (*cloudinit.
On 2014/01/16 12:02:47, rog wrote:
> This seems fine, but I don't entirely understand its rationale.
Perhaps you
> could explain why this needs to be separate from MachineConfig?
I think it's mostly because of the provisioner, which also needs to call
this separately from MachineConfig.
Kapil Thangavelu (hazmat) wrote : | # |
On 2014/01/16 12:02:47, rog wrote:
> LGTM
> This seems fine, but I don't entirely understand its rationale.
Perhaps you
> could explain why this needs to be separate from MachineConfig?
Machine config exposes data (mostly internal imo) via the api that a
client has to then format into an install script. This is a fragile
error prone process as it is effectively reimplementing internal juju
details and the various cloudinit modules and options within juju core
are subject to change without notice. By exposing a provisioning script
to the client, the client basically gets a blob that it can use for
initialization which is a much cleaner api then exposing internal
details and leaving the burden on the client.
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
https:/
File state/api/client.go (right):
https:/
state/api/
On 2014/01/16 13:08:18, dimitern wrote:
> I think it's a bit better like this:
> err = ...
> if err != nil {
> return "", err
> }
> return result.Script, nil
> (like other similar methods).
Sure, though there's quite a lot that do it the way I did it (I just
copied MachineConfig).
https:/
File state/api/
https:/
state/api/
On 2014/01/16 13:08:18, dimitern wrote:
> I'm not sure we can remove these fields just yet (1.16 compatibility).
MachineConfig was added in 1.17.0. I may just remove MachineConfig
altogether. It's only used by manual provisioning, which could just use
the new API instead.
https:/
File state/api/
https:/
state/api/
On 2014/01/16 13:08:18, dimitern wrote:
> s/ProvisioningS
well)
Done.
https:/
File state/apiserver
https:/
state/apiserver
"launchpad.
On 2014/01/16 13:08:18, dimitern wrote:
> I'd prefer corecloudinit for consistency sake.
coreCloudinit is consistent with the rest of the code base (there are
zero instances of corecloudinit, a bunch of coreCloudinit).
https:/
File state/apiserver
https:/
state/apiserver
On 2014/01/16 13:08:18, dimitern wrote:
> I'd like to see a test about using ProvisioningScript against 1.16 API
server,
> to make sure we're handling it gracefully when it's not implemented.
I was going to do this, but it doesn't make sense to yet*. The only
place it's used is by an external client, where the version is
guaranteed to include the method.
* When I change environs/manual to use ProvisioningScript instead of
MachineConfig, it may make sense. However, I don't see *any* tests for
compatibility, nor do I know how this would even be accomplished. If
there really are tests for the 1dot16 code paths, please point me at
one.
https:/
File state/statecmd/
https:/
state/statecmd/
not...
Preview Diff
1 | === modified file 'cloudinit/sshinit/configure.go' |
2 | --- cloudinit/sshinit/configure.go 2014-01-13 06:58:19 +0000 |
3 | +++ cloudinit/sshinit/configure.go 2014-01-17 03:18:53 +0000 |
4 | @@ -36,7 +36,7 @@ |
5 | // and executes a script that carries out cloud-config. |
6 | func Configure(params ConfigureParams) error { |
7 | logger.Infof("Provisioning machine agent on %s", params.Host) |
8 | - script, err := generateScript(params.Config) |
9 | + script, err := ConfigureScript(params.Config) |
10 | if err != nil { |
11 | return err |
12 | } |
13 | @@ -51,9 +51,9 @@ |
14 | return cmd.Run() |
15 | } |
16 | |
17 | -// generateScript generates the script that applies |
18 | +// ConfigureScript generates the bash script that applies |
19 | // the specified cloud-config. |
20 | -func generateScript(cloudcfg *cloudinit.Config) (string, error) { |
21 | +func ConfigureScript(cloudcfg *cloudinit.Config) (string, error) { |
22 | // TODO(axw): 2013-08-23 bug 1215777 |
23 | // Carry out configuration for ssh-keys-per-user, |
24 | // machine-updates-authkeys, using cloud-init config. |
25 | |
26 | === modified file 'cloudinit/sshinit/configure_test.go' |
27 | --- cloudinit/sshinit/configure_test.go 2013-12-18 21:47:48 +0000 |
28 | +++ cloudinit/sshinit/configure_test.go 2014-01-17 03:18:53 +0000 |
29 | @@ -1,7 +1,7 @@ |
30 | // Copyright 2013 Canonical Ltd. |
31 | // Licensed under the AGPLv3, see LICENCE file for details. |
32 | |
33 | -package sshinit |
34 | +package sshinit_test |
35 | |
36 | import ( |
37 | "regexp" |
38 | @@ -9,6 +9,7 @@ |
39 | gc "launchpad.net/gocheck" |
40 | |
41 | "launchpad.net/juju-core/cloudinit" |
42 | + "launchpad.net/juju-core/cloudinit/sshinit" |
43 | "launchpad.net/juju-core/constraints" |
44 | "launchpad.net/juju-core/environs" |
45 | envcloudinit "launchpad.net/juju-core/environs/cloudinit" |
46 | @@ -79,12 +80,12 @@ |
47 | return gc.Not(checker) |
48 | } |
49 | |
50 | -var aptgetRegexp = "(.|\n)*" + regexp.QuoteMeta(aptget) |
51 | +var aptgetRegexp = "(.|\n)*" + regexp.QuoteMeta(sshinit.Aptget) |
52 | |
53 | func (s *configureSuite) TestAptSources(c *gc.C) { |
54 | for _, series := range allSeries { |
55 | vers := version.MustParseBinary("1.16.0-" + series + "-amd64") |
56 | - script, err := generateScript(s.getCloudConfig(c, true, vers)) |
57 | + script, err := sshinit.ConfigureScript(s.getCloudConfig(c, true, vers)) |
58 | c.Assert(err, gc.IsNil) |
59 | |
60 | // Only Precise requires the cloud-tools pocket. |
61 | @@ -122,7 +123,7 @@ |
62 | } |
63 | |
64 | func assertScriptMatches(c *gc.C, cfg *cloudinit.Config, pattern string, match bool) { |
65 | - script, err := generateScript(cfg) |
66 | + script, err := sshinit.ConfigureScript(cfg) |
67 | c.Assert(err, gc.IsNil) |
68 | checker := gc.Matches |
69 | if !match { |
70 | |
71 | === added file 'cloudinit/sshinit/export_test.go' |
72 | --- cloudinit/sshinit/export_test.go 1970-01-01 00:00:00 +0000 |
73 | +++ cloudinit/sshinit/export_test.go 2014-01-17 03:18:53 +0000 |
74 | @@ -0,0 +1,6 @@ |
75 | +// Copyright 2014 Canonical Ltd. |
76 | +// Licensed under the AGPLv3, see LICENCE file for details. |
77 | + |
78 | +package sshinit |
79 | + |
80 | +const Aptget = aptget |
81 | |
82 | === modified file 'cloudinit/sshinit/suite_test.go' |
83 | --- cloudinit/sshinit/suite_test.go 2013-11-15 08:47:05 +0000 |
84 | +++ cloudinit/sshinit/suite_test.go 2014-01-17 03:18:53 +0000 |
85 | @@ -1,7 +1,7 @@ |
86 | // Copyright 2013 Canonical Ltd. |
87 | // Licensed under the AGPLv3, see LICENCE file for details. |
88 | |
89 | -package sshinit |
90 | +package sshinit_test |
91 | |
92 | import ( |
93 | stdtesting "testing" |
94 | |
95 | === modified file 'environs/manual/provisioner.go' |
96 | --- environs/manual/provisioner.go 2014-01-09 09:39:56 +0000 |
97 | +++ environs/manual/provisioner.go 2014-01-17 03:18:53 +0000 |
98 | @@ -14,8 +14,6 @@ |
99 | |
100 | coreCloudinit "launchpad.net/juju-core/cloudinit" |
101 | "launchpad.net/juju-core/cloudinit/sshinit" |
102 | - "launchpad.net/juju-core/constraints" |
103 | - "launchpad.net/juju-core/environs" |
104 | "launchpad.net/juju-core/environs/cloudinit" |
105 | "launchpad.net/juju-core/environs/config" |
106 | "launchpad.net/juju-core/instance" |
107 | @@ -118,10 +116,6 @@ |
108 | if err != nil { |
109 | return "", err |
110 | } |
111 | - arch := "" |
112 | - if machineParams.HardwareCharacteristics.Arch != nil { |
113 | - arch = *machineParams.HardwareCharacteristics.Arch |
114 | - } |
115 | |
116 | // Inform Juju that the machine exists. |
117 | machineId, err = recordMachineInState(client, *machineParams) |
118 | @@ -139,20 +133,15 @@ |
119 | |
120 | var configParameters params.MachineConfig |
121 | if stateConn == nil { |
122 | - configParameters, err = client.MachineConfig(machineId, machineParams.Series, arch) |
123 | + configParameters, err = client.MachineConfig(machineId) |
124 | } else { |
125 | - request := params.MachineConfigParams{ |
126 | - MachineId: machineId, |
127 | - Series: machineParams.Series, |
128 | - Arch: arch, |
129 | - } |
130 | - configParameters, err = statecmd.MachineConfig(stateConn.State, request) |
131 | + configParameters, err = statecmd.MachineConfig(stateConn.State, machineId) |
132 | } |
133 | if err != nil { |
134 | return "", err |
135 | } |
136 | // Gather the information needed by the machine agent to run the provisioning script. |
137 | - mcfg, err := finishMachineConfig(configParameters, machineId, machineParams.Nonce, args.DataDir) |
138 | + mcfg, err := statecmd.FinishMachineConfig(configParameters, machineId, machineParams.Nonce, args.DataDir) |
139 | if err != nil { |
140 | return machineId, err |
141 | } |
142 | @@ -300,35 +289,6 @@ |
143 | return machineParams, nil |
144 | } |
145 | |
146 | -func finishMachineConfig(configParameters params.MachineConfig, machineId, nonce, dataDir string) (*cloudinit.MachineConfig, error) { |
147 | - stateInfo := &state.Info{ |
148 | - Addrs: configParameters.StateAddrs, |
149 | - Password: configParameters.Password, |
150 | - Tag: configParameters.Tag, |
151 | - CACert: configParameters.CACert, |
152 | - } |
153 | - apiInfo := &api.Info{ |
154 | - Addrs: configParameters.APIAddrs, |
155 | - Password: configParameters.Password, |
156 | - Tag: configParameters.Tag, |
157 | - CACert: configParameters.CACert, |
158 | - } |
159 | - environConfig, err := config.New(config.NoDefaults, configParameters.EnvironAttrs) |
160 | - if err != nil { |
161 | - return nil, err |
162 | - } |
163 | - mcfg := environs.NewMachineConfig(machineId, nonce, stateInfo, apiInfo) |
164 | - if dataDir != "" { |
165 | - mcfg.DataDir = dataDir |
166 | - } |
167 | - mcfg.Tools = configParameters.Tools |
168 | - err = environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) |
169 | - if err != nil { |
170 | - return nil, err |
171 | - } |
172 | - return mcfg, nil |
173 | -} |
174 | - |
175 | func provisionMachineAgent(host string, mcfg *cloudinit.MachineConfig, stderr io.Writer) error { |
176 | cloudcfg := coreCloudinit.New() |
177 | if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil { |
178 | |
179 | === modified file 'environs/manual/provisioner_test.go' |
180 | --- environs/manual/provisioner_test.go 2014-01-09 09:39:56 +0000 |
181 | +++ environs/manual/provisioner_test.go 2014-01-17 03:18:53 +0000 |
182 | @@ -14,6 +14,7 @@ |
183 | "launchpad.net/juju-core/juju/testing" |
184 | "launchpad.net/juju-core/state" |
185 | "launchpad.net/juju-core/state/api/params" |
186 | + "launchpad.net/juju-core/state/statecmd" |
187 | jc "launchpad.net/juju-core/testing/checkers" |
188 | "launchpad.net/juju-core/version" |
189 | ) |
190 | @@ -119,9 +120,9 @@ |
191 | |
192 | // Now check what we would've configured it with. |
193 | client := s.APIConn.State.Client() |
194 | - configParams, err := client.MachineConfig(machineId, series, arch) |
195 | + configParams, err := client.MachineConfig(machineId) |
196 | c.Assert(err, gc.IsNil) |
197 | - mcfg, err := finishMachineConfig(configParams, machineId, state.BootstrapNonce, "/var/lib/juju") |
198 | + mcfg, err := statecmd.FinishMachineConfig(configParams, machineId, state.BootstrapNonce, "/var/lib/juju") |
199 | c.Assert(err, gc.IsNil) |
200 | c.Check(mcfg, gc.NotNil) |
201 | c.Check(mcfg.APIInfo, gc.NotNil) |
202 | |
203 | === modified file 'state/api/client.go' |
204 | --- state/api/client.go 2014-01-12 20:28:43 +0000 |
205 | +++ state/api/client.go 2014-01-17 03:18:53 +0000 |
206 | @@ -171,16 +171,28 @@ |
207 | |
208 | // MachineConfig returns information from the environment config that are |
209 | // needed for machine cloud-init. |
210 | -func (c *Client) MachineConfig(machineId, series, arch string) (result params.MachineConfig, err error) { |
211 | +func (c *Client) MachineConfig(machineId string) (result params.MachineConfig, err error) { |
212 | args := params.MachineConfigParams{ |
213 | MachineId: machineId, |
214 | - Series: series, |
215 | - Arch: arch, |
216 | } |
217 | err = c.st.Call("Client", "", "MachineConfig", args, &result) |
218 | return result, err |
219 | } |
220 | |
221 | +// ProvisioningScript returns a shell script that, when run, |
222 | +// provisions a machine agent on the machine executing the script. |
223 | +func (c *Client) ProvisioningScript(machineId, nonce string) (script string, err error) { |
224 | + args := params.ProvisioningScriptParams{ |
225 | + MachineId: machineId, |
226 | + Nonce: nonce, |
227 | + } |
228 | + var result params.ProvisioningScriptResult |
229 | + if err = c.st.Call("Client", "", "ProvisioningScript", args, &result); err != nil { |
230 | + return "", err |
231 | + } |
232 | + return result.Script, nil |
233 | +} |
234 | + |
235 | // DestroyMachines removes a given set of machines. |
236 | func (c *Client) DestroyMachines(machines ...string) error { |
237 | params := params.DestroyMachines{MachineNames: machines} |
238 | |
239 | === modified file 'state/api/params/params.go' |
240 | --- state/api/params/params.go 2014-01-07 07:35:12 +0000 |
241 | +++ state/api/params/params.go 2014-01-17 03:18:53 +0000 |
242 | @@ -518,8 +518,6 @@ |
243 | |
244 | type MachineConfigParams struct { |
245 | MachineId string |
246 | - Series string |
247 | - Arch string |
248 | } |
249 | |
250 | // MachineConfig contains information from the environment config that is |
251 | @@ -535,6 +533,20 @@ |
252 | Password string |
253 | } |
254 | |
255 | +// ProvisioningScriptParams contains the parameters for the |
256 | +// ProvisioningScript client API call. |
257 | +type ProvisioningScriptParams struct { |
258 | + MachineId string |
259 | + Nonce string |
260 | + DataDir string |
261 | +} |
262 | + |
263 | +// ProvisioningScriptResult contains the result of the |
264 | +// ProvisioningScript client API call. |
265 | +type ProvisioningScriptResult struct { |
266 | + Script string |
267 | +} |
268 | + |
269 | // EnvironmentGetResults contains the result of EnvironmentGet client |
270 | // API call. |
271 | type EnvironmentGetResults struct { |
272 | |
273 | === modified file 'state/apiserver/client/client.go' |
274 | --- state/apiserver/client/client.go 2014-01-10 04:24:50 +0000 |
275 | +++ state/apiserver/client/client.go 2014-01-17 03:18:53 +0000 |
276 | @@ -15,7 +15,10 @@ |
277 | "launchpad.net/loggo" |
278 | |
279 | "launchpad.net/juju-core/charm" |
280 | + coreCloudinit "launchpad.net/juju-core/cloudinit" |
281 | + "launchpad.net/juju-core/cloudinit/sshinit" |
282 | "launchpad.net/juju-core/environs" |
283 | + "launchpad.net/juju-core/environs/cloudinit" |
284 | "launchpad.net/juju-core/environs/config" |
285 | "launchpad.net/juju-core/errors" |
286 | "launchpad.net/juju-core/instance" |
287 | @@ -601,7 +604,31 @@ |
288 | // MachineConfig returns information from the environment config that is |
289 | // needed for machine cloud-init (both state servers and host nodes). |
290 | func (c *Client) MachineConfig(args params.MachineConfigParams) (params.MachineConfig, error) { |
291 | - return statecmd.MachineConfig(c.api.state, args) |
292 | + return statecmd.MachineConfig(c.api.state, args.MachineId) |
293 | +} |
294 | + |
295 | +// ProvisioningScript returns a shell script that, when run, |
296 | +// provisions a machine agent on the machine executing the script. |
297 | +func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) { |
298 | + var result params.ProvisioningScriptResult |
299 | + mcfgParams, err := statecmd.MachineConfig(c.api.state, args.MachineId) |
300 | + if err != nil { |
301 | + return result, err |
302 | + } |
303 | + mcfg, err := statecmd.FinishMachineConfig(mcfgParams, args.MachineId, args.Nonce, args.DataDir) |
304 | + if err != nil { |
305 | + return result, err |
306 | + } |
307 | + cloudcfg := coreCloudinit.New() |
308 | + if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil { |
309 | + return result, err |
310 | + } |
311 | + // ProvisioningScript is run on an existing machine; |
312 | + // we explicitly disable apt_upgrade so as not to |
313 | + // trample the machine's existing configuration. |
314 | + cloudcfg.SetAptUpgrade(false) |
315 | + result.Script, err = sshinit.ConfigureScript(cloudcfg) |
316 | + return result, err |
317 | } |
318 | |
319 | // DestroyMachines removes a given set of machines. |
320 | |
321 | === modified file 'state/apiserver/client/client_test.go' |
322 | --- state/apiserver/client/client_test.go 2014-01-07 07:35:12 +0000 |
323 | +++ state/apiserver/client/client_test.go 2014-01-17 03:18:53 +0000 |
324 | @@ -7,12 +7,16 @@ |
325 | "fmt" |
326 | "net/url" |
327 | "strconv" |
328 | + "strings" |
329 | |
330 | gc "launchpad.net/gocheck" |
331 | |
332 | "launchpad.net/juju-core/charm" |
333 | + coreCloudinit "launchpad.net/juju-core/cloudinit" |
334 | + "launchpad.net/juju-core/cloudinit/sshinit" |
335 | "launchpad.net/juju-core/constraints" |
336 | "launchpad.net/juju-core/environs" |
337 | + "launchpad.net/juju-core/environs/cloudinit" |
338 | "launchpad.net/juju-core/environs/config" |
339 | "launchpad.net/juju-core/errors" |
340 | "launchpad.net/juju-core/instance" |
341 | @@ -20,6 +24,7 @@ |
342 | "launchpad.net/juju-core/state/api" |
343 | "launchpad.net/juju-core/state/api/params" |
344 | "launchpad.net/juju-core/state/apiserver/client" |
345 | + "launchpad.net/juju-core/state/statecmd" |
346 | coretesting "launchpad.net/juju-core/testing" |
347 | jc "launchpad.net/juju-core/testing/checkers" |
348 | "launchpad.net/juju-core/tools" |
349 | @@ -1650,7 +1655,7 @@ |
350 | |
351 | func (s *clientSuite) TestMachineConfig(c *gc.C) { |
352 | addrs := []instance.Address{instance.NewAddress("1.2.3.4")} |
353 | - hc := instance.MustParseHardware("mem=4G") |
354 | + hc := instance.MustParseHardware("mem=4G arch=amd64") |
355 | apiParams := params.AddMachineParams{ |
356 | Jobs: []params.MachineJob{params.JobHostUnits}, |
357 | InstanceId: instance.Id("1234"), |
358 | @@ -1663,7 +1668,7 @@ |
359 | c.Assert(len(machines), gc.Equals, 1) |
360 | |
361 | machineId := machines[0].Machine |
362 | - machineConfig, err := s.APIState.Client().MachineConfig(machineId, config.DefaultSeries, "amd64") |
363 | + machineConfig, err := s.APIState.Client().MachineConfig(machineId) |
364 | c.Assert(err, gc.IsNil) |
365 | |
366 | envConfig, err := s.State.EnvironConfig() |
367 | @@ -1682,9 +1687,22 @@ |
368 | c.Assert(machineConfig.EnvironAttrs["name"], gc.Equals, "dummyenv") |
369 | } |
370 | |
371 | +func (s *clientSuite) TestMachineConfigNoArch(c *gc.C) { |
372 | + apiParams := params.AddMachineParams{ |
373 | + Jobs: []params.MachineJob{params.JobHostUnits}, |
374 | + InstanceId: instance.Id("1234"), |
375 | + Nonce: "foo", |
376 | + } |
377 | + machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) |
378 | + c.Assert(err, gc.IsNil) |
379 | + c.Assert(len(machines), gc.Equals, 1) |
380 | + _, err = s.APIState.Client().MachineConfig(machines[0].Machine) |
381 | + c.Assert(err, gc.ErrorMatches, fmt.Sprintf("arch is not set for %q", "machine-"+machines[0].Machine)) |
382 | +} |
383 | + |
384 | func (s *clientSuite) TestMachineConfigNoTools(c *gc.C) { |
385 | addrs := []instance.Address{instance.NewAddress("1.2.3.4")} |
386 | - hc := instance.MustParseHardware("mem=4G") |
387 | + hc := instance.MustParseHardware("mem=4G arch=amd64") |
388 | apiParams := params.AddMachineParams{ |
389 | Series: "quantal", |
390 | Jobs: []params.MachineJob{params.JobHostUnits}, |
391 | @@ -1695,10 +1713,55 @@ |
392 | } |
393 | machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) |
394 | c.Assert(err, gc.IsNil) |
395 | - _, err = s.APIState.Client().MachineConfig(machines[0].Machine, "quantal", "amd64") |
396 | + _, err = s.APIState.Client().MachineConfig(machines[0].Machine) |
397 | c.Assert(err, gc.ErrorMatches, tools.ErrNoMatches.Error()) |
398 | } |
399 | |
400 | +func (s *clientSuite) TestProvisioningScript(c *gc.C) { |
401 | + // Inject a machine and then call the ProvisioningScript API. |
402 | + // The result should be the same as when calling MachineConfig, |
403 | + // converting it to a cloudinit.MachineConfig, and disabling |
404 | + // apt_upgrade. |
405 | + apiParams := params.AddMachineParams{ |
406 | + Jobs: []params.MachineJob{params.JobHostUnits}, |
407 | + InstanceId: instance.Id("1234"), |
408 | + Nonce: "foo", |
409 | + HardwareCharacteristics: instance.MustParseHardware("arch=amd64"), |
410 | + } |
411 | + machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) |
412 | + c.Assert(err, gc.IsNil) |
413 | + c.Assert(len(machines), gc.Equals, 1) |
414 | + machineId := machines[0].Machine |
415 | + machineConfig, err := s.APIState.Client().MachineConfig(machineId) |
416 | + c.Assert(err, gc.IsNil) |
417 | + // Call ProvisioningScript. Normally ProvisioningScript and |
418 | + // MachineConfig are mutually exclusive; both of them will |
419 | + // allocate a state/api password for the machine agent. |
420 | + script, err := s.APIState.Client().ProvisioningScript(machineId, apiParams.Nonce) |
421 | + c.Assert(err, gc.IsNil) |
422 | + mcfg, err := statecmd.FinishMachineConfig(machineConfig, machineId, apiParams.Nonce, "") |
423 | + c.Assert(err, gc.IsNil) |
424 | + cloudcfg := coreCloudinit.New() |
425 | + err = cloudinit.ConfigureJuju(mcfg, cloudcfg) |
426 | + c.Assert(err, gc.IsNil) |
427 | + cloudcfg.SetAptUpgrade(false) |
428 | + sshinitScript, err := sshinit.ConfigureScript(cloudcfg) |
429 | + c.Assert(err, gc.IsNil) |
430 | + // ProvisioningScript internally calls MachineConfig, |
431 | + // which allocates a new, random password. Everything |
432 | + // about the scripts should be the same other than |
433 | + // the line containing "oldpassword" from agent.conf. |
434 | + scriptLines := strings.Split(script, "\n") |
435 | + sshinitScriptLines := strings.Split(sshinitScript, "\n") |
436 | + c.Assert(scriptLines, gc.HasLen, len(sshinitScriptLines)) |
437 | + for i, line := range scriptLines { |
438 | + if strings.Contains(line, "oldpassword") { |
439 | + continue |
440 | + } |
441 | + c.Assert(line, gc.Equals, sshinitScriptLines[i]) |
442 | + } |
443 | +} |
444 | + |
445 | func (s *clientSuite) TestClientAuthorizeStoreOnDeployServiceSetCharmAndAddCharm(c *gc.C) { |
446 | store, restore := makeMockCharmStore() |
447 | defer restore() |
448 | |
449 | === modified file 'state/statecmd/machineconfig.go' |
450 | --- state/statecmd/machineconfig.go 2013-12-19 09:42:05 +0000 |
451 | +++ state/statecmd/machineconfig.go 2014-01-17 03:18:53 +0000 |
452 | @@ -6,9 +6,13 @@ |
453 | import ( |
454 | "fmt" |
455 | |
456 | + "launchpad.net/juju-core/constraints" |
457 | "launchpad.net/juju-core/environs" |
458 | + "launchpad.net/juju-core/environs/cloudinit" |
459 | + "launchpad.net/juju-core/environs/config" |
460 | envtools "launchpad.net/juju-core/environs/tools" |
461 | "launchpad.net/juju-core/state" |
462 | + "launchpad.net/juju-core/state/api" |
463 | "launchpad.net/juju-core/state/api/params" |
464 | "launchpad.net/juju-core/tools" |
465 | ) |
466 | @@ -31,7 +35,7 @@ |
467 | // for maintaining compatibiility with juju-1.16 (where the API MachineConfig |
468 | // did not exist). When we drop 1.16 compatibility, this code should be moved |
469 | // back into api.Client |
470 | -func MachineConfig(st *state.State, args params.MachineConfigParams) (params.MachineConfig, error) { |
471 | +func MachineConfig(st *state.State, machineId string) (params.MachineConfig, error) { |
472 | result := params.MachineConfig{} |
473 | environConfig, err := st.EnvironConfig() |
474 | if err != nil { |
475 | @@ -40,12 +44,27 @@ |
476 | // The basic information. |
477 | result.EnvironAttrs = environConfig.AllAttrs() |
478 | |
479 | + // Get the machine so we can get its series and arch. |
480 | + // If the Arch is not set in hardware-characteristics, |
481 | + // an error is returned. |
482 | + machine, err := st.Machine(machineId) |
483 | + if err != nil { |
484 | + return result, err |
485 | + } |
486 | + hc, err := machine.HardwareCharacteristics() |
487 | + if err != nil { |
488 | + return result, err |
489 | + } |
490 | + if hc.Arch == nil { |
491 | + return result, fmt.Errorf("arch is not set for %q", machine.Tag()) |
492 | + } |
493 | + |
494 | // Find the appropriate tools information. |
495 | env, err := environs.New(environConfig) |
496 | if err != nil { |
497 | return result, err |
498 | } |
499 | - tools, err := findInstanceTools(env, args.Series, args.Arch) |
500 | + tools, err := findInstanceTools(env, machine.Series(), *hc.Arch) |
501 | if err != nil { |
502 | return result, err |
503 | } |
504 | @@ -56,10 +75,6 @@ |
505 | if err != nil { |
506 | return result, err |
507 | } |
508 | - machine, err := st.Machine(args.MachineId) |
509 | - if err != nil { |
510 | - return result, err |
511 | - } |
512 | stateInfo, apiInfo, err := auth.SetupAuthentication(machine) |
513 | if err != nil { |
514 | return result, err |
515 | @@ -71,3 +86,34 @@ |
516 | result.Tag = stateInfo.Tag |
517 | return result, nil |
518 | } |
519 | + |
520 | +// FinishMachineConfig is shared by environs/manual and state/apiserver/client |
521 | +// to create a complete cloudinit.MachineConfig from a params.MachineConfig. |
522 | +func FinishMachineConfig(configParameters params.MachineConfig, machineId, nonce, dataDir string) (*cloudinit.MachineConfig, error) { |
523 | + stateInfo := &state.Info{ |
524 | + Addrs: configParameters.StateAddrs, |
525 | + Password: configParameters.Password, |
526 | + Tag: configParameters.Tag, |
527 | + CACert: configParameters.CACert, |
528 | + } |
529 | + apiInfo := &api.Info{ |
530 | + Addrs: configParameters.APIAddrs, |
531 | + Password: configParameters.Password, |
532 | + Tag: configParameters.Tag, |
533 | + CACert: configParameters.CACert, |
534 | + } |
535 | + environConfig, err := config.New(config.NoDefaults, configParameters.EnvironAttrs) |
536 | + if err != nil { |
537 | + return nil, err |
538 | + } |
539 | + mcfg := environs.NewMachineConfig(machineId, nonce, stateInfo, apiInfo) |
540 | + if dataDir != "" { |
541 | + mcfg.DataDir = dataDir |
542 | + } |
543 | + mcfg.Tools = configParameters.Tools |
544 | + err = environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) |
545 | + if err != nil { |
546 | + return nil, err |
547 | + } |
548 | + return mcfg, nil |
549 | +} |
Reviewers: mp+201893_ code.launchpad. net,
Message:
Please take a look.
Description:
ProvisioningScript client API
This is a new client API that, given the ID and nonce
of a machine injected into state, returns a script
that can be executed on that machine to install and
configure a Juju machine agent.
The existing MachineConfig client API call has been
modified: it was taking a series and arch. Series is
guaranteed to be in state.Machine; the arch is not,
but we make it a requirement for calling MachineConfig.
In the only place where MachineConfig is already called,
this is true. ProvisioningScript is expected to be
used similarly.
I have tested "live" by temporarily modifying manual/ provisioner. go to call ProvisioningScript
environs/
and then running it via utils/ssh.Command.
https:/ /code.launchpad .net/~axwalk/ juju-core/ provisioningscr ipt-client- api/+merge/ 201893
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/53040043/
Affected files (+197, -69 lines): sshinit/ configure. go sshinit/ configure_ test.go sshinit/ export_ test.go sshinit/ suite_test. go manual/ provisioner. go manual/ provisioner_ test.go params/ params. go /client/ client. go /client/ client_ test.go machineconfig. go
A [revision details]
M cloudinit/
M cloudinit/
A cloudinit/
M cloudinit/
M environs/
M environs/
M state/api/client.go
M state/api/
M state/apiserver
M state/apiserver
M state/statecmd/