Merge lp:~thumper/juju-core/agent-conf-interface into lp:~go-bot/juju-core/trunk
- agent-conf-interface
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Tim Penhey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1701 |
Proposed branch: | lp:~thumper/juju-core/agent-conf-interface |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
2703 lines (+818/-884) 28 files modified
agent/agent.go (+258/-39) agent/agent_test.go (+232/-391) cmd/jujud/agent.go (+12/-29) cmd/jujud/agent_test.go (+62/-69) cmd/jujud/bootstrap.go (+9/-10) cmd/jujud/bootstrap_test.go (+20/-48) cmd/jujud/deploy_test.go (+0/-28) cmd/jujud/machine.go (+11/-10) cmd/jujud/machine_test.go (+43/-73) cmd/jujud/unit.go (+4/-4) cmd/jujud/unit_test.go (+9/-7) environs/bootstrap.go (+1/-34) environs/cloudinit/cloudinit.go (+35/-26) environs/cloudinit/cloudinit_test.go (+9/-8) provider/azure/config_test.go (+3/-1) provider/azure/customdata_test.go (+7/-4) provider/ec2/config_test.go (+8/-3) provider/ec2/local_test.go (+4/-0) provider/local/environ.go (+47/-48) provider/maas/config_test.go (+3/-1) provider/maas/environ_test.go (+1/-0) provider/openstack/config_test.go (+4/-5) provider/openstack/local_test.go (+1/-0) state/open.go (+5/-6) state/state.go (+4/-2) testing/mgo.go (+5/-1) worker/deployer/simple.go (+15/-21) worker/deployer/simple_test.go (+6/-16) |
To merge this branch: | bzr merge lp:~thumper/juju-core/agent-conf-interface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email:
|
Commit message
Replace agent.Conf with an interface
This was supposed to be smallish, but turned epic.
I also unexported the Conf structure so construction
now has to be through one of the two New* methods or
the Read method.
A lot of the changes are making the call sites work
with the interface instead of directly poking at the
structure.
Quite a few of the tests in agent were testing for
situations that can no longer occur. Those have
been removed.
Description of the change
Replace agent.Conf with an interface
This was supposed to be smallish, but turned epic.
I also unexported the Conf structure so construction
now has to be through one of the two New* methods or
the Read method.
A lot of the changes are making the call sites work
with the interface instead of directly poking at the
structure.
Quite a few of the tests in agent were testing for
situations that can no longer occur. Those have
been removed.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
William Reade (fwereade) wrote : | # |
LGTM, this is awesome. Just a few trivial notes:
https:/
File agent/agent_test.go (left):
https:/
agent/agent_
Please always register suites as pointers -- if/when someone adds test
methods that expect a pointer receiver they'll silently not get run.
Wouldn't matter if everyone was perfect and always ran a failing test
first, but... ;)
https:/
File agent/agent_test.go (right):
https:/
agent/agent_
Nice.
https:/
File cmd/jujud/
https:/
cmd/jujud/
m.SetPassword(
Doesn't SetPassword also SetMongoPassword? (might well not, just thought
it did)
https:/
File cmd/jujud/
https:/
cmd/jujud/
Ditto.
https:/
File environs/
https:/
environs/
FWIW this is, I think, a bootstrap compatibility break (until we have
ian's pick-matching-
dave; I guess it doesn't matter if we release a dev version with this
in, but once this lands we *cannot* do a new stable release without his
change in too.
https:/
environs/
This looks identical to...
https:/
environs/
...this. Would be nice to pull out into a var.
https:/
File provider/
https:/
provider/
I guess it's not practical to consolidate this with the bits in
cloudinit at this stage, but there's a little bit of a smell here I
think.
https:/
File worker/
https:/
worker/
slight raised eyebrow... I expect we'll be passing this field up in the
shared agent connection code sooner or later, and we'll need the api
server to expect "unused"?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John A Meinel (jameinel) wrote : | # |
Some thoughts I wanted to make sure to get out since William did the
review.
https:/
File agent/agent.go (left):
https:/
agent/agent.go:59: }
Is the goal that 'conf' "is a" Config? In which case, we probably want
the:
var _ Config = (*Conf)(nil)
Statement to solidify that (as a declaration to people reading the code,
rather than a constraint that the compiler will find as soon as you
return one of them from a function).
https:/
File agent/agent.go (right):
https:/
agent/agent.go:42:
A bunch of the following functions feel like they should be either
free-form functions that accept the interface, or on a different
interface that helps the Config object do stuff.
It may not be something we want to change right now, but it does
highlight that *Config* was doing a bit more than seems correct.
A different breakdown would be:
an object that handles reading and writing itself to disk.
a function that takes the abstract in-memory content and uses it to open
an API connection
a function that takes the in-memory content and opens a DB connection
etc.
https:/
agent/agent.go:171:
I think we eventually will have API server machines that *don't* host
the Mongo DB (State). So we might want to split this a bit further. We
can argue that we should delay until we actually need it, though I'd
like us to be sure we can get there from here.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
https:/
File agent/agent.go (left):
https:/
agent/agent.go:59: }
On 2013/08/22 11:17:05, jameinel wrote:
> Is the goal that 'conf' "is a" Config? In which case, we probably want
the:
> var _ Config = (*Conf)(nil)
> Statement to solidify that (as a declaration to people reading the
code, rather
> than a constraint that the compiler will find as soon as you return
one of them
> from a function).
Done.
https:/
File agent/agent.go (right):
https:/
agent/agent.go:42:
On 2013/08/22 11:17:05, jameinel wrote:
> A bunch of the following functions feel like they should be either
free-form
> functions that accept the interface, or on a different interface that
helps the
> Config object do stuff.
> It may not be something we want to change right now, but it does
highlight that
> *Config* was doing a bit more than seems correct.
> A different breakdown would be:
> an object that handles reading and writing itself to disk.
> a function that takes the abstract in-memory content and uses it to
open an API
> connection
> a function that takes the in-memory content and opens a DB connection
> etc.
I agree here. I would like to defer that to a later branch.
https:/
agent/agent.go:171:
On 2013/08/22 11:17:05, jameinel wrote:
> I think we eventually will have API server machines that *don't* host
the Mongo
> DB (State). So we might want to split this a bit further. We can argue
that we
> should delay until we actually need it, though I'd like us to be sure
we can get
> there from here.
I don't see any reason why we can't get here from there. It is just
more work :-) A first thing to do would be to add a machine job type
for being an api server. We'd probably then want to have extra configs.
I'm being a bit vague here as until we need it, it is hard to break
apart. I do have a feeling that the StatePort isn't actually used.
https:/
File agent/agent_test.go (left):
https:/
agent/agent_
On 2013/08/22 10:35:44, fwereade wrote:
> Please always register suites as pointers -- if/when someone adds test
methods
> that expect a pointer receiver they'll silently not get run. Wouldn't
matter if
> everyone was perfect and always ran a failing test first, but... ;)
Done.
https:/
File cmd/jujud/
https:/
cmd/jujud/
m.SetPassword(
On 2013/08/22 10:35:44, fwereade wrote:
> Doesn't SetPassword also SetMongoPassword? (might well not, just
thought it did)
No. Although these two methods are almost always done in pairs.
I felt it was outside the scope of this change to fix that.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Penhey (thumper) wrote : | # |
Please take a look.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Roger Peppe (rogpeppe) wrote : | # |
I very much like it that this branch
hides some of the grungy details of the agent
configuration behind appropriate methods.
Thanks a lot for working on this.
I have some thoughts about some other
aspects of this though, outlined
below.
https:/
File agent/agent.go (right):
https:/
agent/agent.go:27: type Config interface {
Doc comment, please.
And I'm a little puzzled as to what we gain by
making this an interface rather than simply
unexporting the component fields and defining
methods on the type. It doesn't reduce
coupling AFAICS, and it's not like the concrete
type is difficult or expensive to mock.
https:/
agent/agent.go:29: // containing the configuration files.
s/the/its/ ?
And I think this is wrong - the data dir is shared between all
agents, otherwise we wouldn't need a distincton between DataDir
and Dir below.
https:/
agent/agent.go:50: // the given Conf.
What given Conf?
https:/
agent/agent.go:53: // Write writes the agent configuration.
... to the agent's directory. ?
https:/
agent/agent.go:61: // GenerateNewPassword creates a new random password
and saves this. The
This doesn't make it very clear what's going on.
Perhaps:
// GenerateNewPassword changes the Config's password
// to be a new random password and saves the
// new configuration by calling Config.Write.
// It returns the new password.
?
https:/
agent/agent.go:69: // APIServerDetails returns the details needed to run
an API server.
This could do with a little more documentation. It does
not say what port, cert or key are. The original documentation
for StateServerCert and StateServerKey was more clear.
Perhaps consider splitting this into two methods, one
to return the port and one to return the cert and key.
https:/
agent/agent.go:74: type conf struct {
"config" might be a better name here.
https:/
agent/agent.go:114: type AgentConfigParams struct {
Doc comment please. And on the fields too. The data
that is passed in here is the principal reason
for the existence of this type.
https:/
agent/agent.go:138: // blank. This is by design.
The conflation of credentials and location is the underlying
problem here, I think (and the central problem with state.Info
as a concept). I'd love to see a branch that fixes that.
https:/
agent/agent.go:175: type StateMachineCon
Doc comment please.
https:/
agent/agent.go:434: func InitialStateCon
Preview Diff
1 | === modified file 'agent/agent.go' |
2 | --- agent/agent.go 2013-08-20 14:02:31 +0000 |
3 | +++ agent/agent.go 2013-08-22 23:04:24 +0000 |
4 | @@ -11,8 +11,10 @@ |
5 | "regexp" |
6 | |
7 | "launchpad.net/goyaml" |
8 | + "launchpad.net/loggo" |
9 | |
10 | "launchpad.net/juju-core/agent/tools" |
11 | + "launchpad.net/juju-core/environs/config" |
12 | "launchpad.net/juju-core/errors" |
13 | "launchpad.net/juju-core/state" |
14 | "launchpad.net/juju-core/state/api" |
15 | @@ -20,11 +22,59 @@ |
16 | "launchpad.net/juju-core/utils" |
17 | ) |
18 | |
19 | -// Conf holds information for a given agent. |
20 | -type Conf struct { |
21 | +var logger = loggo.GetLogger("juju.agent") |
22 | + |
23 | +type Config interface { |
24 | + // DataDir returns the data directory. Each agent has a subdirectory |
25 | + // containing the configuration files. |
26 | + DataDir() string |
27 | + |
28 | + // Tag returns the tag of the entity on whose behalf the state connection |
29 | + // will be made. |
30 | + Tag() string |
31 | + |
32 | + // Dir returns the agent's directory. |
33 | + Dir() string |
34 | + |
35 | + // Nonce returns the nonce saved when the machine was provisioned |
36 | + // TODO: make this one of the key/value pairs. |
37 | + Nonce() string |
38 | + |
39 | + // OpenAPI tries to connect to an API end-point. If a non-empty |
40 | + // newPassword is returned, the password used to connect to the state |
41 | + // should be changed accordingly - the caller should set the entity's |
42 | + // password accordingly. |
43 | + OpenAPI(dialOpts api.DialOpts) (st *api.State, newPassword string, err error) |
44 | + |
45 | + // OpenState tries to open a direct connection to the state database using |
46 | + // the given Conf. |
47 | + OpenState() (*state.State, error) |
48 | + |
49 | + // Write writes the agent configuration. |
50 | + Write() error |
51 | + |
52 | + // WriteCommands returns shell commands to write the agent configuration. |
53 | + // It returns an error if the configuration does not have all the right |
54 | + // elements. |
55 | + WriteCommands() ([]string, error) |
56 | + |
57 | + // GenerateNewPassword creates a new random password and saves this. The |
58 | + // new password string is returned. |
59 | + GenerateNewPassword() (string, error) |
60 | + |
61 | + // PasswordHash returns a hash of the password that is stored for state and |
62 | + // api connections. |
63 | + PasswordHash() string |
64 | + |
65 | + // APIServerDetails returns the details needed to run an API server. |
66 | + APIServerDetails() (port int, cert, key []byte) |
67 | +} |
68 | + |
69 | +// conf holds information for a given agent. |
70 | +type conf struct { |
71 | // DataDir specifies the path of the data directory used by all |
72 | // agents |
73 | - DataDir string |
74 | + dataDir string |
75 | |
76 | // StateServerCert and StateServerKey hold the state server |
77 | // certificate and private key in PEM format. |
78 | @@ -58,20 +108,110 @@ |
79 | APIInfo *api.Info `yaml:",omitempty"` |
80 | } |
81 | |
82 | +// Ensure that conf implements Config. |
83 | +var _ Config = (*conf)(nil) |
84 | + |
85 | +type AgentConfigParams struct { |
86 | + DataDir string |
87 | + Tag string |
88 | + Password string |
89 | + Nonce string |
90 | + StateAddresses []string |
91 | + APIAddresses []string |
92 | + CACert []byte |
93 | +} |
94 | + |
95 | +func newConfig(params AgentConfigParams) (*conf, error) { |
96 | + if params.DataDir == "" { |
97 | + return nil, requiredError("data directory") |
98 | + } |
99 | + if params.Tag == "" { |
100 | + return nil, requiredError("entity tag") |
101 | + } |
102 | + if params.Password == "" { |
103 | + return nil, requiredError("password") |
104 | + } |
105 | + if params.CACert == nil { |
106 | + return nil, requiredError("CA certificate") |
107 | + } |
108 | + // Note that the password parts of the state and api information are |
109 | + // blank. This is by design. |
110 | + var stateInfo *state.Info |
111 | + if len(params.StateAddresses) > 0 { |
112 | + stateInfo = &state.Info{ |
113 | + Addrs: params.StateAddresses, |
114 | + Tag: params.Tag, |
115 | + CACert: params.CACert, |
116 | + } |
117 | + } |
118 | + var apiInfo *api.Info |
119 | + if len(params.APIAddresses) > 0 { |
120 | + apiInfo = &api.Info{ |
121 | + Addrs: params.APIAddresses, |
122 | + Tag: params.Tag, |
123 | + CACert: params.CACert, |
124 | + } |
125 | + } |
126 | + conf := &conf{ |
127 | + dataDir: params.DataDir, |
128 | + OldPassword: params.Password, |
129 | + StateInfo: stateInfo, |
130 | + APIInfo: apiInfo, |
131 | + MachineNonce: params.Nonce, |
132 | + } |
133 | + if err := conf.check(); err != nil { |
134 | + return nil, err |
135 | + } |
136 | + return conf, nil |
137 | +} |
138 | + |
139 | +// NewAgentConfig returns a new config object suitable for use for a unit |
140 | +// agent. As the unit agent becomes entirely APIified, we should remove the |
141 | +// state addresses from here. |
142 | +func NewAgentConfig(params AgentConfigParams) (Config, error) { |
143 | + return newConfig(params) |
144 | +} |
145 | + |
146 | +type StateMachineConfigParams struct { |
147 | + AgentConfigParams |
148 | + StateServerCert []byte |
149 | + StateServerKey []byte |
150 | + StatePort int |
151 | + APIPort int |
152 | +} |
153 | + |
154 | +func NewStateMachineConfig(params StateMachineConfigParams) (Config, error) { |
155 | + if params.StateServerCert == nil { |
156 | + return nil, requiredError("state server cert") |
157 | + } |
158 | + if params.StateServerKey == nil { |
159 | + return nil, requiredError("state server key") |
160 | + } |
161 | + conf, err := newConfig(params.AgentConfigParams) |
162 | + if err != nil { |
163 | + return nil, err |
164 | + } |
165 | + conf.StateServerCert = params.StateServerCert |
166 | + conf.StateServerKey = params.StateServerKey |
167 | + conf.StatePort = params.StatePort |
168 | + conf.APIPort = params.APIPort |
169 | + return conf, nil |
170 | +} |
171 | + |
172 | // ReadConf reads configuration data for the given |
173 | // entity from the given data directory. |
174 | -func ReadConf(dataDir, tag string) (*Conf, error) { |
175 | +func ReadConf(dataDir, tag string) (Config, error) { |
176 | dir := tools.Dir(dataDir, tag) |
177 | data, err := ioutil.ReadFile(path.Join(dir, "agent.conf")) |
178 | if err != nil { |
179 | return nil, err |
180 | } |
181 | - var c Conf |
182 | + var c conf |
183 | if err := goyaml.Unmarshal(data, &c); err != nil { |
184 | return nil, err |
185 | } |
186 | - c.DataDir = dataDir |
187 | - if err := c.Check(); err != nil { |
188 | + c.dataDir = dataDir |
189 | + if err := c.check(); err != nil { |
190 | return nil, err |
191 | } |
192 | if c.StateInfo != nil { |
193 | @@ -88,17 +228,29 @@ |
194 | } |
195 | |
196 | // File returns the path of the given file in the agent's directory. |
197 | -func (c *Conf) File(name string) string { |
198 | +func (c *conf) File(name string) string { |
199 | return path.Join(c.Dir(), name) |
200 | } |
201 | |
202 | -func (c *Conf) confFile() string { |
203 | +func (c *conf) confFile() string { |
204 | return c.File("agent.conf") |
205 | } |
206 | |
207 | +func (c *conf) DataDir() string { |
208 | + return c.dataDir |
209 | +} |
210 | + |
211 | +func (c *conf) Nonce() string { |
212 | + return c.MachineNonce |
213 | +} |
214 | + |
215 | +func (c *conf) APIServerDetails() (port int, cert, key []byte) { |
216 | + return c.APIPort, c.StateServerCert, c.StateServerKey |
217 | +} |
218 | + |
219 | // Tag returns the tag of the entity on whose behalf the state connection will |
220 | // be made. |
221 | -func (c *Conf) Tag() string { |
222 | +func (c *conf) Tag() string { |
223 | if c.StateInfo != nil { |
224 | return c.StateInfo.Tag |
225 | } |
226 | @@ -106,43 +258,25 @@ |
227 | } |
228 | |
229 | // Dir returns the agent's directory. |
230 | -func (c *Conf) Dir() string { |
231 | - return tools.Dir(c.DataDir, c.Tag()) |
232 | +func (c *conf) Dir() string { |
233 | + return tools.Dir(c.dataDir, c.Tag()) |
234 | } |
235 | |
236 | // Check checks that the configuration has all the required elements. |
237 | -func (c *Conf) Check() error { |
238 | - if c.DataDir == "" { |
239 | - return requiredError("data directory") |
240 | - } |
241 | +func (c *conf) check() error { |
242 | if c.StateInfo == nil && c.APIInfo == nil { |
243 | - return requiredError("state info or API info") |
244 | + return requiredError("state or API addresses") |
245 | } |
246 | if c.StateInfo != nil { |
247 | - if c.StateInfo.Tag == "" { |
248 | - return requiredError("state entity tag") |
249 | - } |
250 | if err := checkAddrs(c.StateInfo.Addrs, "state server address"); err != nil { |
251 | return err |
252 | } |
253 | - if len(c.StateInfo.CACert) == 0 { |
254 | - return requiredError("state CA certificate") |
255 | - } |
256 | } |
257 | // TODO(rog) make APIInfo mandatory |
258 | if c.APIInfo != nil { |
259 | - if c.APIInfo.Tag == "" { |
260 | - return requiredError("API entity tag") |
261 | - } |
262 | if err := checkAddrs(c.APIInfo.Addrs, "API server address"); err != nil { |
263 | return err |
264 | } |
265 | - if len(c.APIInfo.CACert) == 0 { |
266 | - return requiredError("API CA certficate") |
267 | - } |
268 | - } |
269 | - if c.StateInfo != nil && c.APIInfo != nil && c.StateInfo.Tag != c.APIInfo.Tag { |
270 | - return fmt.Errorf("mismatched entity tags") |
271 | } |
272 | return nil |
273 | } |
274 | @@ -161,9 +295,46 @@ |
275 | return nil |
276 | } |
277 | |
278 | +func (c *conf) PasswordHash() string { |
279 | + var password string |
280 | + if c.StateInfo == nil { |
281 | + password = c.APIInfo.Password |
282 | + } else { |
283 | + password = c.StateInfo.Password |
284 | + } |
285 | + return utils.PasswordHash(password) |
286 | +} |
287 | + |
288 | +func (c *conf) GenerateNewPassword() (string, error) { |
289 | + newPassword, err := utils.RandomPassword() |
290 | + if err != nil { |
291 | + return "", err |
292 | + } |
293 | + // Make a copy of the configuration so that if we fail |
294 | + // to write the configuration file, the configuration will |
295 | + // still be valid. |
296 | + other := *c |
297 | + if c.StateInfo != nil { |
298 | + stateInfo := *c.StateInfo |
299 | + stateInfo.Password = newPassword |
300 | + other.StateInfo = &stateInfo |
301 | + } |
302 | + if c.APIInfo != nil { |
303 | + apiInfo := *c.APIInfo |
304 | + apiInfo.Password = newPassword |
305 | + other.APIInfo = &apiInfo |
306 | + } |
307 | + |
308 | + if err := other.Write(); err != nil { |
309 | + return "", err |
310 | + } |
311 | + *c = other |
312 | + return newPassword, nil |
313 | +} |
314 | + |
315 | // Write writes the agent configuration. |
316 | -func (c *Conf) Write() error { |
317 | - if err := c.Check(); err != nil { |
318 | +func (c *conf) Write() error { |
319 | + if err := c.check(); err != nil { |
320 | return err |
321 | } |
322 | data, err := goyaml.Marshal(c) |
323 | @@ -186,8 +357,8 @@ |
324 | // WriteCommands returns shell commands to write the agent |
325 | // configuration. It returns an error if the configuration does not |
326 | // have all the right elements. |
327 | -func (c *Conf) WriteCommands() ([]string, error) { |
328 | - if err := c.Check(); err != nil { |
329 | +func (c *conf) WriteCommands() ([]string, error) { |
330 | + if err := c.check(); err != nil { |
331 | return nil, err |
332 | } |
333 | data, err := goyaml.Marshal(c) |
334 | @@ -210,7 +381,7 @@ |
335 | // to the state should be changed accordingly - the caller should write the |
336 | // configuration with StateInfo.Password set to newPassword, then |
337 | // set the entity's password accordingly. |
338 | -func (c *Conf) OpenAPI(dialOpts api.DialOpts) (st *api.State, newPassword string, err error) { |
339 | +func (c *conf) OpenAPI(dialOpts api.DialOpts) (st *api.State, newPassword string, err error) { |
340 | info := *c.APIInfo |
341 | info.Nonce = c.MachineNonce |
342 | if info.Password != "" { |
343 | @@ -231,9 +402,10 @@ |
344 | if err != nil { |
345 | return nil, "", err |
346 | } |
347 | + |
348 | // We've succeeded in connecting with the old password, so |
349 | // we can now change it to something more private. |
350 | - password, err := utils.RandomPassword() |
351 | + password, err := c.GenerateNewPassword() |
352 | if err != nil { |
353 | st.Close() |
354 | return nil, "", err |
355 | @@ -242,7 +414,7 @@ |
356 | } |
357 | |
358 | // OpenState tries to open the state using the given Conf. |
359 | -func (c *Conf) OpenState() (*state.State, error) { |
360 | +func (c *conf) OpenState() (*state.State, error) { |
361 | info := *c.StateInfo |
362 | if info.Password != "" { |
363 | st, err := state.Open(&info, state.DefaultDialOpts()) |
364 | @@ -258,3 +430,50 @@ |
365 | info.Password = c.OldPassword |
366 | return state.Open(&info, state.DefaultDialOpts()) |
367 | } |
368 | + |
369 | +func InitialStateConfiguration(agentConfig Config, cfg *config.Config, timeout state.DialOpts) (*state.State, error) { |
370 | + c := agentConfig.(*conf) |
371 | + info := *c.StateInfo |
372 | + info.Tag = "" |
373 | + logger.Debugf("initializing address %v", info.Addrs) |
374 | + st, err := state.Initialize(&info, cfg, timeout) |
375 | + if err != nil { |
376 | + if errors.IsUnauthorizedError(err) { |
377 | + logger.Errorf("unauthorized: %v", err) |
378 | + } else { |
379 | + logger.Errorf("failed to initialize state: %v", err) |
380 | + } |
381 | + return nil, err |
382 | + } |
383 | + logger.Debugf("state initialized") |
384 | + |
385 | + if err := bootstrapUsers(st, cfg, c.OldPassword); err != nil { |
386 | + st.Close() |
387 | + return nil, err |
388 | + } |
389 | + return st, nil |
390 | +} |
391 | + |
392 | +// bootstrapUsers creates the initial admin user for the database, and sets |
393 | +// the initial password. |
394 | +func bootstrapUsers(st *state.State, cfg *config.Config, passwordHash string) error { |
395 | + logger.Debugf("adding admin user") |
396 | + // Set up initial authentication. |
397 | + u, err := st.AddUser("admin", "") |
398 | + if err != nil { |
399 | + return err |
400 | + } |
401 | + |
402 | + // Note that at bootstrap time, the password is set to |
403 | + // the hash of its actual value. The first time a client |
404 | + // connects to mongo, it changes the mongo password |
405 | + // to the original password. |
406 | + logger.Debugf("setting password hash for admin user") |
407 | + if err := u.SetPasswordHash(passwordHash); err != nil { |
408 | + return err |
409 | + } |
410 | + if err := st.SetAdminMongoPassword(passwordHash); err != nil { |
411 | + return err |
412 | + } |
413 | + return nil |
414 | +} |
415 | |
416 | === modified file 'agent/agent_test.go' |
417 | --- agent/agent_test.go 2013-07-24 03:30:48 +0000 |
418 | +++ agent/agent_test.go 2013-08-22 23:04:24 +0000 |
419 | @@ -4,410 +4,251 @@ |
420 | package agent_test |
421 | |
422 | import ( |
423 | - "os" |
424 | - "os/exec" |
425 | - "path/filepath" |
426 | - |
427 | gc "launchpad.net/gocheck" |
428 | |
429 | "launchpad.net/juju-core/agent" |
430 | - "launchpad.net/juju-core/juju/testing" |
431 | - "launchpad.net/juju-core/state" |
432 | - "launchpad.net/juju-core/state/api" |
433 | coretesting "launchpad.net/juju-core/testing" |
434 | - "launchpad.net/juju-core/utils" |
435 | ) |
436 | |
437 | type suite struct { |
438 | coretesting.LoggingSuite |
439 | } |
440 | |
441 | -var _ = gc.Suite(suite{}) |
442 | +var _ = gc.Suite(&suite{}) |
443 | |
444 | -var confTests = []struct { |
445 | +var agentConfigTests = []struct { |
446 | about string |
447 | - conf agent.Conf |
448 | + params agent.AgentConfigParams |
449 | checkErr string |
450 | }{{ |
451 | - about: "state info only", |
452 | - conf: agent.Conf{ |
453 | - OldPassword: "old password", |
454 | - MachineNonce: "dummy", |
455 | - StateInfo: &state.Info{ |
456 | - Addrs: []string{"foo.com:355", "bar:545"}, |
457 | - CACert: []byte("ca cert"), |
458 | - Tag: "entity", |
459 | - Password: "current password", |
460 | - }, |
461 | - }, |
462 | -}, { |
463 | - about: "state info and api info", |
464 | - conf: agent.Conf{ |
465 | - StateServerCert: []byte("server cert"), |
466 | - StateServerKey: []byte("server key"), |
467 | - StatePort: 1234, |
468 | - APIPort: 4321, |
469 | - OldPassword: "old password", |
470 | - StateInfo: &state.Info{ |
471 | - Addrs: []string{"foo.com:355", "bar:545"}, |
472 | - CACert: []byte("ca cert"), |
473 | - Tag: "entity", |
474 | - Password: "current password", |
475 | - }, |
476 | - APIInfo: &api.Info{ |
477 | - Tag: "entity", |
478 | - Password: "other password", |
479 | - Addrs: []string{"foo.com:555", "bar:555"}, |
480 | - CACert: []byte("api ca cert"), |
481 | - }, |
482 | - }, |
483 | -}, { |
484 | - about: "API info and state info sharing CA cert", |
485 | - conf: agent.Conf{ |
486 | - OldPassword: "old password", |
487 | - StateInfo: &state.Info{ |
488 | - Addrs: []string{"foo.com:355", "bar:545"}, |
489 | - CACert: []byte("ca cert"), |
490 | - Tag: "entity", |
491 | - Password: "current password", |
492 | - }, |
493 | - APIInfo: &api.Info{ |
494 | - Tag: "entity", |
495 | - Addrs: []string{"foo.com:555"}, |
496 | - CACert: []byte("ca cert"), |
497 | - }, |
498 | - }, |
499 | -}, { |
500 | - about: "no api entity tag", |
501 | - conf: agent.Conf{ |
502 | - StateServerCert: []byte("server cert"), |
503 | - StateServerKey: []byte("server key"), |
504 | - OldPassword: "old password", |
505 | - StateInfo: &state.Info{ |
506 | - Addrs: []string{"foo.com:355", "bar:545"}, |
507 | - CACert: []byte("ca cert"), |
508 | - Tag: "entity", |
509 | - Password: "current password", |
510 | - }, |
511 | - APIInfo: &api.Info{ |
512 | - Addrs: []string{"foo.com:555"}, |
513 | - CACert: []byte("api ca cert"), |
514 | - }, |
515 | - }, |
516 | - checkErr: "API entity tag not found in configuration", |
517 | -}, { |
518 | - about: "mismatched entity tags", |
519 | - conf: agent.Conf{ |
520 | - StateServerCert: []byte("server cert"), |
521 | - StateServerKey: []byte("server key"), |
522 | - OldPassword: "old password", |
523 | - StateInfo: &state.Info{ |
524 | - Addrs: []string{"foo.com:355", "bar:545"}, |
525 | - CACert: []byte("ca cert"), |
526 | - Tag: "entity", |
527 | - Password: "current password", |
528 | - }, |
529 | - APIInfo: &api.Info{ |
530 | - Tag: "other", |
531 | - Addrs: []string{"foo.com:555"}, |
532 | - CACert: []byte("api ca cert"), |
533 | - }, |
534 | - }, |
535 | - checkErr: "mismatched entity tags", |
536 | -}, { |
537 | - about: "no state entity tag", |
538 | - conf: agent.Conf{ |
539 | - OldPassword: "old password", |
540 | - StateInfo: &state.Info{ |
541 | - Addrs: []string{"foo.com:355", "bar:545"}, |
542 | - CACert: []byte("ca cert"), |
543 | - Password: "current password", |
544 | - }, |
545 | - }, |
546 | - checkErr: "state entity tag not found in configuration", |
547 | -}, { |
548 | - about: "no state server address", |
549 | - conf: agent.Conf{ |
550 | - OldPassword: "old password", |
551 | - StateInfo: &state.Info{ |
552 | - CACert: []byte("ca cert"), |
553 | - Password: "current password", |
554 | - Tag: "entity", |
555 | - }, |
556 | - }, |
557 | - checkErr: "state server address not found in configuration", |
558 | -}, { |
559 | - about: "state server address with no port", |
560 | - conf: agent.Conf{ |
561 | - OldPassword: "old password", |
562 | - StateInfo: &state.Info{ |
563 | - Addrs: []string{"foo"}, |
564 | - CACert: []byte("ca cert"), |
565 | - Tag: "entity", |
566 | - Password: "current password", |
567 | - }, |
568 | - }, |
569 | - checkErr: "invalid state server address \"foo\"", |
570 | -}, { |
571 | - about: "state server address with non-numeric port", |
572 | - conf: agent.Conf{ |
573 | - OldPassword: "old password", |
574 | - StateInfo: &state.Info{ |
575 | - Addrs: []string{"foo:bar"}, |
576 | - CACert: []byte("ca cert"), |
577 | - Tag: "entity", |
578 | - Password: "current password", |
579 | - }, |
580 | - }, |
581 | - checkErr: "invalid state server address \"foo:bar\"", |
582 | -}, { |
583 | - about: "state server address with bad port", |
584 | - conf: agent.Conf{ |
585 | - OldPassword: "old password", |
586 | - StateInfo: &state.Info{ |
587 | - Addrs: []string{"foo:345d"}, |
588 | - CACert: []byte("ca cert"), |
589 | - Tag: "entity", |
590 | - Password: "current password", |
591 | - }, |
592 | - }, |
593 | - checkErr: "invalid state server address \"foo:345d\"", |
594 | -}, { |
595 | - about: "invalid api server address", |
596 | - conf: agent.Conf{ |
597 | - OldPassword: "old password", |
598 | - StateInfo: &state.Info{ |
599 | - Addrs: []string{"foo:345"}, |
600 | - CACert: []byte("ca cert"), |
601 | - Tag: "entity", |
602 | - Password: "current password", |
603 | - }, |
604 | - APIInfo: &api.Info{ |
605 | - Tag: "entity", |
606 | - Addrs: []string{"bar.com:455", "foo"}, |
607 | - CACert: []byte("ca cert"), |
608 | - }, |
609 | - }, |
610 | - checkErr: "invalid API server address \"foo\"", |
611 | -}, { |
612 | - about: "no api CA cert", |
613 | - conf: agent.Conf{ |
614 | - OldPassword: "old password", |
615 | - StateInfo: &state.Info{ |
616 | - Addrs: []string{"foo:345"}, |
617 | - CACert: []byte("ca cert"), |
618 | - Tag: "entity", |
619 | - Password: "current password", |
620 | - }, |
621 | - APIInfo: &api.Info{ |
622 | - Tag: "entity", |
623 | - Addrs: []string{"foo:3"}, |
624 | - CACert: []byte{}, |
625 | - }, |
626 | - }, |
627 | - checkErr: "API CA certficate not found in configuration", |
628 | -}, { |
629 | - about: "no state or API info", |
630 | - conf: agent.Conf{ |
631 | - OldPassword: "old password", |
632 | - }, |
633 | - checkErr: "state info or API info not found in configuration", |
634 | + about: "missing data directory", |
635 | + checkErr: "data directory not found in configuration", |
636 | +}, { |
637 | + about: "missing tag directory", |
638 | + params: agent.AgentConfigParams{ |
639 | + DataDir: "/data/dir", |
640 | + }, |
641 | + checkErr: "entity tag not found in configuration", |
642 | +}, { |
643 | + about: "missing password", |
644 | + params: agent.AgentConfigParams{ |
645 | + DataDir: "/data/dir", |
646 | + Tag: "omg", |
647 | + }, |
648 | + checkErr: "password not found in configuration", |
649 | +}, { |
650 | + about: "missing CA cert", |
651 | + params: agent.AgentConfigParams{ |
652 | + DataDir: "/data/dir", |
653 | + Tag: "omg", |
654 | + Password: "sekrit", |
655 | + }, |
656 | + checkErr: "CA certificate not found in configuration", |
657 | +}, { |
658 | + about: "need either state or api addresses", |
659 | + params: agent.AgentConfigParams{ |
660 | + DataDir: "/data/dir", |
661 | + Tag: "omg", |
662 | + Password: "sekrit", |
663 | + CACert: []byte("ca cert"), |
664 | + }, |
665 | + checkErr: "state or API addresses not found in configuration", |
666 | +}, { |
667 | + about: "invalid state address", |
668 | + params: agent.AgentConfigParams{ |
669 | + DataDir: "/data/dir", |
670 | + Tag: "omg", |
671 | + Password: "sekrit", |
672 | + CACert: []byte("ca cert"), |
673 | + StateAddresses: []string{"localhost:8080", "bad-address"}, |
674 | + }, |
675 | + checkErr: `invalid state server address "bad-address"`, |
676 | +}, { |
677 | + about: "invalid api address", |
678 | + params: agent.AgentConfigParams{ |
679 | + DataDir: "/data/dir", |
680 | + Tag: "omg", |
681 | + Password: "sekrit", |
682 | + CACert: []byte("ca cert"), |
683 | + APIAddresses: []string{"localhost:8080", "bad-address"}, |
684 | + }, |
685 | + checkErr: `invalid API server address "bad-address"`, |
686 | +}, { |
687 | + about: "good state addresses", |
688 | + params: agent.AgentConfigParams{ |
689 | + DataDir: "/data/dir", |
690 | + Tag: "omg", |
691 | + Password: "sekrit", |
692 | + CACert: []byte("ca cert"), |
693 | + StateAddresses: []string{"localhost:1234"}, |
694 | + }, |
695 | +}, { |
696 | + about: "good api addresses", |
697 | + params: agent.AgentConfigParams{ |
698 | + DataDir: "/data/dir", |
699 | + Tag: "omg", |
700 | + Password: "sekrit", |
701 | + CACert: []byte("ca cert"), |
702 | + APIAddresses: []string{"localhost:1234"}, |
703 | + }, |
704 | +}, { |
705 | + about: "both state and api addresses", |
706 | + params: agent.AgentConfigParams{ |
707 | + DataDir: "/data/dir", |
708 | + Tag: "omg", |
709 | + Password: "sekrit", |
710 | + CACert: []byte("ca cert"), |
711 | + StateAddresses: []string{"localhost:1234"}, |
712 | + APIAddresses: []string{"localhost:1235"}, |
713 | + }, |
714 | +}, { |
715 | + about: "everything...", |
716 | + params: agent.AgentConfigParams{ |
717 | + DataDir: "/data/dir", |
718 | + Tag: "omg", |
719 | + Password: "sekrit", |
720 | + CACert: []byte("ca cert"), |
721 | + StateAddresses: []string{"localhost:1234"}, |
722 | + APIAddresses: []string{"localhost:1235"}, |
723 | + Nonce: "a nonce", |
724 | + }, |
725 | }} |
726 | |
727 | -func (suite) TestConfReadWriteCheck(c *gc.C) { |
728 | - d := c.MkDir() |
729 | - dataDir := filepath.Join(d, "data") |
730 | - for i, test := range confTests { |
731 | - c.Logf("test %d; %s", i, test.about) |
732 | - conf := test.conf |
733 | - conf.DataDir = dataDir |
734 | - err := conf.Check() |
735 | - if test.checkErr != "" { |
736 | - c.Assert(err, gc.ErrorMatches, test.checkErr) |
737 | - c.Assert(conf.Write(), gc.ErrorMatches, test.checkErr) |
738 | - cmds, err := conf.WriteCommands() |
739 | - c.Assert(cmds, gc.IsNil) |
740 | - c.Assert(err, gc.ErrorMatches, test.checkErr) |
741 | - continue |
742 | - } |
743 | - c.Assert(err, gc.IsNil) |
744 | - err = os.Mkdir(dataDir, 0777) |
745 | - c.Assert(err, gc.IsNil) |
746 | - err = conf.Write() |
747 | - c.Assert(err, gc.IsNil) |
748 | - info, err := os.Stat(conf.File("agent.conf")) |
749 | - c.Assert(err, gc.IsNil) |
750 | - c.Assert(info.Mode()&os.ModePerm, gc.Equals, os.FileMode(0600)) |
751 | - |
752 | - // Move the configuration file to a different directory |
753 | - // to check that the entity name gets set correctly when |
754 | - // reading. |
755 | - newDir := filepath.Join(dataDir, "agents", "another") |
756 | - err = os.Mkdir(newDir, 0777) |
757 | - c.Assert(err, gc.IsNil) |
758 | - err = os.Rename(conf.File("agent.conf"), filepath.Join(newDir, "agent.conf")) |
759 | - c.Assert(err, gc.IsNil) |
760 | - |
761 | - rconf, err := agent.ReadConf(dataDir, "another") |
762 | - c.Assert(err, gc.IsNil) |
763 | - c.Assert(rconf.StateInfo.Tag, gc.Equals, "another") |
764 | - if rconf.StateInfo != nil { |
765 | - rconf.StateInfo.Tag = conf.Tag() |
766 | - } |
767 | - if rconf.APIInfo != nil { |
768 | - rconf.APIInfo.Tag = conf.Tag() |
769 | - } |
770 | - c.Assert(rconf, gc.DeepEquals, &conf) |
771 | - |
772 | - err = os.RemoveAll(dataDir) |
773 | - c.Assert(err, gc.IsNil) |
774 | - |
775 | - // Try the equivalent shell commands. |
776 | - cmds, err := conf.WriteCommands() |
777 | - c.Assert(err, gc.IsNil) |
778 | - for _, cmd := range cmds { |
779 | - out, err := exec.Command("sh", "-c", cmd).CombinedOutput() |
780 | - c.Assert(err, gc.IsNil, gc.Commentf("command %q; output %q", cmd, out)) |
781 | - } |
782 | - info, err = os.Stat(conf.File("agent.conf")) |
783 | - c.Assert(err, gc.IsNil) |
784 | - c.Assert(info.Mode()&os.ModePerm, gc.Equals, os.FileMode(0600)) |
785 | - |
786 | - rconf, err = agent.ReadConf(dataDir, conf.StateInfo.Tag) |
787 | - c.Assert(err, gc.IsNil) |
788 | - |
789 | - c.Assert(rconf, gc.DeepEquals, &conf) |
790 | - |
791 | - err = os.RemoveAll(dataDir) |
792 | - c.Assert(err, gc.IsNil) |
793 | - } |
794 | -} |
795 | - |
796 | -func (suite) TestCheckNoDataDir(c *gc.C) { |
797 | - conf := agent.Conf{ |
798 | - StateInfo: &state.Info{ |
799 | - Addrs: []string{"x:4"}, |
800 | - CACert: []byte("xxx"), |
801 | - Tag: "bar", |
802 | - Password: "pass", |
803 | - }, |
804 | - } |
805 | - c.Assert(conf.Check(), gc.ErrorMatches, "data directory not found in configuration") |
806 | -} |
807 | - |
808 | -func (suite) TestConfDir(c *gc.C) { |
809 | - conf := agent.Conf{ |
810 | - DataDir: "/foo", |
811 | - StateInfo: &state.Info{ |
812 | - Addrs: []string{"x:4"}, |
813 | - CACert: []byte("xxx"), |
814 | - Tag: "bar", |
815 | - Password: "pass", |
816 | - }, |
817 | - } |
818 | - c.Assert(conf.Dir(), gc.Equals, "/foo/agents/bar") |
819 | -} |
820 | - |
821 | -func (suite) TestConfFile(c *gc.C) { |
822 | - conf := agent.Conf{ |
823 | - DataDir: "/foo", |
824 | - StateInfo: &state.Info{ |
825 | - Addrs: []string{"x:4"}, |
826 | - CACert: []byte("xxx"), |
827 | - Tag: "bar", |
828 | - Password: "pass", |
829 | - }, |
830 | - } |
831 | - c.Assert(conf.File("x/y"), gc.Equals, "/foo/agents/bar/x/y") |
832 | -} |
833 | - |
834 | -type openSuite struct { |
835 | - testing.JujuConnSuite |
836 | -} |
837 | - |
838 | -var _ = gc.Suite(&openSuite{}) |
839 | - |
840 | -func (s *openSuite) TestOpenStateNormal(c *gc.C) { |
841 | - conf := agent.Conf{ |
842 | - StateInfo: s.StateInfo(c), |
843 | - } |
844 | - conf.OldPassword = "irrelevant" |
845 | - st, err := conf.OpenState() |
846 | - c.Assert(err, gc.IsNil) |
847 | - st.Close() |
848 | -} |
849 | - |
850 | -func (s *openSuite) TestOpenStateFallbackPassword(c *gc.C) { |
851 | - conf := agent.Conf{ |
852 | - StateInfo: s.StateInfo(c), |
853 | - } |
854 | - conf.OldPassword = conf.StateInfo.Password |
855 | - conf.StateInfo.Password = "not the right password" |
856 | - |
857 | - st, err := conf.OpenState() |
858 | - c.Assert(err, gc.IsNil) |
859 | - c.Assert(st, gc.NotNil) |
860 | - st.Close() |
861 | -} |
862 | - |
863 | -func (s *openSuite) TestOpenStateNoPassword(c *gc.C) { |
864 | - conf := agent.Conf{ |
865 | - StateInfo: s.StateInfo(c), |
866 | - } |
867 | - conf.OldPassword = conf.StateInfo.Password |
868 | - conf.StateInfo.Password = "" |
869 | - |
870 | - st, err := conf.OpenState() |
871 | - c.Assert(err, gc.IsNil) |
872 | - c.Assert(st, gc.NotNil) |
873 | - st.Close() |
874 | -} |
875 | - |
876 | -func (s *openSuite) TestOpenAPINormal(c *gc.C) { |
877 | - conf := agent.Conf{ |
878 | - APIInfo: s.APIInfo(c), |
879 | - } |
880 | - conf.OldPassword = "irrelevant" |
881 | - |
882 | - st, newPassword, err := conf.OpenAPI(api.DialOpts{}) |
883 | - c.Assert(err, gc.IsNil) |
884 | - defer st.Close() |
885 | - c.Assert(newPassword, gc.Equals, "") |
886 | - c.Assert(st, gc.NotNil) |
887 | -} |
888 | - |
889 | -func (s *openSuite) TestOpenAPIFallbackPassword(c *gc.C) { |
890 | - conf := agent.Conf{ |
891 | - APIInfo: s.APIInfo(c), |
892 | - } |
893 | - conf.OldPassword = conf.APIInfo.Password |
894 | - conf.APIInfo.Password = "not the right password" |
895 | - |
896 | - st, newPassword, err := conf.OpenAPI(api.DialOpts{}) |
897 | - c.Assert(err, gc.IsNil) |
898 | - defer st.Close() |
899 | - c.Assert(newPassword, gc.Matches, ".+") |
900 | - c.Assert(st, gc.NotNil) |
901 | - p, err := utils.RandomPassword() |
902 | - c.Assert(err, gc.IsNil) |
903 | - c.Assert(newPassword, gc.HasLen, len(p)) |
904 | - c.Assert(conf.OldPassword, gc.Equals, s.APIInfo(c).Password) |
905 | -} |
906 | - |
907 | -func (s *openSuite) TestOpenAPINoPassword(c *gc.C) { |
908 | - conf := agent.Conf{ |
909 | - APIInfo: s.APIInfo(c), |
910 | - } |
911 | - conf.OldPassword = conf.APIInfo.Password |
912 | - conf.APIInfo.Password = "" |
913 | - |
914 | - st, newPassword, err := conf.OpenAPI(api.DialOpts{}) |
915 | - c.Assert(err, gc.IsNil) |
916 | - defer st.Close() |
917 | - c.Assert(newPassword, gc.Matches, ".+") |
918 | - c.Assert(st, gc.NotNil) |
919 | - p, err := utils.RandomPassword() |
920 | - c.Assert(err, gc.IsNil) |
921 | - c.Assert(newPassword, gc.HasLen, len(p)) |
922 | - c.Assert(conf.OldPassword, gc.Equals, s.APIInfo(c).Password) |
923 | -} |
924 | +func (*suite) TestNewAgentConfig(c *gc.C) { |
925 | + |
926 | + for i, test := range agentConfigTests { |
927 | + c.Logf("%v: %s", i, test.about) |
928 | + _, err := agent.NewAgentConfig(test.params) |
929 | + if test.checkErr == "" { |
930 | + c.Assert(err, gc.IsNil) |
931 | + } else { |
932 | + c.Assert(err, gc.ErrorMatches, test.checkErr) |
933 | + } |
934 | + } |
935 | +} |
936 | + |
937 | +func (*suite) TestNewStateMachineConfig(c *gc.C) { |
938 | + type testStruct struct { |
939 | + about string |
940 | + params agent.StateMachineConfigParams |
941 | + checkErr string |
942 | + } |
943 | + var tests = []testStruct{{ |
944 | + about: "missing state server cert", |
945 | + checkErr: "state server cert not found in configuration", |
946 | + }, { |
947 | + about: "missing state server key", |
948 | + params: agent.StateMachineConfigParams{ |
949 | + StateServerCert: []byte("server cert"), |
950 | + }, |
951 | + checkErr: "state server key not found in configuration", |
952 | + }} |
953 | + |
954 | + for _, test := range agentConfigTests { |
955 | + tests = append(tests, testStruct{ |
956 | + about: test.about, |
957 | + params: agent.StateMachineConfigParams{ |
958 | + StateServerCert: []byte("server cert"), |
959 | + StateServerKey: []byte("server key"), |
960 | + AgentConfigParams: test.params, |
961 | + }, |
962 | + checkErr: test.checkErr, |
963 | + }) |
964 | + } |
965 | + |
966 | + for i, test := range tests { |
967 | + c.Logf("%v: %s", i, test.about) |
968 | + _, err := agent.NewStateMachineConfig(test.params) |
969 | + if test.checkErr == "" { |
970 | + c.Assert(err, gc.IsNil) |
971 | + } else { |
972 | + c.Assert(err, gc.ErrorMatches, test.checkErr) |
973 | + } |
974 | + } |
975 | +} |
976 | + |
977 | +var attributeParams = agent.AgentConfigParams{ |
978 | + DataDir: "/data/dir", |
979 | + Tag: "omg", |
980 | + Password: "sekrit", |
981 | + CACert: []byte("ca cert"), |
982 | + StateAddresses: []string{"localhost:1234"}, |
983 | + Nonce: "a nonce", |
984 | +} |
985 | + |
986 | +func (*suite) TestAttributes(c *gc.C) { |
987 | + conf, err := agent.NewAgentConfig(attributeParams) |
988 | + c.Assert(err, gc.IsNil) |
989 | + c.Assert(conf.DataDir(), gc.Equals, "/data/dir") |
990 | + c.Assert(conf.Tag(), gc.Equals, "omg") |
991 | + c.Assert(conf.Dir(), gc.Equals, "/data/dir/agents/omg") |
992 | + c.Assert(conf.Nonce(), gc.Equals, "a nonce") |
993 | +} |
994 | + |
995 | +func (*suite) TestWriteAndRead(c *gc.C) { |
996 | + testParams := attributeParams |
997 | + testParams.DataDir = c.MkDir() |
998 | + conf, err := agent.NewAgentConfig(testParams) |
999 | + c.Assert(err, gc.IsNil) |
1000 | + |
1001 | + c.Assert(conf.Write(), gc.IsNil) |
1002 | + reread, err := agent.ReadConf(conf.DataDir(), conf.Tag()) |
1003 | + c.Assert(err, gc.IsNil) |
1004 | + // Since we can't directly poke the internals, we'll use the WriteCommands |
1005 | + // method. |
1006 | + confCommands, err := conf.WriteCommands() |
1007 | + c.Assert(err, gc.IsNil) |
1008 | + rereadCommands, err := reread.WriteCommands() |
1009 | + c.Assert(err, gc.IsNil) |
1010 | + c.Assert(confCommands, gc.DeepEquals, rereadCommands) |
1011 | +} |
1012 | + |
1013 | +func (*suite) TestGenerateNewPassword(c *gc.C) { |
1014 | + |
1015 | + for i, test := range []struct { |
1016 | + about string |
1017 | + params agent.AgentConfigParams |
1018 | + }{{ |
1019 | + about: "good state addresses", |
1020 | + params: agent.AgentConfigParams{ |
1021 | + DataDir: c.MkDir(), |
1022 | + Tag: "omg", |
1023 | + Password: "sekrit", |
1024 | + CACert: []byte("ca cert"), |
1025 | + StateAddresses: []string{"localhost:1234"}, |
1026 | + }, |
1027 | + }, { |
1028 | + about: "good api addresses", |
1029 | + params: agent.AgentConfigParams{ |
1030 | + DataDir: c.MkDir(), |
1031 | + Tag: "omg", |
1032 | + Password: "sekrit", |
1033 | + CACert: []byte("ca cert"), |
1034 | + APIAddresses: []string{"localhost:1234"}, |
1035 | + }, |
1036 | + }, { |
1037 | + about: "both state and api addresses", |
1038 | + params: agent.AgentConfigParams{ |
1039 | + DataDir: c.MkDir(), |
1040 | + Tag: "omg", |
1041 | + Password: "sekrit", |
1042 | + CACert: []byte("ca cert"), |
1043 | + StateAddresses: []string{"localhost:1234"}, |
1044 | + APIAddresses: []string{"localhost:1235"}, |
1045 | + }, |
1046 | + }} { |
1047 | + c.Logf("%v: %s", i, test.about) |
1048 | + |
1049 | + conf, err := agent.NewAgentConfig(test.params) |
1050 | + c.Assert(err, gc.IsNil) |
1051 | + _, err = conf.GenerateNewPassword() |
1052 | + c.Assert(err, gc.IsNil) |
1053 | + // Show that the password is saved. |
1054 | + reread, err := agent.ReadConf(conf.DataDir(), conf.Tag()) |
1055 | + c.Assert(conf.PasswordHash(), gc.Equals, reread.PasswordHash()) |
1056 | + } |
1057 | +} |
1058 | + |
1059 | +// Actual opening of state and api requires a lot more boiler plate to make |
1060 | +// sure they are valid connections. This is done in the cmd/jujud tests for |
1061 | +// bootstrap, machine and unit tests. |
1062 | |
1063 | === modified file 'cmd/jujud/agent.go' |
1064 | --- cmd/jujud/agent.go 2013-08-02 12:49:26 +0000 |
1065 | +++ cmd/jujud/agent.go 2013-08-22 23:04:24 +0000 |
1066 | @@ -32,8 +32,8 @@ |
1067 | |
1068 | // AgentConf handles command-line flags shared by all agents. |
1069 | type AgentConf struct { |
1070 | - *agent.Conf |
1071 | dataDir string |
1072 | + config agent.Config |
1073 | } |
1074 | |
1075 | // addFlags injects common agent flags into f. |
1076 | @@ -48,10 +48,9 @@ |
1077 | return cmd.CheckEmpty(args) |
1078 | } |
1079 | |
1080 | -func (c *AgentConf) read(tag string) error { |
1081 | - var err error |
1082 | - c.Conf, err = agent.ReadConf(c.dataDir, tag) |
1083 | - return err |
1084 | +func (c *AgentConf) read(tag string) (err error) { |
1085 | + c.config, err = agent.ReadConf(c.dataDir, tag) |
1086 | + return |
1087 | } |
1088 | |
1089 | func importance(err error) int { |
1090 | @@ -124,8 +123,8 @@ |
1091 | return true |
1092 | } |
1093 | |
1094 | -func openState(c *agent.Conf, a Agent) (*state.State, AgentState, error) { |
1095 | - st, err := c.OpenState() |
1096 | +func openState(agentConfig agent.Config, a Agent) (*state.State, AgentState, error) { |
1097 | + st, err := agentConfig.OpenState() |
1098 | if err != nil { |
1099 | return nil, nil, err |
1100 | } |
1101 | @@ -140,13 +139,13 @@ |
1102 | return st, entity, nil |
1103 | } |
1104 | |
1105 | -func openAPIState(c *agent.Conf, a Agent) (*api.State, *apiagent.Entity, error) { |
1106 | +func openAPIState(agentConfig agent.Config, a Agent) (*api.State, *apiagent.Entity, error) { |
1107 | // We let the API dial fail immediately because the |
1108 | // runner's loop outside the caller of openAPIState will |
1109 | // keep on retrying. If we block for ages here, |
1110 | // then the worker that's calling this cannot |
1111 | // be interrupted. |
1112 | - st, newPassword, err := c.OpenAPI(api.DialOpts{}) |
1113 | + st, newPassword, err := agentConfig.OpenAPI(api.DialOpts{}) |
1114 | if err != nil { |
1115 | return nil, nil, err |
1116 | } |
1117 | @@ -158,26 +157,10 @@ |
1118 | st.Close() |
1119 | return nil, nil, err |
1120 | } |
1121 | - if newPassword == "" { |
1122 | - return st, entity, nil |
1123 | - } |
1124 | - // Make a copy of the configuration so that if we fail |
1125 | - // to write the configuration file, the configuration will |
1126 | - // still be valid. |
1127 | - c1 := *c |
1128 | - stateInfo := *c.StateInfo |
1129 | - c1.StateInfo = &stateInfo |
1130 | - apiInfo := *c.APIInfo |
1131 | - c1.APIInfo = &apiInfo |
1132 | - |
1133 | - c1.StateInfo.Password = newPassword |
1134 | - c1.APIInfo.Password = newPassword |
1135 | - if err := c1.Write(); err != nil { |
1136 | - return nil, nil, err |
1137 | - } |
1138 | - *c = c1 |
1139 | - if err := entity.SetPassword(newPassword); err != nil { |
1140 | - return nil, nil, err |
1141 | + if newPassword != "" { |
1142 | + if err := entity.SetPassword(newPassword); err != nil { |
1143 | + return nil, nil, err |
1144 | + } |
1145 | } |
1146 | return st, entity, nil |
1147 | |
1148 | |
1149 | === modified file 'cmd/jujud/agent_test.go' |
1150 | --- cmd/jujud/agent_test.go 2013-08-19 11:17:19 +0000 |
1151 | +++ cmd/jujud/agent_test.go 2013-08-22 23:04:24 +0000 |
1152 | @@ -108,26 +108,60 @@ |
1153 | testing.JujuConnSuite |
1154 | } |
1155 | |
1156 | -// primeAgent writes the configuration file and tools |
1157 | -// for an agent with the given entity name. |
1158 | -// It returns the agent's configuration and the current tools. |
1159 | -func (s *agentSuite) primeAgent(c *gc.C, tag, password string) (*agent.Conf, *tools.Tools) { |
1160 | - agentTools := s.primeTools(c, version.Current) |
1161 | - tools1, err := tools.ChangeAgentTools(s.DataDir(), tag, version.Current) |
1162 | - c.Assert(err, gc.IsNil) |
1163 | - c.Assert(tools1, gc.DeepEquals, agentTools) |
1164 | - |
1165 | - conf := &agent.Conf{ |
1166 | - DataDir: s.DataDir(), |
1167 | - StateInfo: s.StateInfo(c), |
1168 | - APIInfo: s.APIInfo(c), |
1169 | - } |
1170 | - conf.StateInfo.Tag = tag |
1171 | - conf.StateInfo.Password = password |
1172 | - conf.APIInfo.Tag = tag |
1173 | - conf.APIInfo.Password = password |
1174 | - err = conf.Write() |
1175 | - c.Assert(err, gc.IsNil) |
1176 | +// primeAgent writes the configuration file and tools for an agent with the |
1177 | +// given entity name. It returns the agent's configuration and the current |
1178 | +// tools. |
1179 | +func (s *agentSuite) primeAgent(c *gc.C, tag, password string) (agent.Config, *tools.Tools) { |
1180 | + agentTools := s.primeTools(c, version.Current) |
1181 | + tools1, err := tools.ChangeAgentTools(s.DataDir(), tag, version.Current) |
1182 | + c.Assert(err, gc.IsNil) |
1183 | + c.Assert(tools1, gc.DeepEquals, agentTools) |
1184 | + |
1185 | + stateInfo := s.StateInfo(c) |
1186 | + apiInfo := s.APIInfo(c) |
1187 | + conf, err := agent.NewAgentConfig( |
1188 | + agent.AgentConfigParams{ |
1189 | + DataDir: s.DataDir(), |
1190 | + Tag: tag, |
1191 | + Password: password, |
1192 | + Nonce: state.BootstrapNonce, |
1193 | + StateAddresses: stateInfo.Addrs, |
1194 | + APIAddresses: apiInfo.Addrs, |
1195 | + CACert: stateInfo.CACert, |
1196 | + }) |
1197 | + c.Assert(conf.Write(), gc.IsNil) |
1198 | + return conf, agentTools |
1199 | +} |
1200 | + |
1201 | +// primeStateAgent writes the configuration file and tools for an agent with the |
1202 | +// given entity name. It returns the agent's configuration and the current |
1203 | +// tools. |
1204 | +func (s *agentSuite) primeStateAgent(c *gc.C, tag, password string) (agent.Config, *tools.Tools) { |
1205 | + agentTools := s.primeTools(c, version.Current) |
1206 | + tools1, err := tools.ChangeAgentTools(s.DataDir(), tag, version.Current) |
1207 | + c.Assert(err, gc.IsNil) |
1208 | + c.Assert(tools1, gc.DeepEquals, agentTools) |
1209 | + |
1210 | + stateInfo := s.StateInfo(c) |
1211 | + port := coretesting.FindTCPPort() |
1212 | + apiAddr := []string{fmt.Sprintf("localhost:%d", port)} |
1213 | + conf, err := agent.NewStateMachineConfig( |
1214 | + agent.StateMachineConfigParams{ |
1215 | + AgentConfigParams: agent.AgentConfigParams{ |
1216 | + DataDir: s.DataDir(), |
1217 | + Tag: tag, |
1218 | + Password: password, |
1219 | + Nonce: state.BootstrapNonce, |
1220 | + StateAddresses: stateInfo.Addrs, |
1221 | + APIAddresses: apiAddr, |
1222 | + CACert: stateInfo.CACert, |
1223 | + }, |
1224 | + StateServerCert: []byte(coretesting.ServerCert), |
1225 | + StateServerKey: []byte(coretesting.ServerKey), |
1226 | + StatePort: coretesting.MgoPort, |
1227 | + APIPort: port, |
1228 | + }) |
1229 | + c.Assert(conf.Write(), gc.IsNil) |
1230 | return conf, agentTools |
1231 | } |
1232 | |
1233 | @@ -177,21 +211,12 @@ |
1234 | return agentTools |
1235 | } |
1236 | |
1237 | -func (s *agentSuite) testOpenAPIState(c *gc.C, ent state.AgentEntity, agentCmd Agent) { |
1238 | +func (s *agentSuite) testOpenAPIState(c *gc.C, ent state.AgentEntity, agentCmd Agent, initialPassword string) { |
1239 | conf, err := agent.ReadConf(s.DataDir(), ent.Tag()) |
1240 | c.Assert(err, gc.IsNil) |
1241 | |
1242 | // Check that it starts initially and changes the password |
1243 | - err = ent.SetPassword("initial") |
1244 | - c.Assert(err, gc.IsNil) |
1245 | - |
1246 | - conf.OldPassword = "initial" |
1247 | - conf.APIInfo.Password = "" |
1248 | - conf.StateInfo.Password = "" |
1249 | - err = conf.Write() |
1250 | - c.Assert(err, gc.IsNil) |
1251 | - |
1252 | - assertOpen := func(conf *agent.Conf) { |
1253 | + assertOpen := func(conf agent.Config) { |
1254 | st, gotEnt, err := openAPIState(conf, agentCmd) |
1255 | c.Assert(err, gc.IsNil) |
1256 | c.Assert(st, gc.NotNil) |
1257 | @@ -203,41 +228,12 @@ |
1258 | // Check that the initial password is no longer valid. |
1259 | err = ent.Refresh() |
1260 | c.Assert(err, gc.IsNil) |
1261 | - c.Assert(ent.PasswordValid("initial"), gc.Equals, false) |
1262 | - |
1263 | - // Check that the passwords in the configuration are correct. |
1264 | - c.Assert(ent.PasswordValid(conf.APIInfo.Password), gc.Equals, true) |
1265 | - c.Assert(conf.StateInfo.Password, gc.Equals, conf.APIInfo.Password) |
1266 | - c.Assert(conf.OldPassword, gc.Equals, "initial") |
1267 | - |
1268 | - // Read the configuration and check the same |
1269 | - c.Assert(refreshConfig(conf), gc.IsNil) |
1270 | - c.Assert(ent.PasswordValid(conf.APIInfo.Password), gc.Equals, true) |
1271 | - c.Assert(conf.StateInfo.Password, gc.Equals, conf.APIInfo.Password) |
1272 | - c.Assert(conf.OldPassword, gc.Equals, "initial") |
1273 | + c.Assert(ent.PasswordValid(initialPassword), gc.Equals, false) |
1274 | |
1275 | // Read the configuration and check that we can connect with it. |
1276 | - c.Assert(refreshConfig(conf), gc.IsNil) |
1277 | - |
1278 | + conf = refreshConfig(c, conf) |
1279 | // Check we can open the API with the new configuration. |
1280 | assertOpen(conf) |
1281 | - |
1282 | - newPassword := conf.StateInfo.Password |
1283 | - |
1284 | - // Change the password in the configuration and check |
1285 | - // that it falls back to using the initial password |
1286 | - c.Assert(refreshConfig(conf), gc.IsNil) |
1287 | - conf.APIInfo.Password = "spurious" |
1288 | - conf.OldPassword = newPassword |
1289 | - c.Assert(conf.Write(), gc.IsNil) |
1290 | - assertOpen(conf) |
1291 | - |
1292 | - // Check that it's changed the password again... |
1293 | - c.Assert(conf.APIInfo.Password, gc.Not(gc.Equals), "spurious") |
1294 | - c.Assert(conf.APIInfo.Password, gc.Not(gc.Equals), newPassword) |
1295 | - |
1296 | - // ... and that we can still open the state with the new configuration. |
1297 | - assertOpen(conf) |
1298 | } |
1299 | |
1300 | func (s *agentSuite) testUpgrade(c *gc.C, agent runner, currentTools *tools.Tools) { |
1301 | @@ -252,11 +248,8 @@ |
1302 | c.Assert(ug.OldTools, gc.DeepEquals, currentTools) |
1303 | } |
1304 | |
1305 | -func refreshConfig(c *agent.Conf) error { |
1306 | - nc, err := agent.ReadConf(c.DataDir, c.StateInfo.Tag) |
1307 | - if err != nil { |
1308 | - return err |
1309 | - } |
1310 | - *c = *nc |
1311 | - return nil |
1312 | +func refreshConfig(c *gc.C, config agent.Config) agent.Config { |
1313 | + config, err := agent.ReadConf(config.DataDir(), config.Tag()) |
1314 | + c.Assert(err, gc.IsNil) |
1315 | + return config |
1316 | } |
1317 | |
1318 | === modified file 'cmd/jujud/bootstrap.go' |
1319 | --- cmd/jujud/bootstrap.go 2013-07-19 02:21:57 +0000 |
1320 | +++ cmd/jujud/bootstrap.go 2013-08-22 23:04:24 +0000 |
1321 | @@ -7,8 +7,12 @@ |
1322 | "encoding/base64" |
1323 | "fmt" |
1324 | "io/ioutil" |
1325 | + "strings" |
1326 | + |
1327 | "launchpad.net/gnuflag" |
1328 | "launchpad.net/goyaml" |
1329 | + |
1330 | + "launchpad.net/juju-core/agent" |
1331 | "launchpad.net/juju-core/cmd" |
1332 | "launchpad.net/juju-core/constraints" |
1333 | "launchpad.net/juju-core/environs" |
1334 | @@ -16,7 +20,6 @@ |
1335 | "launchpad.net/juju-core/environs/config" |
1336 | "launchpad.net/juju-core/instance" |
1337 | "launchpad.net/juju-core/state" |
1338 | - "strings" |
1339 | ) |
1340 | |
1341 | // Cloud-init write the URL to be used to load the bootstrap state into this file. |
1342 | @@ -54,7 +57,8 @@ |
1343 | |
1344 | // Run initializes state for an environment. |
1345 | func (c *BootstrapCommand) Run(_ *cmd.Context) error { |
1346 | - if err := c.Conf.read("bootstrap"); err != nil { |
1347 | + err := c.Conf.read("bootstrap") |
1348 | + if err != nil { |
1349 | return err |
1350 | } |
1351 | cfg, err := config.New(c.EnvConfig) |
1352 | @@ -62,18 +66,13 @@ |
1353 | return err |
1354 | } |
1355 | |
1356 | - // There is no entity that's created at init time. |
1357 | - c.Conf.StateInfo.Tag = "" |
1358 | - st, err := state.Initialize(c.Conf.StateInfo, cfg, state.DefaultDialOpts()) |
1359 | + // There is no entity that's created at init time |
1360 | + st, err := agent.InitialStateConfiguration(c.Conf.config, cfg, state.DefaultDialOpts()) |
1361 | if err != nil { |
1362 | return err |
1363 | } |
1364 | defer st.Close() |
1365 | |
1366 | - if err := environs.BootstrapUsers(st, cfg, c.Conf.OldPassword); err != nil { |
1367 | - return err |
1368 | - } |
1369 | - |
1370 | // TODO(fwereade): we need to be able to customize machine jobs, |
1371 | // not just hardcode these values; in particular, JobHostUnits |
1372 | // on a machine, like this one, that is running JobManageEnviron |
1373 | @@ -106,7 +105,7 @@ |
1374 | characteristics = bsState.Characteristics[0] |
1375 | } |
1376 | |
1377 | - return environs.ConfigureBootstrapMachine(st, c.Constraints, c.Conf.DataDir, jobs, instance.Id(instId), characteristics) |
1378 | + return environs.ConfigureBootstrapMachine(st, c.Constraints, c.Conf.dataDir, jobs, instance.Id(instId), characteristics) |
1379 | } |
1380 | |
1381 | // yamlBase64Value implements gnuflag.Value on a map[string]interface{}. |
1382 | |
1383 | === modified file 'cmd/jujud/bootstrap_test.go' |
1384 | --- cmd/jujud/bootstrap_test.go 2013-08-19 11:17:19 +0000 |
1385 | +++ cmd/jujud/bootstrap_test.go 2013-08-22 23:04:24 +0000 |
1386 | @@ -18,7 +18,6 @@ |
1387 | "launchpad.net/juju-core/errors" |
1388 | "launchpad.net/juju-core/instance" |
1389 | "launchpad.net/juju-core/state" |
1390 | - "launchpad.net/juju-core/state/api" |
1391 | "launchpad.net/juju-core/testing" |
1392 | "launchpad.net/juju-core/utils" |
1393 | ) |
1394 | @@ -77,39 +76,27 @@ |
1395 | return utils.PasswordHash(testPassword) |
1396 | } |
1397 | |
1398 | -func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, args ...string) (machineConf *agent.Conf, cmd *BootstrapCommand, err error) { |
1399 | +func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, args ...string) (machineConf agent.Config, cmd *BootstrapCommand, err error) { |
1400 | ioutil.WriteFile(s.providerStateURLFile, []byte("test://localhost/provider-state\n"), 0600) |
1401 | - bootConf := &agent.Conf{ |
1402 | - DataDir: s.dataDir, |
1403 | - OldPassword: testPasswordHash(), |
1404 | - StateInfo: &state.Info{ |
1405 | - Tag: "bootstrap", |
1406 | - Addrs: []string{testing.MgoAddr}, |
1407 | - CACert: []byte(testing.CACert), |
1408 | - }, |
1409 | - APIInfo: &api.Info{ |
1410 | - Tag: "bootstrap", |
1411 | - Addrs: []string{"0.1.2.3:1234"}, |
1412 | - CACert: []byte(testing.CACert), |
1413 | - }, |
1414 | + // NOTE: the old test used an equivalent of the NewAgentConfig, but it |
1415 | + // really should be using NewStateMachineConfig. |
1416 | + params := agent.AgentConfigParams{ |
1417 | + DataDir: s.dataDir, |
1418 | + Tag: "bootstrap", |
1419 | + Password: testPasswordHash(), |
1420 | + Nonce: state.BootstrapNonce, |
1421 | + StateAddresses: []string{testing.MgoAddr}, |
1422 | + APIAddresses: []string{"0.1.2.3:1234"}, |
1423 | + CACert: []byte(testing.CACert), |
1424 | } |
1425 | + bootConf, err := agent.NewAgentConfig(params) |
1426 | + c.Assert(err, gc.IsNil) |
1427 | err = bootConf.Write() |
1428 | c.Assert(err, gc.IsNil) |
1429 | |
1430 | - machineConf = &agent.Conf{ |
1431 | - DataDir: s.dataDir, |
1432 | - OldPassword: testPasswordHash(), |
1433 | - StateInfo: &state.Info{ |
1434 | - Tag: "machine-0", |
1435 | - Addrs: []string{testing.MgoAddr}, |
1436 | - CACert: []byte(testing.CACert), |
1437 | - }, |
1438 | - APIInfo: &api.Info{ |
1439 | - Tag: "machine-0", |
1440 | - Addrs: []string{"0.1.2.3:1234"}, |
1441 | - CACert: []byte(testing.CACert), |
1442 | - }, |
1443 | - } |
1444 | + params.Tag = "machine-0" |
1445 | + machineConf, err = agent.NewAgentConfig(params) |
1446 | + c.Assert(err, gc.IsNil) |
1447 | err = machineConf.Write() |
1448 | c.Assert(err, gc.IsNil) |
1449 | |
1450 | @@ -238,28 +225,13 @@ |
1451 | // Check that the machine configuration has been given a new |
1452 | // password and that we can connect to mongo as that machine |
1453 | // and that the in-mongo password also verifies correctly. |
1454 | - |
1455 | - machineConf1, err := agent.ReadConf(machineConf.DataDir, "machine-0") |
1456 | + machineConf1, err := agent.ReadConf(machineConf.DataDir(), "machine-0") |
1457 | c.Assert(err, gc.IsNil) |
1458 | - |
1459 | - c.Assert(machineConf1.OldPassword, gc.Equals, "") |
1460 | - c.Assert(machineConf1.APIInfo.Password, gc.Not(gc.Equals), "") |
1461 | - c.Assert(machineConf1.StateInfo.Password, gc.Equals, machineConf1.APIInfo.Password) |
1462 | - |
1463 | - // Check that no other information has been lost. |
1464 | - machineConf.OldPassword = "" |
1465 | - machineConf.APIInfo.Password = machineConf1.APIInfo.Password |
1466 | - machineConf.StateInfo.Password = machineConf1.StateInfo.Password |
1467 | - c.Assert(machineConf1, gc.DeepEquals, machineConf) |
1468 | - |
1469 | - info.Tag, info.Password = "machine-0", machineConf1.StateInfo.Password |
1470 | - st, err = state.Open(info, state.DefaultDialOpts()) |
1471 | + c.Assert(machineConf1.PasswordHash(), gc.Not(gc.Equals), testPasswordHash()) |
1472 | + |
1473 | + st, err = machineConf1.OpenState() |
1474 | c.Assert(err, gc.IsNil) |
1475 | defer st.Close() |
1476 | - |
1477 | - m, err := st.Machine("0") |
1478 | - c.Assert(err, gc.IsNil) |
1479 | - c.Assert(m.PasswordValid(machineConf1.StateInfo.Password), gc.Equals, true) |
1480 | } |
1481 | |
1482 | var base64ConfigTests = []struct { |
1483 | |
1484 | === modified file 'cmd/jujud/deploy_test.go' |
1485 | --- cmd/jujud/deploy_test.go 2013-08-19 11:17:19 +0000 |
1486 | +++ cmd/jujud/deploy_test.go 2013-08-22 23:04:24 +0000 |
1487 | @@ -12,10 +12,8 @@ |
1488 | gc "launchpad.net/gocheck" |
1489 | |
1490 | "launchpad.net/juju-core/state" |
1491 | - apideployer "launchpad.net/juju-core/state/api/deployer" |
1492 | "launchpad.net/juju-core/testing" |
1493 | "launchpad.net/juju-core/utils/set" |
1494 | - "launchpad.net/juju-core/worker/deployer" |
1495 | ) |
1496 | |
1497 | // fakeManager allows us to test deployments without actually deploying units |
1498 | @@ -78,29 +76,3 @@ |
1499 | } |
1500 | } |
1501 | } |
1502 | - |
1503 | -func patchDeployContext(c *gc.C, st *state.State, expectInfo *state.Info, expectDataDir string) (*fakeContext, func()) { |
1504 | - ctx := &fakeContext{ |
1505 | - inited: make(chan struct{}), |
1506 | - } |
1507 | - e0 := *expectInfo |
1508 | - expectInfo = &e0 |
1509 | - orig := newDeployContext |
1510 | - newDeployContext = func(dst *apideployer.State, dataDir string) (deployer.Context, error) { |
1511 | - caCert, err := dst.CACert() |
1512 | - if err != nil { |
1513 | - return nil, err |
1514 | - } |
1515 | - stateAddrs, err := dst.StateAddresses() |
1516 | - if err != nil { |
1517 | - return nil, err |
1518 | - } |
1519 | - c.Check(stateAddrs, gc.DeepEquals, expectInfo.Addrs) |
1520 | - c.Check(caCert, gc.DeepEquals, expectInfo.CACert) |
1521 | - c.Check(dataDir, gc.Equals, expectDataDir) |
1522 | - ctx.st = st |
1523 | - close(ctx.inited) |
1524 | - return ctx, nil |
1525 | - } |
1526 | - return ctx, func() { newDeployContext = orig } |
1527 | -} |
1528 | |
1529 | === modified file 'cmd/jujud/machine.go' |
1530 | --- cmd/jujud/machine.go 2013-08-19 13:29:56 +0000 |
1531 | +++ cmd/jujud/machine.go 2013-08-22 23:04:24 +0000 |
1532 | @@ -89,7 +89,7 @@ |
1533 | if err := a.Conf.read(a.Tag()); err != nil { |
1534 | return err |
1535 | } |
1536 | - charm.CacheDir = filepath.Join(a.Conf.DataDir, "charmcache") |
1537 | + charm.CacheDir = filepath.Join(a.Conf.dataDir, "charmcache") |
1538 | |
1539 | // ensureStateWorker ensures that there is a worker that |
1540 | // connects to the state that runs within itself all the workers |
1541 | @@ -137,7 +137,7 @@ |
1542 | // |
1543 | // If a state worker is necessary, APIWorker calls ensureStateWorker. |
1544 | func (a *MachineAgent) APIWorker(ensureStateWorker func()) (worker.Worker, error) { |
1545 | - st, entity, err := openAPIState(a.Conf.Conf, a) |
1546 | + st, entity, err := openAPIState(a.Conf.config, a) |
1547 | if err != nil { |
1548 | // There was an error connecting to the API, |
1549 | // https://launchpad.net/bugs/1199915 means that we may just |
1550 | @@ -167,12 +167,12 @@ |
1551 | }) |
1552 | runner.StartWorker("upgrader", func() (worker.Worker, error) { |
1553 | // TODO(rog) use id instead of *Machine (or introduce Clone method) |
1554 | - return upgrader.New(st.Upgrader(), a.Tag(), a.Conf.DataDir), nil |
1555 | + return upgrader.New(st.Upgrader(), a.Tag(), a.Conf.dataDir), nil |
1556 | }) |
1557 | for _, job := range entity.Jobs() { |
1558 | switch job { |
1559 | case params.JobHostUnits: |
1560 | - deployerTask, err := newDeployer(st.Deployer(), a.Tag(), a.Conf.DataDir) |
1561 | + deployerTask, err := newDeployer(st.Deployer(), a.Tag(), a.Conf.dataDir) |
1562 | if err != nil { |
1563 | return nil, err |
1564 | } |
1565 | @@ -192,9 +192,9 @@ |
1566 | } |
1567 | |
1568 | // StateJobs returns a worker running all the workers that require |
1569 | -// a *state.State connection. |
1570 | +// a *state.State cofnnection. |
1571 | func (a *MachineAgent) StateWorker() (worker.Worker, error) { |
1572 | - st, entity, err := openState(a.Conf.Conf, a) |
1573 | + st, entity, err := openState(a.Conf.config, a) |
1574 | if err != nil { |
1575 | return nil, err |
1576 | } |
1577 | @@ -202,7 +202,7 @@ |
1578 | m := entity.(*state.Machine) |
1579 | // TODO(rog) use more discriminating test for errors |
1580 | // rather than taking everything down indiscriminately. |
1581 | - dataDir := a.Conf.DataDir |
1582 | + dataDir := a.Conf.dataDir |
1583 | runner := worker.NewRunner(allFatal, moreImportant) |
1584 | // At this stage, since we don't embed lxc containers, just start an lxc |
1585 | // provisioner task for non-lxc containers. Since we have only LXC |
1586 | @@ -245,10 +245,11 @@ |
1587 | // the agent's configuration file. In the future, we may retrieve |
1588 | // the state server certificate and key from the state, and |
1589 | // this should then change. |
1590 | - if len(a.Conf.StateServerCert) == 0 || len(a.Conf.StateServerKey) == 0 { |
1591 | + port, cert, key := a.Conf.config.APIServerDetails() |
1592 | + if len(cert) == 0 || len(key) == 0 { |
1593 | return nil, &fatalError{"configuration does not have state server cert/key"} |
1594 | } |
1595 | - return apiserver.NewServer(st, fmt.Sprintf(":%d", a.Conf.APIPort), a.Conf.StateServerCert, a.Conf.StateServerKey) |
1596 | + return apiserver.NewServer(st, fmt.Sprintf(":%d", port), cert, key) |
1597 | }) |
1598 | runner.StartWorker("cleaner", func() (worker.Worker, error) { |
1599 | return cleaner.NewCleaner(st), nil |
1600 | @@ -275,7 +276,7 @@ |
1601 | return nil, err |
1602 | } |
1603 | // Check the machine nonce as provisioned matches the agent.Conf value. |
1604 | - if !m.CheckProvisioned(a.Conf.MachineNonce) { |
1605 | + if !m.CheckProvisioned(a.Conf.config.Nonce()) { |
1606 | // The agent is running on a different machine to the one it |
1607 | // should be according to state. It must stop immediately. |
1608 | log.Errorf("running machine %v agent on inappropriate instance", m) |
1609 | |
1610 | === modified file 'cmd/jujud/machine_test.go' |
1611 | --- cmd/jujud/machine_test.go 2013-08-20 15:01:49 +0000 |
1612 | +++ cmd/jujud/machine_test.go 2013-08-22 23:04:24 +0000 |
1613 | @@ -4,7 +4,6 @@ |
1614 | package main |
1615 | |
1616 | import ( |
1617 | - "fmt" |
1618 | "path/filepath" |
1619 | "reflect" |
1620 | "time" |
1621 | @@ -24,11 +23,13 @@ |
1622 | "launchpad.net/juju-core/provider/dummy" |
1623 | "launchpad.net/juju-core/state" |
1624 | "launchpad.net/juju-core/state/api" |
1625 | + apideployer "launchpad.net/juju-core/state/api/deployer" |
1626 | "launchpad.net/juju-core/state/api/params" |
1627 | "launchpad.net/juju-core/state/watcher" |
1628 | "launchpad.net/juju-core/testing" |
1629 | "launchpad.net/juju-core/testing/checkers" |
1630 | "launchpad.net/juju-core/version" |
1631 | + "launchpad.net/juju-core/worker/deployer" |
1632 | ) |
1633 | |
1634 | type MachineSuite struct { |
1635 | @@ -61,22 +62,27 @@ |
1636 | s.agentSuite.TearDownTest(c) |
1637 | } |
1638 | |
1639 | +const initialMachinePassword = "machine-password" |
1640 | + |
1641 | // primeAgent adds a new Machine to run the given jobs, and sets up the |
1642 | // machine agent's directory. It returns the new machine, the |
1643 | // agent's configuration and the tools currently running. |
1644 | -func (s *MachineSuite) primeAgent(c *gc.C, jobs ...state.MachineJob) (*state.Machine, *agent.Conf, *tools.Tools) { |
1645 | +func (s *MachineSuite) primeAgent(c *gc.C, jobs ...state.MachineJob) (m *state.Machine, config agent.Config, tools *tools.Tools) { |
1646 | m, err := s.State.InjectMachine("series", constraints.Value{}, "ardbeg-0", instance.HardwareCharacteristics{}, jobs...) |
1647 | c.Assert(err, gc.IsNil) |
1648 | - err = m.SetMongoPassword("machine-password") |
1649 | - c.Assert(err, gc.IsNil) |
1650 | - err = m.SetPassword("machine-password") |
1651 | - c.Assert(err, gc.IsNil) |
1652 | - conf, tools := s.agentSuite.primeAgent(c, names.MachineTag(m.Id()), "machine-password") |
1653 | - conf.MachineNonce = state.BootstrapNonce |
1654 | - conf.APIInfo.Nonce = conf.MachineNonce |
1655 | - err = conf.Write() |
1656 | - c.Assert(err, gc.IsNil) |
1657 | - return m, conf, tools |
1658 | + err = m.SetMongoPassword(initialMachinePassword) |
1659 | + c.Assert(err, gc.IsNil) |
1660 | + err = m.SetPassword(initialMachinePassword) |
1661 | + c.Assert(err, gc.IsNil) |
1662 | + tag := names.MachineTag(m.Id()) |
1663 | + if m.IsStateServer() { |
1664 | + config, tools = s.agentSuite.primeStateAgent(c, tag, initialMachinePassword) |
1665 | + } else { |
1666 | + config, tools = s.agentSuite.primeAgent(c, tag, initialMachinePassword) |
1667 | + } |
1668 | + err = config.Write() |
1669 | + c.Assert(err, gc.IsNil) |
1670 | + return m, config, tools |
1671 | } |
1672 | |
1673 | // newAgent returns a new MachineAgent instance |
1674 | @@ -128,7 +134,7 @@ |
1675 | err := a.Stop() |
1676 | c.Assert(err, gc.IsNil) |
1677 | c.Assert(<-done, gc.IsNil) |
1678 | - c.Assert(charm.CacheDir, gc.Equals, filepath.Join(ac.DataDir, "charmcache")) |
1679 | + c.Assert(charm.CacheDir, gc.Equals, filepath.Join(ac.DataDir(), "charmcache")) |
1680 | } |
1681 | |
1682 | func (s *MachineSuite) TestWithDeadMachine(c *gc.C) { |
1683 | @@ -174,9 +180,9 @@ |
1684 | } |
1685 | |
1686 | func (s *MachineSuite) TestHostUnits(c *gc.C) { |
1687 | - m, conf, _ := s.primeAgent(c, state.JobHostUnits) |
1688 | + m, _, _ := s.primeAgent(c, state.JobHostUnits) |
1689 | a := s.newAgent(c, m) |
1690 | - ctx, reset := patchDeployContext(c, s.BackingState, conf.StateInfo, conf.DataDir) |
1691 | + ctx, reset := patchDeployContext(c, s.BackingState) |
1692 | defer reset() |
1693 | go func() { c.Check(a.Run(nil), gc.IsNil) }() |
1694 | defer func() { c.Check(a.Stop(), gc.IsNil) }() |
1695 | @@ -234,6 +240,19 @@ |
1696 | ctx.waitDeployed(c) |
1697 | } |
1698 | |
1699 | +func patchDeployContext(c *gc.C, st *state.State) (*fakeContext, func()) { |
1700 | + ctx := &fakeContext{ |
1701 | + inited: make(chan struct{}), |
1702 | + } |
1703 | + orig := newDeployContext |
1704 | + newDeployContext = func(dst *apideployer.State, dataDir string) (deployer.Context, error) { |
1705 | + ctx.st = st |
1706 | + close(ctx.inited) |
1707 | + return ctx, nil |
1708 | + } |
1709 | + return ctx, func() { newDeployContext = orig } |
1710 | +} |
1711 | + |
1712 | func (s *MachineSuite) TestManageEnviron(c *gc.C) { |
1713 | usefulVersion := version.Current |
1714 | usefulVersion.Series = "series" // to match the charm created below |
1715 | @@ -297,35 +316,18 @@ |
1716 | } |
1717 | |
1718 | func (s *MachineSuite) TestUpgrade(c *gc.C) { |
1719 | - m, conf, currentTools := s.primeAgent(c, state.JobManageState, state.JobManageEnviron, state.JobHostUnits) |
1720 | - addAPIInfo(conf, m) |
1721 | - err := conf.Write() |
1722 | - c.Assert(err, gc.IsNil) |
1723 | + m, _, currentTools := s.primeAgent(c, state.JobManageState, state.JobManageEnviron, state.JobHostUnits) |
1724 | a := s.newAgent(c, m) |
1725 | s.testUpgrade(c, a, currentTools) |
1726 | } |
1727 | |
1728 | -// addAPIInfo adds information to the agent's configuration |
1729 | -// for serving the API. |
1730 | -func addAPIInfo(conf *agent.Conf, m *state.Machine) { |
1731 | - port := testing.FindTCPPort() |
1732 | - conf.APIInfo.Addrs = []string{fmt.Sprintf("localhost:%d", port)} |
1733 | - conf.APIInfo.CACert = []byte(testing.CACert) |
1734 | - conf.StateServerCert = []byte(testing.ServerCert) |
1735 | - conf.StateServerKey = []byte(testing.ServerKey) |
1736 | - conf.APIPort = port |
1737 | -} |
1738 | - |
1739 | var fastDialOpts = api.DialOpts{ |
1740 | Timeout: 1 * time.Second, |
1741 | RetryDelay: 10 * time.Millisecond, |
1742 | } |
1743 | |
1744 | -func (s *MachineSuite) assertJobWithState(c *gc.C, job state.MachineJob, test func(*agent.Conf, *state.State)) { |
1745 | +func (s *MachineSuite) assertJobWithState(c *gc.C, job state.MachineJob, test func(agent.Config, *state.State)) { |
1746 | stm, conf, _ := s.primeAgent(c, job) |
1747 | - addAPIInfo(conf, stm) |
1748 | - err := conf.Write() |
1749 | - c.Assert(err, gc.IsNil) |
1750 | a := s.newAgent(c, stm) |
1751 | defer a.Stop() |
1752 | |
1753 | @@ -346,7 +348,7 @@ |
1754 | c.Fatalf("state not opened") |
1755 | } |
1756 | |
1757 | - err = a.Stop() |
1758 | + err := a.Stop() |
1759 | if job == state.JobManageState { |
1760 | // When shutting down, the API server can be shut down before |
1761 | // the other workers that connect to it, so they get an error so |
1762 | @@ -370,18 +372,18 @@ |
1763 | } |
1764 | |
1765 | func (s *MachineSuite) TestManageStateServesAPI(c *gc.C) { |
1766 | - s.assertJobWithState(c, state.JobManageState, func(conf *agent.Conf, agentState *state.State) { |
1767 | - st, err := api.Open(conf.APIInfo, fastDialOpts) |
1768 | + s.assertJobWithState(c, state.JobManageState, func(conf agent.Config, agentState *state.State) { |
1769 | + st, _, err := conf.OpenAPI(fastDialOpts) |
1770 | c.Assert(err, gc.IsNil) |
1771 | defer st.Close() |
1772 | - m, err := st.Machiner().Machine(conf.APIInfo.Tag) |
1773 | + m, err := st.Machiner().Machine(conf.Tag()) |
1774 | c.Assert(err, gc.IsNil) |
1775 | c.Assert(m.Life(), gc.Equals, params.Alive) |
1776 | }) |
1777 | } |
1778 | |
1779 | func (s *MachineSuite) TestManageStateRunsCleaner(c *gc.C) { |
1780 | - s.assertJobWithState(c, state.JobManageState, func(conf *agent.Conf, agentState *state.State) { |
1781 | + s.assertJobWithState(c, state.JobManageState, func(conf agent.Config, agentState *state.State) { |
1782 | // Create a service and unit, and destroy the service. |
1783 | service, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress")) |
1784 | c.Assert(err, gc.IsNil) |
1785 | @@ -419,7 +421,7 @@ |
1786 | } |
1787 | |
1788 | func (s *MachineSuite) TestManageStateRunsMinUnitsWorker(c *gc.C) { |
1789 | - s.assertJobWithState(c, state.JobManageState, func(conf *agent.Conf, agentState *state.State) { |
1790 | + s.assertJobWithState(c, state.JobManageState, func(conf agent.Config, agentState *state.State) { |
1791 | // Ensure that the MinUnits worker is alive by doing a simple check |
1792 | // that it responds to state changes: add a service, set its minimum |
1793 | // number of units to one, wait for the worker to add the missing unit. |
1794 | @@ -451,38 +453,6 @@ |
1795 | }) |
1796 | } |
1797 | |
1798 | -var serveAPIWithBadConfTests = []struct { |
1799 | - change func(c *agent.Conf) |
1800 | - err string |
1801 | -}{{ |
1802 | - func(c *agent.Conf) { |
1803 | - c.StateServerCert = nil |
1804 | - }, |
1805 | - "configuration does not have state server cert/key", |
1806 | -}, { |
1807 | - func(c *agent.Conf) { |
1808 | - c.StateServerKey = nil |
1809 | - }, |
1810 | - "configuration does not have state server cert/key", |
1811 | -}} |
1812 | - |
1813 | -func (s *MachineSuite) TestServeAPIWithBadConf(c *gc.C) { |
1814 | - m, conf, _ := s.primeAgent(c, state.JobManageState) |
1815 | - addAPIInfo(conf, m) |
1816 | - for i, t := range serveAPIWithBadConfTests { |
1817 | - c.Logf("test %d: %q", i, t.err) |
1818 | - conf1 := *conf |
1819 | - t.change(&conf1) |
1820 | - err := conf1.Write() |
1821 | - c.Assert(err, gc.IsNil) |
1822 | - a := s.newAgent(c, m) |
1823 | - err = runWithTimeout(a) |
1824 | - c.Assert(err, gc.ErrorMatches, t.err) |
1825 | - err = refreshConfig(conf) |
1826 | - c.Assert(err, gc.IsNil) |
1827 | - } |
1828 | -} |
1829 | - |
1830 | // opRecvTimeout waits for any of the given kinds of operation to |
1831 | // be received from ops, and times out if not. |
1832 | func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation { |
1833 | @@ -504,5 +474,5 @@ |
1834 | |
1835 | func (s *MachineSuite) TestOpenAPIState(c *gc.C) { |
1836 | m, _, _ := s.primeAgent(c, state.JobHostUnits) |
1837 | - s.testOpenAPIState(c, m, s.newAgent(c, m)) |
1838 | + s.testOpenAPIState(c, m, s.newAgent(c, m), initialMachinePassword) |
1839 | } |
1840 | |
1841 | === modified file 'cmd/jujud/unit.go' |
1842 | --- cmd/jujud/unit.go 2013-08-02 14:04:05 +0000 |
1843 | +++ cmd/jujud/unit.go 2013-08-22 23:04:24 +0000 |
1844 | @@ -79,12 +79,12 @@ |
1845 | |
1846 | // StateWorkers returns a worker that runs the unit agent workers. |
1847 | func (a *UnitAgent) StateWorkers() (worker.Worker, error) { |
1848 | - st, entity, err := openState(a.Conf.Conf, a) |
1849 | + st, entity, err := openState(a.Conf.config, a) |
1850 | if err != nil { |
1851 | return nil, err |
1852 | } |
1853 | unit := entity.(*state.Unit) |
1854 | - dataDir := a.Conf.DataDir |
1855 | + dataDir := a.Conf.dataDir |
1856 | runner := worker.NewRunner(allFatal, moreImportant) |
1857 | runner.StartWorker("uniter", func() (worker.Worker, error) { |
1858 | return uniter.NewUniter(st, unit.Name(), dataDir), nil |
1859 | @@ -93,11 +93,11 @@ |
1860 | } |
1861 | |
1862 | func (a *UnitAgent) APIWorkers() (worker.Worker, error) { |
1863 | - st, entity, err := openAPIState(a.Conf.Conf, a) |
1864 | + st, entity, err := openAPIState(a.Conf.config, a) |
1865 | if err != nil { |
1866 | return nil, err |
1867 | } |
1868 | - dataDir := a.Conf.DataDir |
1869 | + dataDir := a.Conf.dataDir |
1870 | runner := worker.NewRunner(allFatal, moreImportant) |
1871 | runner.StartWorker("upgrader", func() (worker.Worker, error) { |
1872 | return upgrader.New(st.Upgrader(), entity.Tag(), dataDir), nil |
1873 | |
1874 | === modified file 'cmd/jujud/unit_test.go' |
1875 | --- cmd/jujud/unit_test.go 2013-08-19 11:17:19 +0000 |
1876 | +++ cmd/jujud/unit_test.go 2013-08-22 23:04:24 +0000 |
1877 | @@ -33,18 +33,20 @@ |
1878 | s.GitSuite.TearDownTest(c) |
1879 | } |
1880 | |
1881 | +const initialUnitPassword = "unit-password" |
1882 | + |
1883 | // primeAgent creates a unit, and sets up the unit agent's directory. |
1884 | // It returns the new unit and the agent's configuration. |
1885 | -func (s *UnitSuite) primeAgent(c *gc.C) (*state.Unit, *agent.Conf, *tools.Tools) { |
1886 | +func (s *UnitSuite) primeAgent(c *gc.C) (*state.Unit, agent.Config, *tools.Tools) { |
1887 | svc, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress")) |
1888 | c.Assert(err, gc.IsNil) |
1889 | unit, err := svc.AddUnit() |
1890 | c.Assert(err, gc.IsNil) |
1891 | - err = unit.SetMongoPassword("unit-password") |
1892 | - c.Assert(err, gc.IsNil) |
1893 | - err = unit.SetPassword("unit-password") |
1894 | - c.Assert(err, gc.IsNil) |
1895 | - conf, tools := s.agentSuite.primeAgent(c, unit.Tag(), "unit-password") |
1896 | + err = unit.SetMongoPassword(initialUnitPassword) |
1897 | + c.Assert(err, gc.IsNil) |
1898 | + err = unit.SetPassword(initialUnitPassword) |
1899 | + c.Assert(err, gc.IsNil) |
1900 | + conf, tools := s.agentSuite.primeAgent(c, unit.Tag(), initialUnitPassword) |
1901 | return unit, conf, tools |
1902 | } |
1903 | |
1904 | @@ -150,5 +152,5 @@ |
1905 | func (s *UnitSuite) TestOpenAPIState(c *gc.C) { |
1906 | c.Skip("unit agent API connection not implemented yet") |
1907 | unit, _, _ := s.primeAgent(c) |
1908 | - s.testOpenAPIState(c, unit, s.newAgent(c, unit)) |
1909 | + s.testOpenAPIState(c, unit, s.newAgent(c, unit), initialUnitPassword) |
1910 | } |
1911 | |
1912 | === modified file 'environs/bootstrap.go' |
1913 | --- environs/bootstrap.go 2013-08-06 21:02:51 +0000 |
1914 | +++ environs/bootstrap.go 2013-08-22 23:04:24 +0000 |
1915 | @@ -9,7 +9,6 @@ |
1916 | |
1917 | "launchpad.net/juju-core/agent" |
1918 | "launchpad.net/juju-core/constraints" |
1919 | - "launchpad.net/juju-core/environs/config" |
1920 | "launchpad.net/juju-core/errors" |
1921 | "launchpad.net/juju-core/instance" |
1922 | "launchpad.net/juju-core/state" |
1923 | @@ -82,31 +81,6 @@ |
1924 | return VerifyStorage(storage) |
1925 | } |
1926 | |
1927 | -// BootstrapUsers creates the initial admin user for the database, and sets |
1928 | -// the initial password. |
1929 | -func BootstrapUsers(st *state.State, cfg *config.Config, passwordHash string) error { |
1930 | - logger.Debugf("adding admin user") |
1931 | - // Set up initial authentication. |
1932 | - u, err := st.AddUser("admin", "") |
1933 | - if err != nil { |
1934 | - return err |
1935 | - } |
1936 | - |
1937 | - // Note that at bootstrap time, the password is set to |
1938 | - // the hash of its actual value. The first time a client |
1939 | - // connects to mongo, it changes the mongo password |
1940 | - // to the original password. |
1941 | - logger.Debugf("setting password hash for admin user") |
1942 | - if err := u.SetPasswordHash(passwordHash); err != nil { |
1943 | - return err |
1944 | - } |
1945 | - if err := st.SetAdminMongoPassword(passwordHash); err != nil { |
1946 | - return err |
1947 | - } |
1948 | - return nil |
1949 | - |
1950 | -} |
1951 | - |
1952 | // ConfigureBootstrapMachine adds the initial machine into state. As a part |
1953 | // of this process the environmental constraints are saved as constraints used |
1954 | // when bootstrapping are considered constraints for the entire environment. |
1955 | @@ -136,17 +110,10 @@ |
1956 | if err != nil { |
1957 | return err |
1958 | } |
1959 | - newPassword, err := utils.RandomPassword() |
1960 | + newPassword, err := mconf.GenerateNewPassword() |
1961 | if err != nil { |
1962 | return err |
1963 | } |
1964 | - mconf.StateInfo.Password = newPassword |
1965 | - mconf.APIInfo.Password = newPassword |
1966 | - mconf.OldPassword = "" |
1967 | - |
1968 | - if err := mconf.Write(); err != nil { |
1969 | - return err |
1970 | - } |
1971 | if err := m.SetMongoPassword(newPassword); err != nil { |
1972 | return err |
1973 | } |
1974 | |
1975 | === modified file 'environs/cloudinit/cloudinit.go' |
1976 | --- environs/cloudinit/cloudinit.go 2013-08-09 06:56:44 +0000 |
1977 | +++ environs/cloudinit/cloudinit.go 2013-08-22 23:04:24 +0000 |
1978 | @@ -230,36 +230,45 @@ |
1979 | return filepath.Join(cfg.DataDir, name) |
1980 | } |
1981 | |
1982 | -func (cfg *MachineConfig) agentConfig(tag string) *agent.Conf { |
1983 | - info := *cfg.StateInfo |
1984 | - apiInfo := *cfg.APIInfo |
1985 | - c := &agent.Conf{ |
1986 | - DataDir: cfg.DataDir, |
1987 | - StateInfo: &info, |
1988 | - APIInfo: &apiInfo, |
1989 | - StateServerCert: cfg.StateServerCert, |
1990 | - StateServerKey: cfg.StateServerKey, |
1991 | - StatePort: cfg.StatePort, |
1992 | - APIPort: cfg.APIPort, |
1993 | - MachineNonce: cfg.MachineNonce, |
1994 | - } |
1995 | - c.OldPassword = cfg.StateInfo.Password |
1996 | - |
1997 | - c.StateInfo.Addrs = cfg.stateHostAddrs() |
1998 | - c.StateInfo.Tag = tag |
1999 | - c.StateInfo.Password = "" |
2000 | - |
2001 | - c.APIInfo.Addrs = cfg.apiHostAddrs() |
2002 | - c.APIInfo.Tag = tag |
2003 | - c.APIInfo.Password = "" |
2004 | - |
2005 | - return c |
2006 | +func (cfg *MachineConfig) agentConfig(tag string) (agent.Config, error) { |
2007 | + // TODO for HAState: the stateHostAddrs and apiHostAddrs here assume that |
2008 | + // if the machine is a stateServer then to use localhost. This may be |
2009 | + // sufficient, but needs thought in the new world order. |
2010 | + var password string |
2011 | + if cfg.StateInfo == nil { |
2012 | + password = cfg.APIInfo.Password |
2013 | + } else { |
2014 | + password = cfg.StateInfo.Password |
2015 | + } |
2016 | + var configParams = agent.AgentConfigParams{ |
2017 | + DataDir: cfg.DataDir, |
2018 | + Tag: tag, |
2019 | + Password: password, |
2020 | + Nonce: cfg.MachineNonce, |
2021 | + StateAddresses: cfg.stateHostAddrs(), |
2022 | + APIAddresses: cfg.apiHostAddrs(), |
2023 | + CACert: cfg.StateInfo.CACert, |
2024 | + } |
2025 | + if cfg.StateServer { |
2026 | + return agent.NewStateMachineConfig( |
2027 | + agent.StateMachineConfigParams{ |
2028 | + AgentConfigParams: configParams, |
2029 | + StateServerCert: cfg.StateServerCert, |
2030 | + StateServerKey: cfg.StateServerKey, |
2031 | + StatePort: cfg.StatePort, |
2032 | + APIPort: cfg.APIPort, |
2033 | + }) |
2034 | + } |
2035 | + return agent.NewAgentConfig(configParams) |
2036 | } |
2037 | |
2038 | // addAgentInfo adds agent-required information to the agent's directory |
2039 | // and returns the agent directory name. |
2040 | -func (cfg *MachineConfig) addAgentInfo(c *cloudinit.Config, tag string) (*agent.Conf, error) { |
2041 | - acfg := cfg.agentConfig(tag) |
2042 | +func (cfg *MachineConfig) addAgentInfo(c *cloudinit.Config, tag string) (agent.Config, error) { |
2043 | + acfg, err := cfg.agentConfig(tag) |
2044 | + if err != nil { |
2045 | + return nil, err |
2046 | + } |
2047 | cmds, err := acfg.WriteCommands() |
2048 | if err != nil { |
2049 | return nil, err |
2050 | |
2051 | === modified file 'environs/cloudinit/cloudinit_test.go' |
2052 | --- environs/cloudinit/cloudinit_test.go 2013-08-20 14:02:31 +0000 |
2053 | +++ environs/cloudinit/cloudinit_test.go 2013-08-22 23:04:24 +0000 |
2054 | @@ -96,7 +96,7 @@ |
2055 | restart rsyslog |
2056 | mkdir -p '/var/lib/juju/agents/machine-0' |
2057 | install -m 600 /dev/null '/var/lib/juju/agents/machine-0/agent\.conf' |
2058 | -printf '%s\\n' 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\n' > '/var/lib/juju/agents/machine-0/agent\.conf' |
2059 | +printf '%s\\n' 'stateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\n' > '/var/lib/juju/agents/machine-0/agent\.conf' |
2060 | install -m 600 /dev/null '/var/lib/juju/server\.pem' |
2061 | printf '%s\\n' 'SERVER CERT\\n[^']*SERVER KEY\\n[^']*' > '/var/lib/juju/server\.pem' |
2062 | mkdir -p /var/lib/juju/db/journal |
2063 | @@ -107,7 +107,7 @@ |
2064 | start juju-db |
2065 | mkdir -p '/var/lib/juju/agents/bootstrap' |
2066 | install -m 600 /dev/null '/var/lib/juju/agents/bootstrap/agent\.conf' |
2067 | -printf '%s\\n' 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\n' > '/var/lib/juju/agents/bootstrap/agent\.conf' |
2068 | +printf '%s\\n' 'stateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\n' > '/var/lib/juju/agents/bootstrap/agent\.conf' |
2069 | echo 'some-url' > /tmp/provider-state-url |
2070 | /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --constraints 'mem=2048M' --debug |
2071 | rm -rf '/var/lib/juju/agents/bootstrap' |
2072 | @@ -155,7 +155,7 @@ |
2073 | restart rsyslog |
2074 | mkdir -p '/var/lib/juju/agents/machine-0' |
2075 | install -m 600 /dev/null '/var/lib/juju/agents/machine-0/agent\.conf' |
2076 | -printf '%s\\n' 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\n' > '/var/lib/juju/agents/machine-0/agent\.conf' |
2077 | +printf '%s\\n' 'stateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: machine-0\\n password: ""\\n' > '/var/lib/juju/agents/machine-0/agent\.conf' |
2078 | install -m 600 /dev/null '/var/lib/juju/server\.pem' |
2079 | printf '%s\\n' 'SERVER CERT\\n[^']*SERVER KEY\\n[^']*' > '/var/lib/juju/server\.pem' |
2080 | mkdir -p /var/lib/juju/db/journal |
2081 | @@ -166,7 +166,7 @@ |
2082 | start juju-db |
2083 | mkdir -p '/var/lib/juju/agents/bootstrap' |
2084 | install -m 600 /dev/null '/var/lib/juju/agents/bootstrap/agent\.conf' |
2085 | -printf '%s\\n' 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\n' > '/var/lib/juju/agents/bootstrap/agent\.conf' |
2086 | +printf '%s\\n' 'stateservercert:\\n[^']+stateserverkey:\\n[^']+stateport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - localhost:37017\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - localhost:17070\\n cacert:\\n[^']+ tag: bootstrap\\n password: ""\\n' > '/var/lib/juju/agents/bootstrap/agent\.conf' |
2087 | echo 'some-url' > /tmp/provider-state-url |
2088 | /var/lib/juju/tools/1\.2\.3-raring-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --constraints 'mem=2048M' --debug |
2089 | rm -rf '/var/lib/juju/agents/bootstrap' |
2090 | @@ -209,7 +209,7 @@ |
2091 | restart rsyslog |
2092 | mkdir -p '/var/lib/juju/agents/machine-99' |
2093 | install -m 600 /dev/null '/var/lib/juju/agents/machine-99/agent\.conf' |
2094 | -printf '%s\\n' 'datadir: /var/lib/juju\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - state-addr\.testing\.invalid:12345\\n cacert:\\n[^']+ tag: machine-99\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - state-addr\.testing\.invalid:54321\\n cacert:\\n[^']+ tag: machine-99\\n password: ""\\n' > '/var/lib/juju/agents/machine-99/agent\.conf' |
2095 | +printf '%s\\n' 'oldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - state-addr\.testing\.invalid:12345\\n cacert:\\n[^']+ tag: machine-99\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - state-addr\.testing\.invalid:54321\\n cacert:\\n[^']+ tag: machine-99\\n password: ""\\n' > '/var/lib/juju/agents/machine-99/agent\.conf' |
2096 | ln -s 1\.2\.3-linux-amd64 '/var/lib/juju/tools/machine-99' |
2097 | cat >> /etc/init/jujud-machine-99\.conf << 'EOF'\\ndescription "juju machine-99 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\nenv JUJU_PROVIDER_TYPE="dummy"\\n\\nlimit nofile 20000 20000\\n\\nexec /var/lib/juju/tools/machine-99/jujud machine --log-file '/var/log/juju/machine-99\.log' --data-dir '/var/lib/juju' --machine-id 99 --debug >> /var/log/juju/machine-99\.log 2>&1\\nEOF\\n |
2098 | start jujud-machine-99 |
2099 | @@ -250,7 +250,7 @@ |
2100 | restart rsyslog |
2101 | mkdir -p '/var/lib/juju/agents/machine-2-lxc-1' |
2102 | install -m 600 /dev/null '/var/lib/juju/agents/machine-2-lxc-1/agent\.conf' |
2103 | -printf '%s\\n' 'datadir: /var/lib/juju\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - state-addr\.testing\.invalid:12345\\n cacert:\\n[^']+ tag: machine-2-lxc-1\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - state-addr\.testing\.invalid:54321\\n cacert:\\n[^']+ tag: machine-2-lxc-1\\n password: ""\\n' > '/var/lib/juju/agents/machine-2-lxc-1/agent\.conf' |
2104 | +printf '%s\\n' 'oldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n addrs:\\n - state-addr\.testing\.invalid:12345\\n cacert:\\n[^']+ tag: machine-2-lxc-1\\n password: ""\\noldapipassword: ""\\napiinfo:\\n addrs:\\n - state-addr\.testing\.invalid:54321\\n cacert:\\n[^']+ tag: machine-2-lxc-1\\n password: ""\\n' > '/var/lib/juju/agents/machine-2-lxc-1/agent\.conf' |
2105 | ln -s 1\.2\.3-linux-amd64 '/var/lib/juju/tools/machine-2-lxc-1' |
2106 | cat >> /etc/init/jujud-machine-2-lxc-1\.conf << 'EOF'\\ndescription "juju machine-2-lxc-1 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\nenv JUJU_PROVIDER_TYPE="dummy"\\n\\nlimit nofile 20000 20000\\n\\nexec /var/lib/juju/tools/machine-2-lxc-1/jujud machine --log-file '/var/log/juju/machine-2-lxc-1\.log' --data-dir '/var/lib/juju' --machine-id 2/lxc/1 --debug >> /var/log/juju/machine-2-lxc-1\.log 2>&1\\nEOF\\n |
2107 | start jujud-machine-2-lxc-1 |
2108 | @@ -572,8 +572,9 @@ |
2109 | AuthorizedKeys: "sshkey1", |
2110 | MachineEnvironment: map[string]string{osenv.JujuProviderType: "dummy"}, |
2111 | StateInfo: &state.Info{ |
2112 | - Addrs: []string{"host:98765"}, |
2113 | - CACert: []byte(testing.CACert), |
2114 | + Addrs: []string{"host:98765"}, |
2115 | + CACert: []byte(testing.CACert), |
2116 | + Password: "password", |
2117 | }, |
2118 | APIInfo: &api.Info{ |
2119 | Addrs: []string{"host:9999"}, |
2120 | |
2121 | === modified file 'provider/azure/config_test.go' |
2122 | --- provider/azure/config_test.go 2013-08-09 05:36:08 +0000 |
2123 | +++ provider/azure/config_test.go 2013-08-22 23:04:24 +0000 |
2124 | @@ -13,7 +13,9 @@ |
2125 | "launchpad.net/juju-core/testing" |
2126 | ) |
2127 | |
2128 | -type configSuite struct{} |
2129 | +type configSuite struct { |
2130 | + testing.LoggingSuite |
2131 | +} |
2132 | |
2133 | var _ = gc.Suite(&configSuite{}) |
2134 | |
2135 | |
2136 | === modified file 'provider/azure/customdata_test.go' |
2137 | --- provider/azure/customdata_test.go 2013-08-01 04:34:27 +0000 |
2138 | +++ provider/azure/customdata_test.go 2013-08-22 23:04:24 +0000 |
2139 | @@ -17,7 +17,9 @@ |
2140 | "launchpad.net/juju-core/testing" |
2141 | ) |
2142 | |
2143 | -type customDataSuite struct{} |
2144 | +type customDataSuite struct { |
2145 | + testing.LoggingSuite |
2146 | +} |
2147 | |
2148 | var _ = gc.Suite(&customDataSuite{}) |
2149 | |
2150 | @@ -31,9 +33,10 @@ |
2151 | DataDir: dir, |
2152 | Tools: &tools.Tools{URL: "file://" + dir}, |
2153 | StateInfo: &state.Info{ |
2154 | - CACert: []byte(testing.CACert), |
2155 | - Addrs: []string{"127.0.0.1:123"}, |
2156 | - Tag: names.MachineTag(machineID), |
2157 | + CACert: []byte(testing.CACert), |
2158 | + Addrs: []string{"127.0.0.1:123"}, |
2159 | + Tag: names.MachineTag(machineID), |
2160 | + Password: "password", |
2161 | }, |
2162 | APIInfo: &api.Info{ |
2163 | CACert: []byte(testing.CACert), |
2164 | |
2165 | === modified file 'provider/ec2/config_test.go' |
2166 | --- provider/ec2/config_test.go 2013-08-19 11:19:07 +0000 |
2167 | +++ provider/ec2/config_test.go 2013-08-22 23:04:24 +0000 |
2168 | @@ -7,20 +7,23 @@ |
2169 | |
2170 | import ( |
2171 | "io/ioutil" |
2172 | + "os" |
2173 | + "path/filepath" |
2174 | + "strings" |
2175 | + |
2176 | "launchpad.net/goamz/aws" |
2177 | gc "launchpad.net/gocheck" |
2178 | "launchpad.net/goyaml" |
2179 | + |
2180 | "launchpad.net/juju-core/environs" |
2181 | "launchpad.net/juju-core/environs/config" |
2182 | "launchpad.net/juju-core/testing" |
2183 | - "os" |
2184 | - "path/filepath" |
2185 | - "strings" |
2186 | ) |
2187 | |
2188 | // Use local suite since this file lives in the ec2 package |
2189 | // for testing internals. |
2190 | type ConfigSuite struct { |
2191 | + testing.LoggingSuite |
2192 | savedHome, savedAccessKey, savedSecretKey string |
2193 | } |
2194 | |
2195 | @@ -301,6 +304,7 @@ |
2196 | } |
2197 | |
2198 | func (s *ConfigSuite) SetUpTest(c *gc.C) { |
2199 | + s.LoggingSuite.SetUpTest(c) |
2200 | s.savedHome = os.Getenv("HOME") |
2201 | s.savedAccessKey = os.Getenv("AWS_ACCESS_KEY_ID") |
2202 | s.savedSecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY") |
2203 | @@ -323,6 +327,7 @@ |
2204 | os.Setenv("AWS_ACCESS_KEY_ID", s.savedAccessKey) |
2205 | os.Setenv("AWS_SECRET_ACCESS_KEY", s.savedSecretKey) |
2206 | delete(aws.Regions, "configtest") |
2207 | + s.LoggingSuite.TearDownTest(c) |
2208 | } |
2209 | |
2210 | func (s *ConfigSuite) TestConfig(c *gc.C) { |
2211 | |
2212 | === modified file 'provider/ec2/local_test.go' |
2213 | --- provider/ec2/local_test.go 2013-08-22 21:27:52 +0000 |
2214 | +++ provider/ec2/local_test.go 2013-08-22 23:04:24 +0000 |
2215 | @@ -280,6 +280,7 @@ |
2216 | // provisioning agent. |
2217 | series := t.env.Config().DefaultSeries() |
2218 | info.Tag = "machine-1" |
2219 | + info.Password = "password" |
2220 | apiInfo.Tag = "machine-1" |
2221 | inst1, hc, err := t.env.StartInstance("1", "fake_nonce", series, constraints.Value{}, info, apiInfo) |
2222 | c.Assert(err, gc.IsNil) |
2223 | @@ -314,6 +315,7 @@ |
2224 | c.Assert(err, gc.IsNil) |
2225 | c.Assert(info, gc.NotNil) |
2226 | info.Tag = "machine-1" |
2227 | + info.Password = "password" |
2228 | apiInfo.Tag = "machine-1" |
2229 | t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated) |
2230 | inst, _, err := t.env.StartInstance("1", "fake_nonce", series, constraints.Value{}, info, apiInfo) |
2231 | @@ -329,6 +331,7 @@ |
2232 | c.Assert(err, gc.IsNil) |
2233 | c.Assert(info, gc.NotNil) |
2234 | info.Tag = "machine-1" |
2235 | + info.Password = "password" |
2236 | apiInfo.Tag = "machine-1" |
2237 | _, hc, err := t.env.StartInstance("1", "fake_nonce", series, constraints.MustParse("mem=1024"), info, apiInfo) |
2238 | c.Assert(err, gc.IsNil) |
2239 | @@ -346,6 +349,7 @@ |
2240 | c.Assert(err, gc.IsNil) |
2241 | c.Assert(info, gc.NotNil) |
2242 | info.Tag = "machine-1" |
2243 | + info.Password = "password" |
2244 | apiInfo.Tag = "machine-1" |
2245 | inst, _, err := t.env.StartInstance("1", "fake_nonce", series, constraints.Value{}, info, apiInfo) |
2246 | c.Assert(err, gc.IsNil) |
2247 | |
2248 | === modified file 'provider/local/environ.go' |
2249 | --- provider/local/environ.go 2013-08-13 04:55:12 +0000 |
2250 | +++ provider/local/environ.go 2013-08-22 23:04:24 +0000 |
2251 | @@ -125,13 +125,14 @@ |
2252 | // Need to write out the agent file for machine-0 before initializing |
2253 | // state, as as part of that process, it will reset the password in the |
2254 | // agent file. |
2255 | - if err := env.writeBootstrapAgentConfFile(cert, key); err != nil { |
2256 | + agentConfig, err := env.writeBootstrapAgentConfFile(env.config.AdminSecret(), cert, key) |
2257 | + if err != nil { |
2258 | return err |
2259 | } |
2260 | |
2261 | // Have to initialize the state configuration with localhost so we get |
2262 | // "special" permissions. |
2263 | - stateConnection, err := env.initialStateConfiguration(boostrapInstanceId, cons) |
2264 | + stateConnection, err := env.initialStateConfiguration(agentConfig, cons) |
2265 | if err != nil { |
2266 | return err |
2267 | } |
2268 | @@ -493,59 +494,57 @@ |
2269 | return getAddressForInterface(lxcBridgeName) |
2270 | } |
2271 | |
2272 | -func (env *localEnviron) writeBootstrapAgentConfFile(cert, key []byte) error { |
2273 | - info, apiInfo, err := env.StateInfo() |
2274 | - if err != nil { |
2275 | - logger.Errorf("failed to get state info to write bootstrap agent file: %v", err) |
2276 | - return err |
2277 | - } |
2278 | +func (env *localEnviron) writeBootstrapAgentConfFile(secret string, cert, key []byte) (agent.Config, error) { |
2279 | tag := names.MachineTag("0") |
2280 | - info.Tag = tag |
2281 | - apiInfo.Tag = tag |
2282 | - conf := &agent.Conf{ |
2283 | - DataDir: env.config.rootDir(), |
2284 | - StateInfo: info, |
2285 | - APIInfo: apiInfo, |
2286 | - StateServerCert: cert, |
2287 | - StateServerKey: key, |
2288 | - StatePort: env.config.StatePort(), |
2289 | - APIPort: env.config.APIPort(), |
2290 | - MachineNonce: state.BootstrapNonce, |
2291 | - } |
2292 | - if err := conf.Write(); err != nil { |
2293 | - logger.Errorf("failed to write bootstrap agent file: %v", err) |
2294 | - return err |
2295 | - } |
2296 | - return nil |
2297 | -} |
2298 | - |
2299 | -func (env *localEnviron) initialStateConfiguration(addr string, cons constraints.Value) (*state.State, error) { |
2300 | + passwordHash := utils.PasswordHash(secret) |
2301 | // We don't check the existance of the CACert here as if it wasn't set, we |
2302 | // wouldn't get this far. |
2303 | cfg := env.config.Config |
2304 | caCert, _ := cfg.CACert() |
2305 | - addr = fmt.Sprintf("%s:%d", addr, cfg.StatePort()) |
2306 | - info := &state.Info{ |
2307 | - Addrs: []string{addr}, |
2308 | - CACert: caCert, |
2309 | - } |
2310 | + // NOTE: the state address HAS to be localhost, otherwise the mongo |
2311 | + // initialization fails. There is some magic code somewhere in the mongo |
2312 | + // connection code that treats connections from localhost as special, and |
2313 | + // will raise unauthorized errors during the initialization if the caller |
2314 | + // is not connected from localhost. |
2315 | + stateAddress := fmt.Sprintf("localhost:%d", cfg.StatePort()) |
2316 | + apiAddress := fmt.Sprintf("localhost:%d", cfg.APIPort()) |
2317 | + config, err := agent.NewStateMachineConfig( |
2318 | + agent.StateMachineConfigParams{ |
2319 | + AgentConfigParams: agent.AgentConfigParams{ |
2320 | + DataDir: env.config.rootDir(), |
2321 | + Tag: tag, |
2322 | + Password: passwordHash, |
2323 | + Nonce: state.BootstrapNonce, |
2324 | + StateAddresses: []string{stateAddress}, |
2325 | + APIAddresses: []string{apiAddress}, |
2326 | + CACert: caCert, |
2327 | + }, |
2328 | + StateServerCert: cert, |
2329 | + StateServerKey: key, |
2330 | + StatePort: cfg.StatePort(), |
2331 | + APIPort: cfg.APIPort(), |
2332 | + }) |
2333 | + if err != nil { |
2334 | + return nil, err |
2335 | + } |
2336 | + if err := config.Write(); err != nil { |
2337 | + logger.Errorf("failed to write bootstrap agent file: %v", err) |
2338 | + return nil, err |
2339 | + } |
2340 | + return config, nil |
2341 | +} |
2342 | + |
2343 | +func (env *localEnviron) initialStateConfiguration(agentConfig agent.Config, cons constraints.Value) (*state.State, error) { |
2344 | timeout := state.DialOpts{60 * time.Second} |
2345 | - bootstrap, err := environs.BootstrapConfig(cfg) |
2346 | - if err != nil { |
2347 | - return nil, err |
2348 | - } |
2349 | - st, err := state.Initialize(info, bootstrap, timeout) |
2350 | - if err != nil { |
2351 | - logger.Errorf("failed to initialize state: %v", err) |
2352 | - return nil, err |
2353 | - } |
2354 | - logger.Debugf("state initialized") |
2355 | + bootstrap, err := environs.BootstrapConfig(env.config.Config) |
2356 | + if err != nil { |
2357 | + return nil, err |
2358 | + } |
2359 | + st, err := agent.InitialStateConfiguration(agentConfig, bootstrap, timeout) |
2360 | + if err != nil { |
2361 | + return nil, err |
2362 | + } |
2363 | |
2364 | - passwordHash := utils.PasswordHash(cfg.AdminSecret()) |
2365 | - if err := environs.BootstrapUsers(st, cfg, passwordHash); err != nil { |
2366 | - st.Close() |
2367 | - return nil, err |
2368 | - } |
2369 | jobs := []state.MachineJob{state.JobManageEnviron, state.JobManageState} |
2370 | |
2371 | if err := environs.ConfigureBootstrapMachine( |
2372 | |
2373 | === modified file 'provider/maas/config_test.go' |
2374 | --- provider/maas/config_test.go 2013-08-09 04:58:34 +0000 |
2375 | +++ provider/maas/config_test.go 2013-08-22 23:04:24 +0000 |
2376 | @@ -11,7 +11,9 @@ |
2377 | "launchpad.net/juju-core/version" |
2378 | ) |
2379 | |
2380 | -type configSuite struct{} |
2381 | +type configSuite struct { |
2382 | + testing.LoggingSuite |
2383 | +} |
2384 | |
2385 | var _ = gc.Suite(&configSuite{}) |
2386 | |
2387 | |
2388 | === modified file 'provider/maas/environ_test.go' |
2389 | --- provider/maas/environ_test.go 2013-08-15 17:49:53 +0000 |
2390 | +++ provider/maas/environ_test.go 2013-08-22 23:04:24 +0000 |
2391 | @@ -257,6 +257,7 @@ |
2392 | stateInfo, apiInfo, err := env.StateInfo() |
2393 | c.Assert(err, gc.IsNil) |
2394 | stateInfo.Tag = "machine-1" |
2395 | + stateInfo.Password = "password" |
2396 | apiInfo.Tag = "machine-1" |
2397 | series := version.Current.Series |
2398 | nonce := "12345" |
2399 | |
2400 | === modified file 'provider/openstack/config_test.go' |
2401 | --- provider/openstack/config_test.go 2013-07-23 05:02:37 +0000 |
2402 | +++ provider/openstack/config_test.go 2013-08-22 23:04:24 +0000 |
2403 | @@ -19,6 +19,7 @@ |
2404 | ) |
2405 | |
2406 | type ConfigSuite struct { |
2407 | + testing.LoggingSuite |
2408 | savedVars map[string]string |
2409 | oldJujuHome *testing.FakeHome |
2410 | } |
2411 | @@ -178,6 +179,7 @@ |
2412 | } |
2413 | |
2414 | func (s *ConfigSuite) SetUpTest(c *gc.C) { |
2415 | + s.LoggingSuite.SetUpTest(c) |
2416 | s.oldJujuHome = testing.MakeEmptyFakeHome(c) |
2417 | s.savedVars = make(map[string]string) |
2418 | for v, val := range envVars { |
2419 | @@ -191,6 +193,7 @@ |
2420 | os.Setenv(k, v) |
2421 | } |
2422 | s.oldJujuHome.Restore() |
2423 | + s.LoggingSuite.TearDownTest(c) |
2424 | } |
2425 | |
2426 | var configTests = []configTest{ |
2427 | @@ -469,17 +472,13 @@ |
2428 | func (s *ConfigDeprecationSuite) setupLogger(c *gc.C) { |
2429 | var err error |
2430 | s.writer = &testWriter{} |
2431 | - s.oldWriter, s.oldLevel, err = loggo.RemoveWriter("default") |
2432 | - c.Assert(err, gc.IsNil) |
2433 | - err = loggo.RegisterWriter("test", s.writer, loggo.TRACE) |
2434 | + err = loggo.RegisterWriter("test", s.writer, loggo.WARNING) |
2435 | c.Assert(err, gc.IsNil) |
2436 | } |
2437 | |
2438 | func (s *ConfigDeprecationSuite) resetLogger(c *gc.C) { |
2439 | _, _, err := loggo.RemoveWriter("test") |
2440 | c.Assert(err, gc.IsNil) |
2441 | - err = loggo.RegisterWriter("default", s.oldWriter, s.oldLevel) |
2442 | - c.Assert(err, gc.IsNil) |
2443 | } |
2444 | |
2445 | type testWriter struct { |
2446 | |
2447 | === modified file 'provider/openstack/local_test.go' |
2448 | --- provider/openstack/local_test.go 2013-08-21 01:26:36 +0000 |
2449 | +++ provider/openstack/local_test.go 2013-08-22 23:04:24 +0000 |
2450 | @@ -460,6 +460,7 @@ |
2451 | // and without a provisioning agent. |
2452 | series := s.env.Config().DefaultSeries() |
2453 | info.Tag = "machine-1" |
2454 | + info.Password = "password" |
2455 | apiInfo.Tag = "machine-1" |
2456 | inst1, _, err := s.env.StartInstance("1", "fake_nonce", series, constraints.Value{}, info, apiInfo) |
2457 | c.Assert(err, gc.IsNil) |
2458 | |
2459 | === modified file 'state/open.go' |
2460 | --- state/open.go 2013-08-06 21:02:51 +0000 |
2461 | +++ state/open.go 2013-08-22 23:04:24 +0000 |
2462 | @@ -19,7 +19,6 @@ |
2463 | "launchpad.net/juju-core/constraints" |
2464 | "launchpad.net/juju-core/environs/config" |
2465 | "launchpad.net/juju-core/errors" |
2466 | - "launchpad.net/juju-core/log" |
2467 | "launchpad.net/juju-core/state/presence" |
2468 | "launchpad.net/juju-core/state/watcher" |
2469 | "launchpad.net/juju-core/utils" |
2470 | @@ -66,7 +65,7 @@ |
2471 | // representing the environment connected to. |
2472 | // It returns unauthorizedError if access is unauthorized. |
2473 | func Open(info *Info, opts DialOpts) (*State, error) { |
2474 | - log.Infof("state: opening state; mongo addresses: %q; entity %q", info.Addrs, info.Tag) |
2475 | + logger.Infof("opening state; mongo addresses: %q; entity %q", info.Addrs, info.Tag) |
2476 | if len(info.Addrs) == 0 { |
2477 | return nil, stderrors.New("no mongo addresses") |
2478 | } |
2479 | @@ -86,12 +85,12 @@ |
2480 | dial := func(addr net.Addr) (net.Conn, error) { |
2481 | c, err := net.Dial("tcp", addr.String()) |
2482 | if err != nil { |
2483 | - log.Errorf("state: connection failed, will retry: %v", err) |
2484 | + logger.Debugf("connection failed, will retry: %v", err) |
2485 | return nil, err |
2486 | } |
2487 | cc := tls.Client(c, tlsConfig) |
2488 | if err := cc.Handshake(); err != nil { |
2489 | - log.Errorf("state: TLS handshake failed: %v", err) |
2490 | + logger.Errorf("TLS handshake failed: %v", err) |
2491 | return nil, err |
2492 | } |
2493 | return cc, nil |
2494 | @@ -104,7 +103,7 @@ |
2495 | if err != nil { |
2496 | return nil, err |
2497 | } |
2498 | - log.Infof("state: connection established") |
2499 | + logger.Infof("connection established") |
2500 | st, err := newState(session, info) |
2501 | if err != nil { |
2502 | session.Close() |
2503 | @@ -134,7 +133,7 @@ |
2504 | } else if !errors.IsNotFoundError(err) { |
2505 | return nil, err |
2506 | } |
2507 | - log.Infof("state: initializing environment") |
2508 | + logger.Infof("initializing environment") |
2509 | if err := checkEnvironConfig(cfg); err != nil { |
2510 | return nil, err |
2511 | } |
2512 | |
2513 | === modified file 'state/state.go' |
2514 | --- state/state.go 2013-08-21 09:27:41 +0000 |
2515 | +++ state/state.go 2013-08-22 23:04:24 +0000 |
2516 | @@ -17,13 +17,13 @@ |
2517 | "labix.org/v2/mgo" |
2518 | "labix.org/v2/mgo/bson" |
2519 | "labix.org/v2/mgo/txn" |
2520 | + "launchpad.net/loggo" |
2521 | |
2522 | "launchpad.net/juju-core/charm" |
2523 | "launchpad.net/juju-core/constraints" |
2524 | "launchpad.net/juju-core/environs/config" |
2525 | "launchpad.net/juju-core/errors" |
2526 | "launchpad.net/juju-core/instance" |
2527 | - "launchpad.net/juju-core/log" |
2528 | "launchpad.net/juju-core/names" |
2529 | "launchpad.net/juju-core/state/api/params" |
2530 | "launchpad.net/juju-core/state/multiwatcher" |
2531 | @@ -32,6 +32,8 @@ |
2532 | "launchpad.net/juju-core/utils" |
2533 | ) |
2534 | |
2535 | +var logger = loggo.GetLogger("juju.state") |
2536 | + |
2537 | // TODO(niemeyer): This must not be exported. |
2538 | type D []bson.DocElem |
2539 | |
2540 | @@ -1122,7 +1124,7 @@ |
2541 | err = fmt.Errorf("unknown cleanup kind %q", doc.Kind) |
2542 | } |
2543 | if err != nil { |
2544 | - log.Warningf("cleanup failed: %v", err) |
2545 | + logger.Warningf("cleanup failed: %v", err) |
2546 | continue |
2547 | } |
2548 | ops := []txn.Op{{ |
2549 | |
2550 | === modified file 'testing/mgo.go' |
2551 | --- testing/mgo.go 2013-07-31 08:17:33 +0000 |
2552 | +++ testing/mgo.go 2013-08-22 23:04:24 +0000 |
2553 | @@ -32,6 +32,9 @@ |
2554 | // MgoTestPackage. |
2555 | MgoAddr string |
2556 | |
2557 | + // MgoPort holds the port used by the shared MongoDB server. |
2558 | + MgoPort int |
2559 | + |
2560 | // mgoServer holds the running MongoDB command. |
2561 | mgoServer *exec.Cmd |
2562 | |
2563 | @@ -65,7 +68,8 @@ |
2564 | if err != nil { |
2565 | return fmt.Errorf("cannot write cert/key PEM: %v", err) |
2566 | } |
2567 | - mgoport := strconv.Itoa(FindTCPPort()) |
2568 | + MgoPort = FindTCPPort() |
2569 | + mgoport := strconv.Itoa(MgoPort) |
2570 | mgoargs := []string{ |
2571 | "--auth", |
2572 | "--dbpath", dbdir, |
2573 | |
2574 | === modified file 'worker/deployer/simple.go' |
2575 | --- worker/deployer/simple.go 2013-07-31 21:44:45 +0000 |
2576 | +++ worker/deployer/simple.go 2013-08-22 23:04:24 +0000 |
2577 | @@ -16,8 +16,6 @@ |
2578 | "launchpad.net/juju-core/juju/osenv" |
2579 | "launchpad.net/juju-core/log/syslog" |
2580 | "launchpad.net/juju-core/names" |
2581 | - "launchpad.net/juju-core/state" // Only because of state.Info |
2582 | - "launchpad.net/juju-core/state/api" |
2583 | "launchpad.net/juju-core/upstart" |
2584 | "launchpad.net/juju-core/version" |
2585 | ) |
2586 | @@ -89,33 +87,29 @@ |
2587 | defer removeOnErr(&err, toolsDir) |
2588 | |
2589 | // Retrieve the state addresses. |
2590 | + // TODO: remove the state addresses when unit agent is API only. |
2591 | stateAddrs, err := ctx.addresser.StateAddresses() |
2592 | if err != nil { |
2593 | return err |
2594 | } |
2595 | + logger.Debugf("state addresses: %q", stateAddrs) |
2596 | apiAddrs, err := ctx.addresser.APIAddresses() |
2597 | if err != nil { |
2598 | return err |
2599 | } |
2600 | - |
2601 | - stateInfo := state.Info{ |
2602 | - Addrs: stateAddrs, |
2603 | - Tag: tag, |
2604 | - CACert: ctx.caCert, |
2605 | - } |
2606 | - logger.Debugf("state addresses: %q", stateAddrs) |
2607 | - apiInfo := api.Info{ |
2608 | - Addrs: apiAddrs, |
2609 | - Tag: tag, |
2610 | - CACert: ctx.caCert, |
2611 | - } |
2612 | logger.Debugf("API addresses: %q", apiAddrs) |
2613 | - // Prepare the agent's configuration data. |
2614 | - conf := &agent.Conf{ |
2615 | - DataDir: ctx.dataDir, |
2616 | - OldPassword: initialPassword, |
2617 | - StateInfo: &stateInfo, |
2618 | - APIInfo: &apiInfo, |
2619 | + conf, err := agent.NewAgentConfig( |
2620 | + agent.AgentConfigParams{ |
2621 | + DataDir: ctx.dataDir, |
2622 | + Tag: tag, |
2623 | + Password: initialPassword, |
2624 | + Nonce: "unused", |
2625 | + StateAddresses: stateAddrs, |
2626 | + APIAddresses: apiAddrs, |
2627 | + CACert: ctx.caCert, |
2628 | + }) |
2629 | + if err != nil { |
2630 | + return err |
2631 | } |
2632 | if err := conf.Write(); err != nil { |
2633 | return err |
2634 | @@ -138,7 +132,7 @@ |
2635 | |
2636 | cmd := strings.Join([]string{ |
2637 | path.Join(toolsDir, "jujud"), "unit", |
2638 | - "--data-dir", conf.DataDir, |
2639 | + "--data-dir", conf.DataDir(), |
2640 | "--unit-name", unitName, |
2641 | "--debug", // TODO: propagate debug state sensibly |
2642 | }, " ") |
2643 | |
2644 | === modified file 'worker/deployer/simple_test.go' |
2645 | --- worker/deployer/simple_test.go 2013-08-19 11:20:02 +0000 |
2646 | +++ worker/deployer/simple_test.go 2013-08-22 23:04:24 +0000 |
2647 | @@ -17,8 +17,7 @@ |
2648 | "launchpad.net/juju-core/agent" |
2649 | "launchpad.net/juju-core/agent/tools" |
2650 | "launchpad.net/juju-core/names" |
2651 | - "launchpad.net/juju-core/state" |
2652 | - "launchpad.net/juju-core/state/api" |
2653 | + "launchpad.net/juju-core/testing" |
2654 | "launchpad.net/juju-core/testing/checkers" |
2655 | "launchpad.net/juju-core/version" |
2656 | "launchpad.net/juju-core/worker/deployer" |
2657 | @@ -130,6 +129,7 @@ |
2658 | } |
2659 | |
2660 | type SimpleToolsFixture struct { |
2661 | + testing.LoggingSuite |
2662 | dataDir string |
2663 | initDir string |
2664 | logDir string |
2665 | @@ -141,6 +141,7 @@ |
2666 | var fakeJujud = "#!/bin/bash\n# fake-jujud\nexit 0\n" |
2667 | |
2668 | func (fix *SimpleToolsFixture) SetUp(c *gc.C, dataDir string) { |
2669 | + fix.LoggingSuite.SetUpTest(c) |
2670 | fix.dataDir = dataDir |
2671 | fix.initDir = c.MkDir() |
2672 | fix.logDir = c.MkDir() |
2673 | @@ -166,6 +167,7 @@ |
2674 | |
2675 | func (fix *SimpleToolsFixture) TearDown(c *gc.C) { |
2676 | os.Setenv("PATH", fix.origPath) |
2677 | + fix.LoggingSuite.TearDownTest(c) |
2678 | } |
2679 | |
2680 | func (fix *SimpleToolsFixture) makeBin(c *gc.C, name, script string) { |
2681 | @@ -240,20 +242,8 @@ |
2682 | |
2683 | conf, err := agent.ReadConf(fix.dataDir, tag) |
2684 | c.Assert(err, gc.IsNil) |
2685 | - c.Assert(conf, gc.DeepEquals, &agent.Conf{ |
2686 | - DataDir: fix.dataDir, |
2687 | - OldPassword: password, |
2688 | - StateInfo: &state.Info{ |
2689 | - Addrs: []string{"s1:123", "s2:123"}, |
2690 | - CACert: []byte("test-cert"), |
2691 | - Tag: tag, |
2692 | - }, |
2693 | - APIInfo: &api.Info{ |
2694 | - Addrs: []string{"a1:123", "a2:123"}, |
2695 | - CACert: []byte("test-cert"), |
2696 | - Tag: tag, |
2697 | - }, |
2698 | - }) |
2699 | + c.Assert(conf.Tag(), gc.Equals, tag) |
2700 | + c.Assert(conf.DataDir(), gc.Equals, fix.dataDir) |
2701 | |
2702 | jujudData, err := ioutil.ReadFile(jujudPath) |
2703 | c.Assert(err, gc.IsNil) |
Reviewers: mp+181450_ code.launchpad. net,
Message:
Please take a look.
Description:
Replace agent.Conf with an interface
This was supposed to be smallish, but turned epic.
I also unexported the Conf structure so construction
now has to be through one of the two New* methods or
the Read method.
A lot of the changes are making the call sites work
with the interface instead of directly poking at the
structure.
Quite a few of the tests in agent were testing for
situations that can no longer occur. Those have
been removed.
https:/ /code.launchpad .net/~thumper/ juju-core/ agent-conf- interface/ +merge/ 181450
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/13164043/
Affected files: agent_test. go bootstrap. go bootstrap_ test.go deploy_ test.go machine. go machine_ test.go unit_test. go bootstrap. go cloudinit/ cloudinit. go cloudinit/ cloudinit_ test.go azure/config_ test.go azure/customdat a_test. go ec2/local_ test.go local/environ. go maas/config_ test.go maas/environ_ test.go openstack/ config_ test.go openstack/ local_test. go deployer/ simple. go deployer/ simple_ test.go
A [revision details]
M agent/agent.go
M agent/agent_test.go
M cmd/jujud/agent.go
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/
M cmd/jujud/unit.go
M cmd/jujud/
M environs/
M environs/
M environs/
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
M provider/
M testing/mgo.go
M worker/
M worker/