Merge lp:~axwalk/juju-core/provisioningscript-client-api into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
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
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/manual/provisioner.go to call ProvisioningScript
and then running it via utils/ssh.Command.

https://codereview.appspot.com/53040043/

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/manual/provisioner.go to call ProvisioningScript
and then running it via utils/ssh.Command.

https://codereview.appspot.com/53040043/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

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
environs/manual/provisioner.go to call ProvisioningScript
and then running it via utils/ssh.Command.

https://code.launchpad.net/~axwalk/juju-core/provisioningscript-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):
   A [revision details]
   M cloudinit/sshinit/configure.go
   M cloudinit/sshinit/configure_test.go
   A cloudinit/sshinit/export_test.go
   M cloudinit/sshinit/suite_test.go
   M environs/manual/provisioner.go
   M environs/manual/provisioner_test.go
   M state/api/client.go
   M state/api/params/params.go
   M state/apiserver/client/client.go
   M state/apiserver/client/client_test.go
   M state/statecmd/machineconfig.go

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

LGTM

https://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go
File state/statecmd/machineconfig.go (right):

https://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go#newcode92
state/statecmd/machineconfig.go:92: func
FinishMachineConfig(configParameters params.MachineConfig, machineId,
nonce, dataDir string) (*cloudinit.MachineConfig, error) {
This seems fine, but I don't entirely understand its rationale. Perhaps
you could explain why this needs to be separate from MachineConfig?

https://codereview.appspot.com/53040043/

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

Looking good, apart from some grumbles below.

https://codereview.appspot.com/53040043/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/53040043/diff/1/state/api/client.go#newcode191
state/api/client.go:191: return result.Script, err
I think it's a bit better like this:
err = ...
if err != nil {
   return "", err
}
return result.Script, nil

(like other similar methods).

https://codereview.appspot.com/53040043/diff/1/state/api/params/params.go
File state/api/params/params.go (left):

https://codereview.appspot.com/53040043/diff/1/state/api/params/params.go#oldcode521
state/api/params/params.go:521: Series string
I'm not sure we can remove these fields just yet (1.16 compatibility).

https://codereview.appspot.com/53040043/diff/1/state/api/params/params.go
File state/api/params/params.go (right):

https://codereview.appspot.com/53040043/diff/1/state/api/params/params.go#newcode545
state/api/params/params.go:545: // ProvisioningSCript client API call.
s/ProvisioningSCript/ProvisioningScript/ (and in the comment above as
well)

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client.go
File state/apiserver/client/client.go (right):

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client.go#newcode18
state/apiserver/client/client.go:18: coreCloudinit
"launchpad.net/juju-core/cloudinit"
I'd prefer corecloudinit for consistency sake.

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client_test.go
File state/apiserver/client/client_test.go (right):

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client_test.go#newcode15
state/apiserver/client/client_test.go:15: coreCloudinit
"launchpad.net/juju-core/cloudinit"
corecloudinit please

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client_test.go#newcode1764
state/apiserver/client/client_test.go:1764:
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://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go
File state/statecmd/machineconfig.go (right):

https://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go#newcode59
state/statecmd/machineconfig.go:59: return result, fmt.Errorf("arch is
not set for %s", machine.Tag())
s/for %s/for %q/ ?

https://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go#newcode92
state/statecmd/machineconfig.go:92: func
FinishMachineConfig(configParameters params.MachineConfig, machineId,
nonce, dataDir string) (*cloudinit.MachineConfig, error) {
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.

https://codereview.appspot.com/53040043/

Revision history for this message
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.

https://codereview.appspot.com/53040043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :
Download full text (4.2 KiB)

Please take a look.

https://codereview.appspot.com/53040043/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/53040043/diff/1/state/api/client.go#newcode191
state/api/client.go:191: return result.Script, err
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://codereview.appspot.com/53040043/diff/1/state/api/params/params.go
File state/api/params/params.go (left):

https://codereview.appspot.com/53040043/diff/1/state/api/params/params.go#oldcode521
state/api/params/params.go:521: Series string
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://codereview.appspot.com/53040043/diff/1/state/api/params/params.go
File state/api/params/params.go (right):

https://codereview.appspot.com/53040043/diff/1/state/api/params/params.go#newcode545
state/api/params/params.go:545: // ProvisioningSCript client API call.
On 2014/01/16 13:08:18, dimitern wrote:
> s/ProvisioningSCript/ProvisioningScript/ (and in the comment above as
well)

Done.

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client.go
File state/apiserver/client/client.go (right):

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client.go#newcode18
state/apiserver/client/client.go:18: coreCloudinit
"launchpad.net/juju-core/cloudinit"
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://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client_test.go
File state/apiserver/client/client_test.go (right):

https://codereview.appspot.com/53040043/diff/1/state/apiserver/client/client_test.go#newcode1764
state/apiserver/client/client_test.go:1764:
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://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go
File state/statecmd/machineconfig.go (right):

https://codereview.appspot.com/53040043/diff/1/state/statecmd/machineconfig.go#newcode59
state/statecmd/machineconfig.go:59: return result, fmt.Errorf("arch is
not...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches

to status/vote changes: