Merge lp:~thumper/juju-core/agent-conf-interface into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
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
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+181450@code.launchpad.net

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.

https://codereview.appspot.com/13164043/

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.

https://codereview.appspot.com/13164043/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

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:
   A [revision details]
   M agent/agent.go
   M agent/agent_test.go
   M cmd/jujud/agent.go
   M cmd/jujud/agent_test.go
   M cmd/jujud/bootstrap.go
   M cmd/jujud/bootstrap_test.go
   M cmd/jujud/deploy_test.go
   M cmd/jujud/machine.go
   M cmd/jujud/machine_test.go
   M cmd/jujud/unit.go
   M cmd/jujud/unit_test.go
   M environs/bootstrap.go
   M environs/cloudinit/cloudinit.go
   M environs/cloudinit/cloudinit_test.go
   M provider/azure/config_test.go
   M provider/azure/customdata_test.go
   M provider/ec2/local_test.go
   M provider/local/environ.go
   M provider/maas/config_test.go
   M provider/maas/environ_test.go
   M provider/openstack/config_test.go
   M provider/openstack/local_test.go
   M testing/mgo.go
   M worker/deployer/simple.go
   M worker/deployer/simple_test.go

Revision history for this message
William Reade (fwereade) wrote :

LGTM, this is awesome. Just a few trivial notes:

https://codereview.appspot.com/13164043/diff/1/agent/agent_test.go
File agent/agent_test.go (left):

https://codereview.appspot.com/13164043/diff/1/agent/agent_test.go#oldcode25
agent/agent_test.go:25: var _ = gc.Suite(suite{})
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://codereview.appspot.com/13164043/diff/1/agent/agent_test.go
File agent/agent_test.go (right):

https://codereview.appspot.com/13164043/diff/1/agent/agent_test.go#newcode198
agent/agent_test.go:198: // method.
Nice.

https://codereview.appspot.com/13164043/diff/1/cmd/jujud/machine_test.go
File cmd/jujud/machine_test.go (right):

https://codereview.appspot.com/13164043/diff/1/cmd/jujud/machine_test.go#newcode75
cmd/jujud/machine_test.go:75: err =
m.SetPassword(initialMachinePassword)
Doesn't SetPassword also SetMongoPassword? (might well not, just thought
it did)

https://codereview.appspot.com/13164043/diff/1/cmd/jujud/unit_test.go
File cmd/jujud/unit_test.go (right):

https://codereview.appspot.com/13164043/diff/1/cmd/jujud/unit_test.go#newcode47
cmd/jujud/unit_test.go:47: err = unit.SetPassword(initialUnitPassword)
Ditto.

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

https://codereview.appspot.com/13164043/diff/1/environs/cloudinit/cloudinit.go#newcode249
environs/cloudinit/cloudinit.go:249: Password: password,
FWIW this is, I think, a bootstrap compatibility break (until we have
ian's pick-matching-bootstrap-tools fix in). Please confer with him and
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://codereview.appspot.com/13164043/diff/1/environs/cloudinit/cloudinit.go#newcode254
environs/cloudinit/cloudinit.go:254: },
This looks identical to...

https://codereview.appspot.com/13164043/diff/1/environs/cloudinit/cloudinit.go#newcode270
environs/cloudinit/cloudinit.go:270: })
...this. Would be nice to pull out into a var.

https://codereview.appspot.com/13164043/diff/1/provider/local/environ.go
File provider/local/environ.go (right):

https://codereview.appspot.com/13164043/diff/1/provider/local/environ.go#newcode513
provider/local/environ.go:513: CACert: info.CACert,
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://codereview.appspot.com/13164043/diff/1/worker/deployer/simple.go
File worker/deployer/simple.go (right):

https://codereview.appspot.com/13164043/diff/1/worker/deployer/simple.go#newcode106
worker/deployer/simple.go:106: Nonce: "unused",
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"?

https://codereview.appspot.com/13164043/

Revision history for this message
John A Meinel (jameinel) wrote :

Some thoughts I wanted to make sure to get out since William did the
review.

https://codereview.appspot.com/13164043/diff/1/agent/agent.go
File agent/agent.go (left):

https://codereview.appspot.com/13164043/diff/1/agent/agent.go#oldcode59
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://codereview.appspot.com/13164043/diff/1/agent/agent.go
File agent/agent.go (right):

https://codereview.appspot.com/13164043/diff/1/agent/agent.go#newcode42
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://codereview.appspot.com/13164043/diff/1/agent/agent.go#newcode171
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.

https://codereview.appspot.com/13164043/

Revision history for this message
Tim Penhey (thumper) wrote :
Download full text (5.8 KiB)

https://codereview.appspot.com/13164043/diff/1/agent/agent.go
File agent/agent.go (left):

https://codereview.appspot.com/13164043/diff/1/agent/agent.go#oldcode59
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://codereview.appspot.com/13164043/diff/1/agent/agent.go
File agent/agent.go (right):

https://codereview.appspot.com/13164043/diff/1/agent/agent.go#newcode42
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://codereview.appspot.com/13164043/diff/1/agent/agent.go#newcode171
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://codereview.appspot.com/13164043/diff/1/agent/agent_test.go
File agent/agent_test.go (left):

https://codereview.appspot.com/13164043/diff/1/agent/agent_test.go#oldcode25
agent/agent_test.go:25: var _ = gc.Suite(suite{})
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://codereview.appspot.com/13164043/diff/1/cmd/jujud/machine_test.go
File cmd/jujud/machine_test.go (right):

https://codereview.appspot.com/13164043/diff/1/cmd/jujud/machine_test.go#newcode75
cmd/jujud/machine_test.go:75: err =
m.SetPassword(initialMachinePassword)
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.

https://coder...

Read more...

Revision history for this message
Tim Penhey (thumper) wrote :
Revision history for this message
Roger Peppe (rogpeppe) wrote :
Download full text (4.9 KiB)

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://codereview.appspot.com/13164043/diff/12001/agent/agent.go
File agent/agent.go (right):

https://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode27
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://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode29
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://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode50
agent/agent.go:50: // the given Conf.
What given Conf?

https://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode53
agent/agent.go:53: // Write writes the agent configuration.
... to the agent's directory. ?

https://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode61
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://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode69
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://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode74
agent/agent.go:74: type conf struct {
"config" might be a better name here.

https://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode114
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://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode138
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://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode175
agent/agent.go:175: type StateMachineConfigParams struct {
Doc comment please.

https://codereview.appspot.com/13164043/diff/12001/agent/agent.go#newcode434
agent/agent.go:434: func InitialStateConfiguration(...

Read more...

Preview Diff

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

Subscribers

People subscribed via source and target branches

to status/vote changes: