Merge lp:~mattyw/juju-core/user-add-remove-cli into lp:~go-bot/juju-core/trunk

Proposed by Matthew Williams
Status: Merged
Approved by: Matthew Williams
Approved revision: no longer in the source branch.
Merged at revision: 2527
Proposed branch: lp:~mattyw/juju-core/user-add-remove-cli
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 2254 lines (+646/-128)
63 files modified
cmd/cmd.go (+7/-7)
cmd/envcmd/environmentcommand.go (+15/-2)
cmd/envcmd/environmentcommand_test.go (+21/-18)
cmd/envcmd/export_test.go (+1/-1)
cmd/juju/addmachine.go (+6/-1)
cmd/juju/addrelation.go (+6/-1)
cmd/juju/addunit.go (+2/-1)
cmd/juju/adduser.go (+103/-0)
cmd/juju/adduser_test.go (+106/-0)
cmd/juju/authorisedkeys_add.go (+6/-1)
cmd/juju/authorisedkeys_delete.go (+6/-1)
cmd/juju/authorisedkeys_import.go (+6/-1)
cmd/juju/authorisedkeys_list.go (+10/-1)
cmd/juju/bootstrap.go (+6/-1)
cmd/juju/bootstrap_test.go (+2/-5)
cmd/juju/cmd_test.go (+2/-1)
cmd/juju/constraints.go (+11/-2)
cmd/juju/debuglog_test.go (+2/-2)
cmd/juju/deploy.go (+6/-1)
cmd/juju/destroymachine.go (+6/-1)
cmd/juju/destroyrelation.go (+6/-1)
cmd/juju/destroyservice.go (+6/-1)
cmd/juju/destroyunit.go (+6/-1)
cmd/juju/endpoint.go (+6/-1)
cmd/juju/ensureavailability.go (+6/-1)
cmd/juju/environment.go (+16/-3)
cmd/juju/expose.go (+6/-1)
cmd/juju/get.go (+6/-1)
cmd/juju/plugin.go (+3/-2)
cmd/juju/publish.go (+6/-1)
cmd/juju/publish_test.go (+3/-5)
cmd/juju/removeuser.go (+55/-0)
cmd/juju/removeuser_test.go (+35/-0)
cmd/juju/resolved.go (+6/-1)
cmd/juju/retryprovisioning.go (+2/-1)
cmd/juju/run.go (+6/-1)
cmd/juju/run_test.go (+1/-1)
cmd/juju/set.go (+6/-1)
cmd/juju/ssh.go (+6/-1)
cmd/juju/status.go (+3/-2)
cmd/juju/switch.go (+3/-2)
cmd/juju/switch_test.go (+2/-2)
cmd/juju/synctools.go (+6/-1)
cmd/juju/unexpose.go (+6/-1)
cmd/juju/unset.go (+6/-1)
cmd/juju/upgradecharm.go (+6/-1)
cmd/juju/upgradejuju.go (+6/-1)
cmd/jujud/run_test.go (+3/-3)
cmd/plugins/juju-metadata/imagemetadata.go (+2/-1)
cmd/plugins/juju-metadata/toolsmetadata.go (+10/-1)
cmd/plugins/juju-metadata/validateimagemetadata.go (+7/-2)
cmd/plugins/juju-metadata/validatetoolsmetadata.go (+7/-2)
cmd/plugins/juju-restore/restore.go (+6/-1)
cmd/supercommand.go (+0/-1)
environs/configstore/disk.go (+23/-18)
environs/configstore/mem.go (+2/-2)
environs/manual/init_test.go (+3/-3)
environs/manual/provisioner_test.go (+2/-2)
juju/api.go (+9/-0)
state/api/params/params.go (+6/-0)
state/apiserver/client/api_test.go (+1/-1)
state/user_test.go (+6/-6)
testing/environ.go (+4/-2)
To merge this branch: bzr merge lp:~mattyw/juju-core/user-add-remove-cli
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+210962@code.launchpad.net

Commit message

https://codereview.appspot.com/75600044/

Preliminary support for basic juju id commands:

juju add-user <username> <password>

if password is not supplied a strong one is generated.
The resulting jenv file is output (or saved to a file if -o option is provided)

juju remove-user <username>

doesn't actually remove the user. But mark them as inactive

add-user and remove-user aren't registered yet as they are useless without
login. But the details of this are still being worked out.

This branch also contains fixes for lp:1285256.
The EnvCommandBase struct includes a function for ensuring the envname is not ""

Description of the change

Preliminary support for basic juju id commands:

juju add-user <username> <password>

if password is not supplied uses gopass to take the password from the command line

juju remove-user <username>

doesn't actually remove the user. But mark them as inactive

add-user and remove-user aren't registered yet as they are useless without
login. But the details of this are still being worked out.

This branch also contains fixes for lp:1285256.
The EnvCommandBase struct includes a function for ensuring the envname is not "".

This replaces a review at https://codereview.appspot.com/51450047/

https://codereview.appspot.com/75600044/

To post a comment you must log in.
Revision history for this message
Matthew Williams (mattyw) wrote :

Reviewers: mp+210962_code.launchpad.net,

Message:
Please take a look.

Description:
Preliminary support for basic juju id commands:

juju add-user <username> <password>

if password is not supplied uses gopass to take the password from the
command line

juju remove-user <username>

doesn't actually remove the user. But mark them as inactive

add-user and remove-user aren't registered yet as they are useless
without
login. But the details of this are still being worked out.

This branch also contains fixes for lp:1285256.
The EnvCommandBase struct includes a function for ensuring the envname
is not "".

This replaces a review at https://codereview.appspot.com/51450047/

https://code.launchpad.net/~mattyw/juju-core/user-add-remove-cli/+merge/210962

(do not edit description out of merge proposal)

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

Affected files (+497, -109 lines):
   A [revision details]
   M cmd/cmd.go
   M cmd/envcmd/environmentcommand.go
   M cmd/envcmd/environmentcommand_test.go
   M cmd/envcmd/export_test.go
   M cmd/juju/addmachine.go
   M cmd/juju/addrelation.go
   M cmd/juju/addunit.go
   A cmd/juju/adduser.go
   A cmd/juju/adduser_test.go
   M cmd/juju/authorisedkeys_add.go
   M cmd/juju/authorisedkeys_delete.go
   M cmd/juju/authorisedkeys_import.go
   M cmd/juju/authorisedkeys_list.go
   M cmd/juju/bootstrap.go
   M cmd/juju/cmd_test.go
   M cmd/juju/constraints.go
   M cmd/juju/debuglog_test.go
   M cmd/juju/deploy.go
   M cmd/juju/destroymachine.go
   M cmd/juju/destroyrelation.go
   M cmd/juju/destroyservice.go
   M cmd/juju/destroyunit.go
   M cmd/juju/endpoint.go
   M cmd/juju/environment.go
   M cmd/juju/expose.go
   M cmd/juju/get.go
   M cmd/juju/plugin.go
   M cmd/juju/publish.go
   M cmd/juju/publish_test.go
   A cmd/juju/removeuser.go
   A cmd/juju/removeuser_test.go
   M cmd/juju/resolved.go
   M cmd/juju/run.go
   M cmd/juju/set.go
   M cmd/juju/ssh.go
   M cmd/juju/status.go
   M cmd/juju/switch.go
   M cmd/juju/switch_test.go
   M cmd/juju/synctools.go
   M cmd/juju/unexpose.go
   M cmd/juju/unset.go
   M cmd/juju/upgradecharm.go
   M cmd/juju/upgradejuju.go
   M cmd/jujud/run.go
   M cmd/jujud/run_test.go
   M cmd/package_test.go
   M cmd/plugins/juju-metadata/imagemetadata.go
   M cmd/plugins/juju-metadata/toolsmetadata.go
   M cmd/plugins/juju-metadata/validateimagemetadata.go
   M cmd/plugins/juju-metadata/validatetoolsmetadata.go
   M cmd/plugins/juju-restore/restore.go
   M cmd/supercommand.go
   M errors/errors.go
   M juju/api.go
   M juju/osenv/vars_windows_test.go
   M state/api/params/params.go
   M state/apiserver/client/api_test.go
   M state/user.go
   M state/user_test.go
   M testing/environ.go
   M utils/ssh/ssh.go
   M utils/ssh/ssh_test.go

Revision history for this message
Matthew Williams (mattyw) wrote :

On 2014/03/14 05:17:51, mattyw wrote:
> Please take a look.

This follows on from the review here:
https://codereview.appspot.com/51450047/

https://codereview.appspot.com/75600044/

Revision history for this message
William Reade (fwereade) wrote :
Download full text (4.7 KiB)

Sorry, I don't think we're quite done here yet.

I am very happy with the EnvCommandBase stuff, though, so I'd swiftly
approve a CL that just landed those changes while we argue about the
UI/UX for user management.

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/addmachine.go
File cmd/juju/addmachine.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/addmachine.go#newcode60
cmd/juju/addmachine.go:60: envcmd.EnvCommandBase
This has been scratching at the back of my mind for a while... "Base"
isn't quite the right term. "EnvCommand" -- or, hey, why not
"EnvironmentCommand"? -- might actually be a bit clearer, but then it's
not really itself a *command* in itself. Bah.

Nothing necessarily actionable here because I can't actually suggest
anything better, but I'd love it if we did come up with a better name.

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser.go#newcode59
cmd/juju/adduser.go:59: c.Password = args[1]
I'm still not really in love with allowing people to specify passwords
(especially in the absence of resetting post-distribution).

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser.go#newcode70
cmd/juju/adduser.go:70: return client.AddUser(c.User, c.Password)
Any possibility we could design this to output a .jenv (*without* all
the bootstrap-config stuff) on stdout? I think that the
pass-jenvs-around model is pretty tolerable, really.

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser_test.go
File cmd/juju/adduser_test.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser_test.go#newcode21
cmd/juju/adduser_test.go:21: func (s *AddUserSuite) Testadduser(c *gc.C)
{
TestAddUser, please

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser_test.go#newcode27
cmd/juju/adduser_test.go:27: c.Assert(err, gc.ErrorMatches, "Failed to
create user: user already exists")
we don't seem to have tests for the automatic password generation...

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/removeuser_test.go
File cmd/juju/removeuser_test.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/removeuser_test.go#newcode19
cmd/juju/removeuser_test.go:19: func (s *RemoveUserSuite)
Testremoveuser(c *gc.C) {
TestRemoveUser

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/removeuser_test.go#newcode24
cmd/juju/removeuser_test.go:24: c.Assert(err, gc.IsNil)
no tests for CheckEmpty etc (same applies to AddUser as well fwiw)

https://codereview.appspot.com/75600044/diff/20001/cmd/plugins/juju-metadata/validatetoolsmetadata.go
File cmd/plugins/juju-metadata/validatetoolsmetadata.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/plugins/juju-metadata/validatetoolsmetadata.go#newcode24
cmd/plugins/juju-metadata/validatetoolsmetadata.go:24:
envcmd.EnvCommandBase
Hmm. Shouldn't these be using Ensure..?

https://codereview.appspot.com/75600044/diff/20001/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/75600044/diff/20001/errors/errors.go#newcod...

Read more...

Revision history for this message
Matthew Williams (mattyw) wrote :

Updated the comments, stuff I haven't commented on is stuff I will do

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser.go#newcode59
cmd/juju/adduser.go:59: c.Password = args[1]
On 2014/03/18 01:11:39, fwereade wrote:
> I'm still not really in love with allowing people to specify passwords
> (especially in the absence of resetting post-distribution).

I'm not a huge fan of this either, it was added for two reasons:
1) Makes testing easier
2) Allows people to "script" adding users

Suggestions welcome

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser.go#newcode70
cmd/juju/adduser.go:70: return client.AddUser(c.User, c.Password)
On 2014/03/18 01:11:39, fwereade wrote:
> Any possibility we could design this to output a .jenv (*without* all
the
> bootstrap-config stuff) on stdout? I think that the pass-jenvs-around
model is
> pretty tolerable, really.

Let's discuss this

https://codereview.appspot.com/75600044/diff/20001/juju/osenv/vars_windows_test.go
File juju/osenv/vars_windows_test.go (right):

https://codereview.appspot.com/75600044/diff/20001/juju/osenv/vars_windows_test.go#newcode9
juju/osenv/vars_windows_test.go:9:
"launchpad.net/juju-core/testing/testbase"
On 2014/03/18 01:11:39, fwereade wrote:
> This is a bit surprising. Are you sure it's right?

I'll double check - I'm sure I was left with no choice at the time

https://codereview.appspot.com/75600044/diff/20001/state/user.go
File state/user.go (right):

https://codereview.appspot.com/75600044/diff/20001/state/user.go#newcode32
state/user.go:32: return nil, fmt.Errorf("password can't be empty")
On 2014/03/18 01:11:39, fwereade wrote:
> why can admin have an empty password set? (and wasn't there a constant
for
> "admin"?)

Admin does have a constant- must have missed this one.

I'm sure there was a reason for this - either we discussed it or not
having it caused problems - I'll double check

https://codereview.appspot.com/75600044/

Revision history for this message
Matthew Williams (mattyw) wrote :

https://codereview.appspot.com/75600044/diff/20001/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/75600044/diff/20001/errors/errors.go#newcode98
errors/errors.go:98: return fmt.Sprintf("rc: %v", e.Code)
On 2014/03/18 01:11:39, fwereade wrote:
> If we're touching this can we please do something about the
overwhelmingly
> shitty UX of being told "rc: 2", which means literally nothing to most
> semi-normal humans? just "subprocess exited with code 2" or
something... it'd be
> way better if we said *which* one but that's way out of scope.

Done.

https://codereview.appspot.com/75600044/diff/20001/juju/osenv/vars_windows_test.go
File juju/osenv/vars_windows_test.go (right):

https://codereview.appspot.com/75600044/diff/20001/juju/osenv/vars_windows_test.go#newcode9
juju/osenv/vars_windows_test.go:9:
"launchpad.net/juju-core/testing/testbase"
On 2014/03/18 03:45:22, mattyw wrote:
> On 2014/03/18 01:11:39, fwereade wrote:
> > This is a bit surprising. Are you sure it's right?

> I'll double check - I'm sure I was left with no choice at the time

Done.

https://codereview.appspot.com/75600044/diff/20001/state/user.go
File state/user.go (right):

https://codereview.appspot.com/75600044/diff/20001/state/user.go#newcode32
state/user.go:32: return nil, fmt.Errorf("password can't be empty")
On 2014/03/18 03:45:22, mattyw wrote:
> On 2014/03/18 01:11:39, fwereade wrote:
> > why can admin have an empty password set? (and wasn't there a
constant for
> > "admin"?)

> Admin does have a constant- must have missed this one.

> I'm sure there was a reason for this - either we discussed it or not
having it
> caused problems - I'll double check

Done.

https://codereview.appspot.com/75600044/

Revision history for this message
Matthew Williams (mattyw) wrote :

Please take a look.

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser_test.go
File cmd/juju/adduser_test.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser_test.go#newcode21
cmd/juju/adduser_test.go:21: func (s *AddUserSuite) Testadduser(c *gc.C)
{
On 2014/03/18 01:11:39, fwereade wrote:
> TestAddUser, please

Done.

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/adduser_test.go#newcode27
cmd/juju/adduser_test.go:27: c.Assert(err, gc.ErrorMatches, "Failed to
create user: user already exists")
On 2014/03/18 01:11:39, fwereade wrote:
> we don't seem to have tests for the automatic password generation...
This isn't supported in the adduser stuff at the moment. Can I add this
in a separate branch?

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/removeuser_test.go
File cmd/juju/removeuser_test.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/removeuser_test.go#newcode19
cmd/juju/removeuser_test.go:19: func (s *RemoveUserSuite)
Testremoveuser(c *gc.C) {
On 2014/03/18 01:11:39, fwereade wrote:
> TestRemoveUser

Done.

https://codereview.appspot.com/75600044/diff/20001/cmd/juju/removeuser_test.go#newcode24
cmd/juju/removeuser_test.go:24: c.Assert(err, gc.IsNil)
On 2014/03/18 01:11:39, fwereade wrote:
> no tests for CheckEmpty etc (same applies to AddUser as well fwiw)

Done.

https://codereview.appspot.com/75600044/diff/20001/cmd/plugins/juju-metadata/validatetoolsmetadata.go
File cmd/plugins/juju-metadata/validatetoolsmetadata.go (right):

https://codereview.appspot.com/75600044/diff/20001/cmd/plugins/juju-metadata/validatetoolsmetadata.go#newcode24
cmd/plugins/juju-metadata/validatetoolsmetadata.go:24:
envcmd.EnvCommandBase
On 2014/03/18 01:11:39, fwereade wrote:
> Hmm. Shouldn't these be using Ensure..?

Done.

https://codereview.appspot.com/75600044/diff/20001/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/75600044/diff/20001/errors/errors.go#newcode124
errors/errors.go:124: // return code from the cmd.Main function rather
than the default of 1 if
On 2014/03/18 01:11:39, fwereade wrote:
> and this comment in particular makes me wonder whether it's right to
have moved
> it. I think it *is* but it's a conceptual boundary violation to
mention cmd.Main
> in this context -- can we maybe just give it a generic name like
"ExitCodeError"
> and keep the special handling details in cmd.Main?

> Scope issues again, though, so you can TODO this with a bug tagged
tech-debt if
> you like.

Done.

https://codereview.appspot.com/75600044/

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

TYVM for all the fixes -- a couple of bits need some work, but they'll
hopefully be changes that simplify/reduce scope rather than otherwise.
Let me know what you think.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode61
cmd/juju/adduser.go:61: pass, err := terminal.ReadPassword(0)
I'm not sure we really want this -- it STM to qualify as actual logic,
and thus to belong in Run rather than Init. It also should be using
stdin/out/err from the cmd.Context passed into run. Furthermore,
ReadPassword seems like overkill when we're just going to be storing it
in plaintext locally.

Let's just give them a default strong password in that case?

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode68
cmd/juju/adduser.go:68: c.Password = args[1]
I feel this lot could be easier to follow. If we drop the terminal
stuff, how about something like:

switch len(args) {
case 0:
     return fmt.Errorf("no username supplied")
case 1:
     c.Password = utils.RandomPassword()
case 2:
     c.Password = args[1]
default:
     return cmd.CheckEmpty(args[2:])
}
c.User = args[0]
return nil

..? YMMV, just a suggestion.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode88
cmd/juju/adduser.go:88:
//info.SetAPICredentials(configstore.APICredentials{c.User, c.Password})
d

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode89
cmd/juju/adduser.go:89:
//info.SetBootstrapConfig(map[string]interface{}{})
d

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode98
cmd/juju/adduser.go:98: err = ioutil.WriteFile(c.OutputFile, data, 0777)
You need to ctx.AbsPath() this so we can test relative paths work right.

https://codereview.appspot.com/75600044/diff/40001/environs/configstore/disk.go
File environs/configstore/disk.go (right):

https://codereview.appspot.com/75600044/diff/40001/environs/configstore/disk.go#newcode33
environs/configstore/disk.go:33: type EnvInfo struct {
I don't think we can just expose this type, it opens up all sorts of
cans of worms with Write and suchlike.

Would you extract the exported fields into a separate exported type, but
keep this one private, and embed the exported type therein? You'd need
to tweak the marshalling to read/write the embedded struct, not
environInfo itself, but it should otherwise be trivial. Make sense?

https://codereview.appspot.com/75600044/diff/40001/environs/configstore/disk.go#newcode168
environs/configstore/disk.go:168: func (info *EnvInfo) Write() error {
Yeah, this isn't going to be too happy if it's created without a .path.
Keep this type private, please.

https://codereview.appspot.com/75600044/

Revision history for this message
Matthew Williams (mattyw) wrote :

Please take a look.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode61
cmd/juju/adduser.go:61: pass, err := terminal.ReadPassword(0)
On 2014/03/27 12:14:34, fwereade wrote:
> I'm not sure we really want this -- it STM to qualify as actual logic,
and thus
> to belong in Run rather than Init. It also should be using
stdin/out/err from
> the cmd.Context passed into run. Furthermore, ReadPassword seems like
overkill
> when we're just going to be storing it in plaintext locally.

> Let's just give them a default strong password in that case?

Done.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode68
cmd/juju/adduser.go:68: c.Password = args[1]
On 2014/03/27 12:14:34, fwereade wrote:
> I feel this lot could be easier to follow. If we drop the terminal
stuff, how
> about something like:

> switch len(args) {
> case 0:
> return fmt.Errorf("no username supplied")
> case 1:
> c.Password = utils.RandomPassword()
> case 2:
> c.Password = args[1]
> default:
> return cmd.CheckEmpty(args[2:])
> }
> c.User = args[0]
> return nil

> ..? YMMV, just a suggestion.

This is a great idea - let'd do this!

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode68
cmd/juju/adduser.go:68: c.Password = args[1]
On 2014/03/27 12:14:34, fwereade wrote:
> I feel this lot could be easier to follow. If we drop the terminal
stuff, how
> about something like:

> switch len(args) {
> case 0:
> return fmt.Errorf("no username supplied")
> case 1:
> c.Password = utils.RandomPassword()
> case 2:
> c.Password = args[1]
> default:
> return cmd.CheckEmpty(args[2:])
> }
> c.User = args[0]
> return nil

> ..? YMMV, just a suggestion.

Done.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode88
cmd/juju/adduser.go:88:
//info.SetAPICredentials(configstore.APICredentials{c.User, c.Password})
On 2014/03/27 12:14:34, fwereade wrote:
> d

Done.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode89
cmd/juju/adduser.go:89:
//info.SetBootstrapConfig(map[string]interface{}{})
On 2014/03/27 12:14:34, fwereade wrote:
> d

Done.

https://codereview.appspot.com/75600044/diff/40001/cmd/juju/adduser.go#newcode98
cmd/juju/adduser.go:98: err = ioutil.WriteFile(c.OutputFile, data, 0777)
On 2014/03/27 12:14:34, fwereade wrote:
> You need to ctx.AbsPath() this so we can test relative paths work
right.

Done.

https://codereview.appspot.com/75600044/

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

Couple more tweaks to the command, and a couple of tests (and the json
marshalling); should be the last round.

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go#newcode21
cmd/juju/adduser.go:21: when the environent is destroyed.
Mention that it writes to stdout by default?

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go#newcode64
cmd/juju/adduser.go:64: fmt.Println(c.Password)
This isn't using the context to print, which it should; but it can't,
because Init. And really, dammit, we shouldn't be erroring for reasons
unrelated to arg-parsing here at all. Sorry I didn't spot this earlier.

Let's just lose the printing, because it'll be written out anyway in a
mo -- or, ?nicer?, leave c.Password empty, and do the RandomPassword in
Run if c.Password == "".

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go#newcode98
cmd/juju/adduser.go:98: return c.out.Write(ctx, outputInfo)
I think I'd still write this stuff out before calling AddUser -- a
nonsense jenv can be regenerated, but you can't recover the password if
the process goes down before we write it out.

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser_test.go
File cmd/juju/adduser_test.go (right):

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser_test.go#newcode59
cmd/juju/adduser_test.go:59: c.Assert(string(data), gc.DeepEquals,
string(expectedStr))
We should have some basic tests for --format to stdout and to -o. Don't
necessarily need to go overboard.

https://codereview.appspot.com/75600044/

Revision history for this message
Matthew Williams (mattyw) wrote :

Please take a look.

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go#newcode21
cmd/juju/adduser.go:21: when the environent is destroyed.
On 2014/03/27 17:27:23, fwereade wrote:
> Mention that it writes to stdout by default?

Done.

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go#newcode64
cmd/juju/adduser.go:64: fmt.Println(c.Password)
On 2014/03/27 17:27:23, fwereade wrote:
> This isn't using the context to print, which it should; but it can't,
because
> Init. And really, dammit, we shouldn't be erroring for reasons
unrelated to
> arg-parsing here at all. Sorry I didn't spot this earlier.

> Let's just lose the printing, because it'll be written out anyway in a
mo -- or,
> ?nicer?, leave c.Password empty, and do the RandomPassword in Run if
c.Password
> == "".

Done.

https://codereview.appspot.com/75600044/diff/60001/cmd/juju/adduser.go#newcode98
cmd/juju/adduser.go:98: return c.out.Write(ctx, outputInfo)
On 2014/03/27 17:27:23, fwereade wrote:
> I think I'd still write this stuff out before calling AddUser -- a
nonsense jenv
> can be regenerated, but you can't recover the password if the process
goes down
> before we write it out.

Done.

https://codereview.appspot.com/75600044/

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

LGTM with trivial fixes:

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser.go#newcode22
cmd/juju/adduser.go:22: An example jenv file will be output to allow you
to use the environment as that user.
A jenv file identifying the user and the environment will be written to
stdout, or to a path you specify with --output.

When add-environment lands, please add a note to this doc saying what
you can do with the jenv.

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser.go#newcode35
cmd/juju/adduser.go:35: generatePassword bool
I rather like exporting the fields that get set up by Init at least --
and this field should really go next to Password.

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser_test.go
File cmd/juju/adduser_test.go (right):

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser_test.go#newcode48
cmd/juju/adduser_test.go:48: ca-cert: ""
because yaml is evil, please test the output by parsing it, rather than
just checking for equality.

https://codereview.appspot.com/75600044/diff/80001/environs/info/info.go
File environs/info/info.go (right):

https://codereview.appspot.com/75600044/diff/80001/environs/info/info.go#newcode6
environs/info/info.go:6: // TODO add json tags
d

https://codereview.appspot.com/75600044/

Revision history for this message
Matthew Williams (mattyw) wrote :

Please take a look.

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser.go#newcode22
cmd/juju/adduser.go:22: An example jenv file will be output to allow you
to use the environment as that user.
On 2014/03/28 10:09:22, fwereade wrote:
> A jenv file identifying the user and the environment will be written
to stdout,
> or to a path you specify with --output.

> When add-environment lands, please add a note to this doc saying what
you can do
> with the jenv.

Done.

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser.go#newcode35
cmd/juju/adduser.go:35: generatePassword bool
On 2014/03/28 10:09:22, fwereade wrote:
> I rather like exporting the fields that get set up by Init at least --
and this
> field should really go next to Password.

Done.

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser_test.go
File cmd/juju/adduser_test.go (right):

https://codereview.appspot.com/75600044/diff/80001/cmd/juju/adduser_test.go#newcode48
cmd/juju/adduser_test.go:48: ca-cert: ""
On 2014/03/28 10:09:22, fwereade wrote:
> because yaml is evil, please test the output by parsing it, rather
than just
> checking for equality.

Done.

https://codereview.appspot.com/75600044/diff/80001/environs/info/info.go
File environs/info/info.go (right):

https://codereview.appspot.com/75600044/diff/80001/environs/info/info.go#newcode6
environs/info/info.go:6: // TODO add json tags
On 2014/03/28 10:09:22, fwereade wrote:
> d

Done.

https://codereview.appspot.com/75600044/

Revision history for this message
William Reade (fwereade) wrote :
Revision history for this message
Matthew Williams (mattyw) wrote :
Revision history for this message
Go Bot (go-bot) wrote :
Download full text (59.5 KiB)

The attempt to merge lp:~mattyw/juju-core/user-add-remove-cli into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core 0.013s
ok launchpad.net/juju-core/agent 1.083s
ok launchpad.net/juju-core/agent/mongo 0.554s
ok launchpad.net/juju-core/agent/tools 0.208s
ok launchpad.net/juju-core/bzr 5.396s
ok launchpad.net/juju-core/cert 2.426s
ok launchpad.net/juju-core/charm 0.381s
? launchpad.net/juju-core/charm/hooks [no test files]
? launchpad.net/juju-core/charm/testing [no test files]
ok launchpad.net/juju-core/cloudinit 0.027s
ok launchpad.net/juju-core/cloudinit/sshinit 0.834s
ok launchpad.net/juju-core/cmd 0.152s
ok launchpad.net/juju-core/cmd/charm-admin 0.780s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/envcmd 0.248s

----------------------------------------------------------------------
FAIL: bootstrap_test.go:364: BootstrapSuite.TestBootstrapTwice

[LOG] 17.73050 DEBUG juju.environs.tools no architecture specified when finding tools, looking for any
[LOG] 17.73051 DEBUG juju.environs.tools no series specified when finding tools, looking for any
[LOG] 17.73059 DEBUG juju.environs.simplestreams fetchData failed for "file:///tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.sjson": stat /tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.sjson: no such file or directory
[LOG] 17.73062 DEBUG juju.environs.simplestreams cannot load index "file:///tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.sjson": invalid URL "file:///tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.sjson" not found
[LOG] 17.73067 DEBUG juju.environs.simplestreams fetchData failed for "file:///tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.json": stat /tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.json: no such file or directory
[LOG] 17.73070 DEBUG juju.environs.simplestreams cannot load index "file:///tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.json": invalid URL "file:///tmp/gocheck-1976235410884491574/24/tools/streams/v1/index.json" not found
[LOG] 17.73211 INFO juju.environs.tools Writing tools/streams/v1/index.json
[LOG] 17.73241 INFO juju.environs.tools Writing tools/streams/v1/com.ubuntu.juju:released:tools.json
[LOG] 17.73290 INFO juju.provider.dummy reset environment
[LOG] 17.73654 INFO juju Reset successfully reset admin password
[LOG] 17.73733 DEBUG juju.environs.configstore Making /tmp/gocheck-1976235410884491574/25/.juju/environments
clearing private storage
removing files: []
[LOG] 17.84044 WARNING juju.cmd.juju ignoring environments.yaml: using bootstrap config in file "/tmp/gocheck-1976235410884491574/25/.juju/environments/peckham.jenv"
[LOG] 17.84120 DEBUG juju.environs ConfigForName found bootstrap config map[string]interface {}{"admin-secret":"arble", "bootstrap-addresses-delay":10, "image-stream":"", "logging-config":"<root>=DEBUG;unit=DEBUG", "development":false, "firewall-mode":"instance", "syslog-port":6514, "bootstrap-timeout":600, "broken":"", "name":"peckham", "ca-cert":"-----BEGIN CERTIFICATE-----\nMIIB2DCCAY...

Revision history for this message
Roger Peppe (rogpeppe) wrote :
Download full text (3.8 KiB)

Looks great in general, with some comments and suggestions below.

https://codereview.appspot.com/75600044/diff/120001/cmd/envcmd/environmentcommand.go
File cmd/envcmd/environmentcommand.go (right):

https://codereview.appspot.com/75600044/diff/120001/cmd/envcmd/environmentcommand.go#newcode67
cmd/envcmd/environmentcommand.go:67: func (c *EnvCommandBase)
EnsureEnvNameSet() error {
I suggest that Init would be a more appropriate name for this method (we
want every command that embeds it to call it)

// Init initializes the Command.
func (c *Command) Init() error

https://codereview.appspot.com/75600044/diff/120001/cmd/plugins/juju-restore/restore.go
File cmd/plugins/juju-restore/restore.go (right):

https://codereview.appspot.com/75600044/diff/120001/cmd/plugins/juju-restore/restore.go#newcode92
cmd/plugins/juju-restore/restore.go:92: if c.showDescription {
this should call EnsureEnvNameSet, presumably?

https://codereview.appspot.com/75600044/diff/120001/environs/info/info.go
File environs/info/info.go (right):

https://codereview.appspot.com/75600044/diff/120001/environs/info/info.go#newcode6
environs/info/info.go:6: type EnvironInfo struct {
Why does this need its own package?

https://codereview.appspot.com/75600044/diff/120001/environs/manual/provisioner_test.go
File environs/manual/provisioner_test.go (right):

https://codereview.appspot.com/75600044/diff/120001/environs/manual/provisioner_test.go#newcode112
environs/manual/provisioner_test.go:112: c.Assert(err, gc.ErrorMatches,
"error checking if provisioned: subprocess encountered error code 255")
I know it's not your doing, but I don't like seeing these error
messages. An error code tells us almost nothing. We really need to
change things so we use stderr when possible. No action required, just
saying.

https://codereview.appspot.com/75600044/diff/120001/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/75600044/diff/120001/errors/errors.go#newcode101
errors/errors.go:101: func IsRcPassthroughError(err error) bool {
If reasonable, I think this really does belong to cmd, because it
pertains directly to functionality in that package.

https://codereview.appspot.com/75600044/diff/120001/errors/errors.go#newcode126
errors/errors.go:126: // TODO (mattyw) It's odd that we're talking about
cmd.Main in here.
indeed

https://codereview.appspot.com/75600044/diff/120001/juju/api.go
File juju/api.go (right):

https://codereview.appspot.com/75600044/diff/120001/juju/api.go#newcode104
juju/api.go:104: func NewUserManagerClient(envName string)
(*usermanager.Client, error) {
Rather than create a new function in juju for every single API client,
I'd prefer to just export NewAPIClient here, and for clients to use
facade New methods as appropriate.

So I'd delete NewKeyManagerClient and NewUserManagerClient, and add
methods to api.Client, KeyManager and UserManager.

That means a certain amount of shake up (the New functions need to
change to take an interface to avoid cyclic imports), but it's the same
pattern that works well for agents, and consistency is good.

Perhaps leave it like it is for now, and address that in a subsequent
CL?

https://codereview.appspot.com/7560...

Read more...

Revision history for this message
Matthew Williams (mattyw) wrote :
Download full text (3.2 KiB)

https://codereview.appspot.com/75600044/diff/120001/cmd/envcmd/environmentcommand.go
File cmd/envcmd/environmentcommand.go (right):

https://codereview.appspot.com/75600044/diff/120001/cmd/envcmd/environmentcommand.go#newcode67
cmd/envcmd/environmentcommand.go:67: func (c *EnvCommandBase)
EnsureEnvNameSet() error {
On 2014/03/31 14:21:12, rog wrote:
> I suggest that Init would be a more appropriate name for this method
(we want
> every command that embeds it to call it)

> // Init initializes the Command.
> func (c *Command) Init() error

Done.

https://codereview.appspot.com/75600044/diff/120001/cmd/plugins/juju-restore/restore.go
File cmd/plugins/juju-restore/restore.go (right):

https://codereview.appspot.com/75600044/diff/120001/cmd/plugins/juju-restore/restore.go#newcode92
cmd/plugins/juju-restore/restore.go:92: if c.showDescription {
On 2014/03/31 14:21:12, rog wrote:
> this should call EnsureEnvNameSet, presumably?

Done.

https://codereview.appspot.com/75600044/diff/120001/environs/info/info.go
File environs/info/info.go (right):

https://codereview.appspot.com/75600044/diff/120001/environs/info/info.go#newcode6
environs/info/info.go:6: type EnvironInfo struct {
On 2014/03/31 14:21:12, rog wrote:
> Why does this need its own package?

Done.

https://codereview.appspot.com/75600044/diff/120001/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/75600044/diff/120001/errors/errors.go#newcode101
errors/errors.go:101: func IsRcPassthroughError(err error) bool {
On 2014/03/31 14:21:12, rog wrote:
> If reasonable, I think this really does belong to cmd, because it
pertains
> directly to functionality in that package.

Done.

https://codereview.appspot.com/75600044/diff/120001/errors/errors.go#newcode126
errors/errors.go:126: // TODO (mattyw) It's odd that we're talking about
cmd.Main in here.
On 2014/03/31 14:21:12, rog wrote:
> indeed

Done.

https://codereview.appspot.com/75600044/diff/120001/juju/api.go
File juju/api.go (right):

https://codereview.appspot.com/75600044/diff/120001/juju/api.go#newcode104
juju/api.go:104: func NewUserManagerClient(envName string)
(*usermanager.Client, error) {
On 2014/03/31 14:21:12, rog wrote:
> Rather than create a new function in juju for every single API client,
I'd
> prefer to just export NewAPIClient here, and for clients to use facade
New
> methods as appropriate.

> So I'd delete NewKeyManagerClient and NewUserManagerClient, and add
methods to
> api.Client, KeyManager and UserManager.

https://bugs.launchpad.net/juju-core/+bug/1300637
> That means a certain amount of shake up (the New functions need to
change to
> take an interface to avoid cyclic imports), but it's the same pattern
that works
> well for agents, and consistency is good.

> Perhaps leave it like it is for now, and address that in a subsequent
CL?

https://codereview.appspot.com/75600044/diff/120001/testing/environ.go
File testing/environ.go (right):

https://codereview.appspot.com/75600044/diff/120001/testing/environ.go#newcode247
testing/environ.go:247: func MakeSampleHomeWithFiles(c *gc.C, files
[]TestFile) *FakeHome {
On 2014/03/31 14:21:12, rog wrote:
> Or perhaps change MakeSampleHome to be:

> func...

Read more...

Revision history for this message
Matthew Williams (mattyw) wrote :
Revision history for this message
William Reade (fwereade) wrote :
Download full text (3.3 KiB)

I'm a little bit irritated at the changes to EnsureEnvNameSet, and the
EnvironInfoData type, but neither decision is irrevocable and I really
cannot be bothered to fight them; let's just land this.

LGTM contingent on the other tweaks below, ping me if anything's not
clear.

https://codereview.appspot.com/75600044/diff/120001/cmd/envcmd/environmentcommand.go
File cmd/envcmd/environmentcommand.go (right):

https://codereview.appspot.com/75600044/diff/120001/cmd/envcmd/environmentcommand.go#newcode67
cmd/envcmd/environmentcommand.go:67: func (c *EnvCommandBase)
EnsureEnvNameSet() error {
On 2014/04/01 08:07:31, mattyw wrote:
> On 2014/03/31 14:21:12, rog wrote:
> > I suggest that Init would be a more appropriate name for this method
(we want
> > every command that embeds it to call it)
> >
> > // Init initializes the Command.
> > func (c *Command) Init() error

> Done.

I suggest that it would not, because it's aping the cmd.Command
interface but not actually satisfying it; and it has different
responsibilities, which might be most relevant at Init time but are
actually to do with, uh, ensuring an environment is set...

https://codereview.appspot.com/75600044/diff/120001/environs/info/info.go
File environs/info/info.go (right):

https://codereview.appspot.com/75600044/diff/120001/environs/info/info.go#newcode6
environs/info/info.go:6: type EnvironInfo struct {
On 2014/04/01 08:07:31, mattyw wrote:
> On 2014/03/31 14:21:12, rog wrote:
> > Why does this need its own package?

> Done.

What's the benefit of removing a small and focused package in favour of
jamming its contents into a tangentially-related one? at the cost of a
sensible type name?

https://codereview.appspot.com/75600044/diff/120001/environs/manual/provisioner_test.go
File environs/manual/provisioner_test.go (right):

https://codereview.appspot.com/75600044/diff/120001/environs/manual/provisioner_test.go#newcode112
environs/manual/provisioner_test.go:112: c.Assert(err, gc.ErrorMatches,
"error checking if provisioned: subprocess encountered error code 255")
On 2014/03/31 14:21:12, rog wrote:
> I know it's not your doing, but I don't like seeing these error
messages. An
> error code tells us almost nothing. We really need to change things so
we use
> stderr when possible. No action required, just saying.

Progress not perfection. This beats the hell out of "rc:255" :).

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/adduser.go#newcode98
cmd/juju/adduser.go:98: c.out.Write(ctx, outputInfo)
doesn't this return an error?

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/environment.go
File cmd/juju/environment.go (right):

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/environment.go#newcode219
cmd/juju/environment.go:219: func (c *UnsetEnvironmentCommand) Init(args
[]string) (err error) {
This needs EnsureEnvNameSet as well

https://codereview.appspot.com/75600044/diff/140001/juju/api.go
File juju/api.go (right):

https://codereview.appspot.com/75600044/diff/140001/juju/api.go#newcode136
juju/api.go:136: envs, err := environs.ReadEnvirons(""...

Read more...

Revision history for this message
Matthew Williams (mattyw) wrote :

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/adduser.go
File cmd/juju/adduser.go (right):

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/adduser.go#newcode98
cmd/juju/adduser.go:98: c.out.Write(ctx, outputInfo)
On 2014/04/01 09:40:54, fwereade wrote:
> doesn't this return an error?

Done.

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/environment.go
File cmd/juju/environment.go (right):

https://codereview.appspot.com/75600044/diff/140001/cmd/juju/environment.go#newcode219
cmd/juju/environment.go:219: func (c *UnsetEnvironmentCommand) Init(args
[]string) (err error) {
On 2014/04/01 09:40:54, fwereade wrote:
> This needs EnsureEnvNameSet as well

Done.

https://codereview.appspot.com/75600044/diff/140001/juju/api.go
File juju/api.go (right):

https://codereview.appspot.com/75600044/diff/140001/juju/api.go#newcode136
juju/api.go:136: envs, err := environs.ReadEnvirons("")
On 2014/04/01 09:40:54, fwereade wrote:
> Shouldn't this code have been deleted? We should never have an empty
envName to
> deal with now...
https://bugs.launchpad.net/juju-core/+bug/1300823

https://codereview.appspot.com/75600044/

Revision history for this message
Matthew Williams (mattyw) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/cmd.go'
2--- cmd/cmd.go 2014-03-26 01:17:17 +0000
3+++ cmd/cmd.go 2014-04-01 14:32:48 +0000
4@@ -17,16 +17,16 @@
5 "launchpad.net/gnuflag"
6 )
7
8-type rcPassthroughError struct {
9- code int
10+type RcPassthroughError struct {
11+ Code int
12 }
13
14-func (e *rcPassthroughError) Error() string {
15- return fmt.Sprintf("rc: %v", e.code)
16+func (e *RcPassthroughError) Error() string {
17+ return fmt.Sprintf("subprocess encountered error code %v", e.Code)
18 }
19
20 func IsRcPassthroughError(err error) bool {
21- _, ok := err.(*rcPassthroughError)
22+ _, ok := err.(*RcPassthroughError)
23 return ok
24 }
25
26@@ -34,7 +34,7 @@
27 // return code from the cmd.Main function rather than the default of 1 if
28 // there is an error.
29 func NewRcPassthroughError(code int) error {
30- return &rcPassthroughError{code}
31+ return &RcPassthroughError{code}
32 }
33
34 // ErrSilent can be returned from Run to signal that Main should exit with
35@@ -243,7 +243,7 @@
36 }
37 if err := c.Run(ctx); err != nil {
38 if IsRcPassthroughError(err) {
39- return err.(*rcPassthroughError).code
40+ return err.(*RcPassthroughError).Code
41 }
42 if err != ErrSilent {
43 fmt.Fprintf(ctx.Stderr, "error: %v\n", err)
44
45=== added directory 'cmd/envcmd'
46=== renamed file 'cmd/environmentcommand.go' => 'cmd/envcmd/environmentcommand.go'
47--- cmd/environmentcommand.go 2014-01-22 22:48:54 +0000
48+++ cmd/envcmd/environmentcommand.go 2014-04-01 14:32:48 +0000
49@@ -1,7 +1,7 @@
50 // Copyright 2013 Canonical Ltd.
51 // Licensed under the AGPLv3, see LICENCE file for details.
52
53-package cmd
54+package envcmd
55
56 import (
57 "fmt"
58@@ -12,6 +12,8 @@
59
60 "launchpad.net/gnuflag"
61
62+ "launchpad.net/juju-core/cmd"
63+ "launchpad.net/juju-core/environs"
64 "launchpad.net/juju-core/juju/osenv"
65 )
66
67@@ -20,7 +22,7 @@
68 // The purpose of EnvCommandBase is to provide a default member and flag
69 // setting for commands that deal across different environments.
70 type EnvCommandBase struct {
71- CommandBase
72+ cmd.CommandBase
73 EnvName string
74 }
75
76@@ -62,6 +64,17 @@
77 return ReadCurrentEnvironment()
78 }
79
80+func (c *EnvCommandBase) Init() error {
81+ if c.EnvName == "" {
82+ envs, err := environs.ReadEnvirons("")
83+ if err != nil {
84+ return err
85+ }
86+ c.EnvName = envs.Default
87+ }
88+ return nil
89+}
90+
91 func (c *EnvCommandBase) SetFlags(f *gnuflag.FlagSet) {
92 defaultEnv := getDefaultEnvironment()
93 f.StringVar(&c.EnvName, "e", defaultEnv, "juju environment to operate in")
94
95=== renamed file 'cmd/environmentcommand_test.go' => 'cmd/envcmd/environmentcommand_test.go'
96--- cmd/environmentcommand_test.go 2014-01-22 22:48:54 +0000
97+++ cmd/envcmd/environmentcommand_test.go 2014-04-01 14:32:48 +0000
98@@ -1,27 +1,30 @@
99 // Copyright 2013 Canonical Ltd.
100 // Licensed under the AGPLv3, see LICENCE file for details.
101
102-package cmd_test
103+package envcmd_test
104
105 import (
106 "io/ioutil"
107 "os"
108+ "testing"
109
110 gc "launchpad.net/gocheck"
111
112- "launchpad.net/juju-core/cmd"
113+ "launchpad.net/juju-core/cmd/envcmd"
114 "launchpad.net/juju-core/juju/osenv"
115- "launchpad.net/juju-core/testing"
116+ jujutesting "launchpad.net/juju-core/testing"
117 )
118
119 type EnvironmentCommandSuite struct {
120- home *testing.FakeHome
121+ home *jujutesting.FakeHome
122 }
123
124 var _ = gc.Suite(&EnvironmentCommandSuite{})
125
126+func Test(t *testing.T) { gc.TestingT(t) }
127+
128 func (s *EnvironmentCommandSuite) SetUpTest(c *gc.C) {
129- s.home = testing.MakeEmptyFakeHome(c)
130+ s.home = jujutesting.MakeEmptyFakeHome(c)
131 }
132
133 func (s *EnvironmentCommandSuite) TearDownTest(c *gc.C) {
134@@ -29,54 +32,54 @@
135 }
136
137 func (s *EnvironmentCommandSuite) TestReadCurrentEnvironmentUnset(c *gc.C) {
138- env := cmd.ReadCurrentEnvironment()
139+ env := envcmd.ReadCurrentEnvironment()
140 c.Assert(env, gc.Equals, "")
141 }
142
143 func (s *EnvironmentCommandSuite) TestReadCurrentEnvironmentSet(c *gc.C) {
144- err := cmd.WriteCurrentEnvironment("fubar")
145+ err := envcmd.WriteCurrentEnvironment("fubar")
146 c.Assert(err, gc.IsNil)
147- env := cmd.ReadCurrentEnvironment()
148+ env := envcmd.ReadCurrentEnvironment()
149 c.Assert(env, gc.Equals, "fubar")
150 }
151
152 func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentNothingSet(c *gc.C) {
153- env := cmd.GetDefaultEnvironment()
154+ env := envcmd.GetDefaultEnvironment()
155 c.Assert(env, gc.Equals, "")
156 }
157
158 func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentCurrentEnvironmentSet(c *gc.C) {
159- err := cmd.WriteCurrentEnvironment("fubar")
160+ err := envcmd.WriteCurrentEnvironment("fubar")
161 c.Assert(err, gc.IsNil)
162- env := cmd.GetDefaultEnvironment()
163+ env := envcmd.GetDefaultEnvironment()
164 c.Assert(env, gc.Equals, "fubar")
165 }
166
167 func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentJujuEnvSet(c *gc.C) {
168 os.Setenv(osenv.JujuEnvEnvKey, "magic")
169- env := cmd.GetDefaultEnvironment()
170+ env := envcmd.GetDefaultEnvironment()
171 c.Assert(env, gc.Equals, "magic")
172 }
173
174 func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentBothSet(c *gc.C) {
175 os.Setenv(osenv.JujuEnvEnvKey, "magic")
176- err := cmd.WriteCurrentEnvironment("fubar")
177+ err := envcmd.WriteCurrentEnvironment("fubar")
178 c.Assert(err, gc.IsNil)
179- env := cmd.GetDefaultEnvironment()
180+ env := envcmd.GetDefaultEnvironment()
181 c.Assert(env, gc.Equals, "magic")
182 }
183
184 func (s *EnvironmentCommandSuite) TestWriteAddsNewline(c *gc.C) {
185- err := cmd.WriteCurrentEnvironment("fubar")
186+ err := envcmd.WriteCurrentEnvironment("fubar")
187 c.Assert(err, gc.IsNil)
188- current, err := ioutil.ReadFile(cmd.GetCurrentEnvironmentFilePath())
189+ current, err := ioutil.ReadFile(envcmd.GetCurrentEnvironmentFilePath())
190 c.Assert(err, gc.IsNil)
191 c.Assert(string(current), gc.Equals, "fubar\n")
192 }
193
194 func (*EnvironmentCommandSuite) TestErrorWritingFile(c *gc.C) {
195 // Can't write a file over a directory.
196- os.MkdirAll(cmd.GetCurrentEnvironmentFilePath(), 0777)
197- err := cmd.WriteCurrentEnvironment("fubar")
198+ os.MkdirAll(envcmd.GetCurrentEnvironmentFilePath(), 0777)
199+ err := envcmd.WriteCurrentEnvironment("fubar")
200 c.Assert(err, gc.ErrorMatches, "unable to write to the environment file: .*")
201 }
202
203=== renamed file 'cmd/export_test.go' => 'cmd/envcmd/export_test.go'
204--- cmd/export_test.go 2014-03-25 22:31:46 +0000
205+++ cmd/envcmd/export_test.go 2014-04-01 14:32:48 +0000
206@@ -1,7 +1,7 @@
207 // Copyright 2013 Canonical Ltd.
208 // Licensed under the AGPLv3, see LICENCE file for details.
209
210-package cmd
211+package envcmd
212
213 var (
214 GetDefaultEnvironment = getDefaultEnvironment
215
216=== modified file 'cmd/juju/addmachine.go'
217--- cmd/juju/addmachine.go 2014-02-22 15:40:29 +0000
218+++ cmd/juju/addmachine.go 2014-04-01 14:32:48 +0000
219@@ -10,6 +10,7 @@
220 "launchpad.net/gnuflag"
221
222 "launchpad.net/juju-core/cmd"
223+ "launchpad.net/juju-core/cmd/envcmd"
224 "launchpad.net/juju-core/constraints"
225 "launchpad.net/juju-core/environs/manual"
226 "launchpad.net/juju-core/instance"
227@@ -56,7 +57,7 @@
228
229 // AddMachineCommand starts a new machine and registers it in the environment.
230 type AddMachineCommand struct {
231- cmd.EnvCommandBase
232+ envcmd.EnvCommandBase
233 // If specified, use this series, else use the environment default-series
234 Series string
235 // If specified, these constraints are merged with those already in the environment.
236@@ -82,6 +83,10 @@
237 }
238
239 func (c *AddMachineCommand) Init(args []string) error {
240+ err := c.EnvCommandBase.Init()
241+ if err != nil {
242+ return err
243+ }
244 if c.Constraints.Container != nil {
245 return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container)
246 }
247
248=== modified file 'cmd/juju/addrelation.go'
249--- cmd/juju/addrelation.go 2013-11-05 04:12:48 +0000
250+++ cmd/juju/addrelation.go 2014-04-01 14:32:48 +0000
251@@ -7,12 +7,13 @@
252 "fmt"
253
254 "launchpad.net/juju-core/cmd"
255+ "launchpad.net/juju-core/cmd/envcmd"
256 "launchpad.net/juju-core/juju"
257 )
258
259 // AddRelationCommand adds a relation between two service endpoints.
260 type AddRelationCommand struct {
261- cmd.EnvCommandBase
262+ envcmd.EnvCommandBase
263 Endpoints []string
264 }
265
266@@ -25,6 +26,10 @@
267 }
268
269 func (c *AddRelationCommand) Init(args []string) error {
270+ err := c.EnvCommandBase.Init()
271+ if err != nil {
272+ return err
273+ }
274 if len(args) != 2 {
275 return fmt.Errorf("a relation must involve two services")
276 }
277
278=== modified file 'cmd/juju/addunit.go'
279--- cmd/juju/addunit.go 2013-10-08 14:53:20 +0000
280+++ cmd/juju/addunit.go 2014-04-01 14:32:48 +0000
281@@ -10,6 +10,7 @@
282 "launchpad.net/gnuflag"
283
284 "launchpad.net/juju-core/cmd"
285+ "launchpad.net/juju-core/cmd/envcmd"
286 "launchpad.net/juju-core/juju"
287 )
288
289@@ -42,7 +43,7 @@
290
291 // AddUnitCommand is responsible adding additional units to a service.
292 type AddUnitCommand struct {
293- cmd.EnvCommandBase
294+ envcmd.EnvCommandBase
295 UnitCommandBase
296 ServiceName string
297 }
298
299=== added file 'cmd/juju/adduser.go'
300--- cmd/juju/adduser.go 1970-01-01 00:00:00 +0000
301+++ cmd/juju/adduser.go 2014-04-01 14:32:48 +0000
302@@ -0,0 +1,103 @@
303+// Copyright 2012, 2013, 2014 Canonical Ltd.
304+// Licensed under the AGPLv3, see LICENCE file for details.
305+
306+package main
307+
308+import (
309+ "fmt"
310+
311+ "launchpad.net/gnuflag"
312+ "launchpad.net/juju-core/cmd"
313+ "launchpad.net/juju-core/cmd/envcmd"
314+ "launchpad.net/juju-core/environs/configstore"
315+ "launchpad.net/juju-core/juju"
316+ "launchpad.net/juju-core/utils"
317+)
318+
319+const addUserDoc = `
320+Add users to an existing environment
321+The user information is stored within an existing environment, and will be lost
322+when the environent is destroyed.
323+A jenv file identifying the user and the environment will be written to stdout,
324+or to a path you specify with --output.
325+
326+Examples:
327+ juju add-user foobar mypass (Add user foobar with password mypass)
328+ juju add-user foobar (Add user foobar. A strong password will be generated and printed)
329+ juju add-user foobar -o filename (Add user foobar (with generated password) and save example jenv file to filename)
330+`
331+
332+type AddUserCommand struct {
333+ envcmd.EnvCommandBase
334+ User string
335+ Password string
336+ GeneratePassword bool
337+ out cmd.Output
338+}
339+
340+func (c *AddUserCommand) Info() *cmd.Info {
341+ return &cmd.Info{
342+ Name: "add-user",
343+ Args: "<username> <password>",
344+ Purpose: "adds a user",
345+ Doc: addUserDoc,
346+ }
347+}
348+
349+func (c *AddUserCommand) SetFlags(f *gnuflag.FlagSet) {
350+ c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{
351+ "yaml": cmd.FormatYaml,
352+ "json": cmd.FormatJson,
353+ })
354+}
355+func (c *AddUserCommand) Init(args []string) error {
356+ err := c.EnvCommandBase.Init()
357+ if err != nil {
358+ return err
359+ }
360+ switch len(args) {
361+ case 0:
362+ return fmt.Errorf("no username supplied")
363+ case 1:
364+ c.GeneratePassword = true
365+ case 2:
366+ c.Password = args[1]
367+ default:
368+ return cmd.CheckEmpty(args[2:])
369+ }
370+
371+ c.User = args[0]
372+ return nil
373+}
374+
375+func (c *AddUserCommand) Run(ctx *cmd.Context) error {
376+ store, err := configstore.Default()
377+ if err != nil {
378+ return fmt.Errorf("cannot open environment info storage: %v", err)
379+ }
380+ storeInfo, err := store.ReadInfo(c.EnvName)
381+ if err != nil {
382+ return err
383+ }
384+ client, err := juju.NewUserManagerClient(c.EnvName)
385+ if err != nil {
386+ return err
387+ }
388+ defer client.Close()
389+ if c.GeneratePassword {
390+ c.Password, err = utils.RandomPassword()
391+ if err != nil {
392+ return fmt.Errorf("Failed to generate password: %v", err)
393+ }
394+ }
395+ outputInfo := configstore.EnvironInfoData{}
396+ outputInfo.User = c.User
397+ outputInfo.Password = c.Password
398+ outputInfo.StateServers = storeInfo.APIEndpoint().Addresses
399+ outputInfo.CACert = storeInfo.APIEndpoint().CACert
400+ err = c.out.Write(ctx, outputInfo)
401+ if err != nil {
402+ return err
403+ }
404+ return client.AddUser(c.User, c.Password)
405+}
406
407=== added file 'cmd/juju/adduser_test.go'
408--- cmd/juju/adduser_test.go 1970-01-01 00:00:00 +0000
409+++ cmd/juju/adduser_test.go 2014-04-01 14:32:48 +0000
410@@ -0,0 +1,106 @@
411+// Copyright 2014 Canonical Ltd.
412+// Licensed under the AGPLv3, see LICENCE file for details.
413+
414+package main
415+
416+import (
417+ "bytes"
418+ "io/ioutil"
419+
420+ gc "launchpad.net/gocheck"
421+ "launchpad.net/goyaml"
422+ jujutesting "launchpad.net/juju-core/juju/testing"
423+
424+ "launchpad.net/juju-core/testing"
425+)
426+
427+// All of the functionality of the AddUser api call is contained elsewhere
428+// This suite provides basic tests for the AddUser command
429+type AddUserSuite struct {
430+ jujutesting.RepoSuite
431+}
432+
433+var _ = gc.Suite(&AddUserSuite{})
434+
435+func (s *AddUserSuite) TestAddUser(c *gc.C) {
436+
437+ _, err := testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password"})
438+ c.Assert(err, gc.IsNil)
439+
440+ _, err = testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "newpassword"})
441+ c.Assert(err, gc.ErrorMatches, "Failed to create user: user already exists")
442+}
443+
444+func (s *AddUserSuite) TestTooManyArgs(c *gc.C) {
445+ _, err := testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password", "whoops"})
446+ c.Assert(err, gc.ErrorMatches, `unrecognized args: \["whoops"\]`)
447+}
448+
449+func (s *AddUserSuite) TestNotEnoughArgs(c *gc.C) {
450+ _, err := testing.RunCommand(c, &AddUserCommand{}, []string{})
451+ c.Assert(err, gc.ErrorMatches, `no username supplied`)
452+}
453+
454+func (s *AddUserSuite) TestJenvYamlFileOutput(c *gc.C) {
455+ expected := map[string]interface{}{
456+ "user": "foobar",
457+ "password": "password",
458+ "state-servers": []interface{}{},
459+ "ca-cert": ""}
460+ tempFile, err := ioutil.TempFile("", "adduser-test")
461+ tempFile.Close()
462+ c.Assert(err, gc.IsNil)
463+ _, err = testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password", "-o", tempFile.Name()})
464+ c.Assert(err, gc.IsNil)
465+ data, err := ioutil.ReadFile(tempFile.Name())
466+ result := map[string]interface{}{}
467+ err = goyaml.Unmarshal(data, &result)
468+ c.Assert(err, gc.IsNil)
469+ c.Assert(result, gc.DeepEquals, expected)
470+}
471+
472+func (s *AddUserSuite) TestJenvYamlOutput(c *gc.C) {
473+ expected := map[string]interface{}{
474+ "user": "foobar",
475+ "password": "password",
476+ "state-servers": []interface{}{},
477+ "ca-cert": ""}
478+ ctx, err := testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password"})
479+ c.Assert(err, gc.IsNil)
480+ stdout := ctx.Stdout.(*bytes.Buffer).Bytes()
481+ result := map[string]interface{}{}
482+ err = goyaml.Unmarshal(stdout, &result)
483+ c.Assert(err, gc.IsNil)
484+ c.Assert(result, gc.DeepEquals, expected)
485+}
486+
487+func (s *AddUserSuite) TestJenvJsonOutput(c *gc.C) {
488+ expected := `{"User":"foobar","Password":"password","state-servers":null,"ca-cert":""}
489+`
490+ tempFile, err := ioutil.TempFile("", "adduser-test")
491+ tempFile.Close()
492+ c.Assert(err, gc.IsNil)
493+ _, err = testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password", "-o", tempFile.Name(), "--format", "json"})
494+ c.Assert(err, gc.IsNil)
495+ data, err := ioutil.ReadFile(tempFile.Name())
496+ c.Assert(string(data), gc.DeepEquals, expected)
497+}
498+
499+func (s *AddUserSuite) TestJenvJsonFileOutput(c *gc.C) {
500+ expected := `{"User":"foobar","Password":"password","state-servers":null,"ca-cert":""}
501+`
502+ ctx, err := testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password", "--format", "json"})
503+ c.Assert(err, gc.IsNil)
504+ stdout := ctx.Stdout.(*bytes.Buffer).String()
505+ c.Assert(stdout, gc.DeepEquals, expected)
506+}
507+
508+func (s *AddUserSuite) TestGeneratePassword(c *gc.C) {
509+ ctx, err := testing.RunCommand(c, &AddUserCommand{}, []string{"foobar"})
510+ c.Assert(err, gc.IsNil)
511+ stdout := ctx.Stdout.(*bytes.Buffer).Bytes()
512+ var d map[string]interface{}
513+ err = goyaml.Unmarshal(stdout, &d)
514+ c.Assert(err, gc.IsNil)
515+ c.Assert(d["user"], gc.DeepEquals, "foobar")
516+}
517
518=== modified file 'cmd/juju/authorisedkeys_add.go'
519--- cmd/juju/authorisedkeys_add.go 2013-12-13 06:53:01 +0000
520+++ cmd/juju/authorisedkeys_add.go 2014-04-01 14:32:48 +0000
521@@ -10,6 +10,7 @@
522 "launchpad.net/gnuflag"
523
524 "launchpad.net/juju-core/cmd"
525+ "launchpad.net/juju-core/cmd/envcmd"
526 "launchpad.net/juju-core/juju"
527 )
528
529@@ -19,7 +20,7 @@
530
531 // AddKeysCommand is used to add a new authorized ssh key for a user.
532 type AddKeysCommand struct {
533- cmd.EnvCommandBase
534+ envcmd.EnvCommandBase
535 user string
536 sshKeys []string
537 }
538@@ -34,6 +35,10 @@
539 }
540
541 func (c *AddKeysCommand) Init(args []string) error {
542+ err := c.EnvCommandBase.Init()
543+ if err != nil {
544+ return err
545+ }
546 switch len(args) {
547 case 0:
548 return errors.New("no ssh key specified")
549
550=== modified file 'cmd/juju/authorisedkeys_delete.go'
551--- cmd/juju/authorisedkeys_delete.go 2013-12-13 06:53:01 +0000
552+++ cmd/juju/authorisedkeys_delete.go 2014-04-01 14:32:48 +0000
553@@ -10,6 +10,7 @@
554 "launchpad.net/gnuflag"
555
556 "launchpad.net/juju-core/cmd"
557+ "launchpad.net/juju-core/cmd/envcmd"
558 "launchpad.net/juju-core/juju"
559 )
560
561@@ -21,7 +22,7 @@
562
563 // DeleteKeysCommand is used to delete authorized ssh keys for a user.
564 type DeleteKeysCommand struct {
565- cmd.EnvCommandBase
566+ envcmd.EnvCommandBase
567 user string
568 keyIds []string
569 }
570@@ -36,6 +37,10 @@
571 }
572
573 func (c *DeleteKeysCommand) Init(args []string) error {
574+ err := c.EnvCommandBase.Init()
575+ if err != nil {
576+ return err
577+ }
578 switch len(args) {
579 case 0:
580 return errors.New("no ssh key id specified")
581
582=== modified file 'cmd/juju/authorisedkeys_import.go'
583--- cmd/juju/authorisedkeys_import.go 2013-12-13 06:53:01 +0000
584+++ cmd/juju/authorisedkeys_import.go 2014-04-01 14:32:48 +0000
585@@ -10,6 +10,7 @@
586 "launchpad.net/gnuflag"
587
588 "launchpad.net/juju-core/cmd"
589+ "launchpad.net/juju-core/cmd/envcmd"
590 "launchpad.net/juju-core/juju"
591 )
592
593@@ -20,7 +21,7 @@
594
595 // ImportKeysCommand is used to add new authorized ssh keys for a user.
596 type ImportKeysCommand struct {
597- cmd.EnvCommandBase
598+ envcmd.EnvCommandBase
599 user string
600 sshKeyIds []string
601 }
602@@ -35,6 +36,10 @@
603 }
604
605 func (c *ImportKeysCommand) Init(args []string) error {
606+ err := c.EnvCommandBase.Init()
607+ if err != nil {
608+ return err
609+ }
610 switch len(args) {
611 case 0:
612 return errors.New("no ssh key id specified")
613
614=== modified file 'cmd/juju/authorisedkeys_list.go'
615--- cmd/juju/authorisedkeys_list.go 2013-12-12 22:14:52 +0000
616+++ cmd/juju/authorisedkeys_list.go 2014-04-01 14:32:48 +0000
617@@ -10,6 +10,7 @@
618 "launchpad.net/gnuflag"
619
620 "launchpad.net/juju-core/cmd"
621+ "launchpad.net/juju-core/cmd/envcmd"
622 "launchpad.net/juju-core/juju"
623 "launchpad.net/juju-core/utils/ssh"
624 )
625@@ -22,7 +23,7 @@
626
627 // ListKeysCommand is used to list the authorized ssh keys.
628 type ListKeysCommand struct {
629- cmd.EnvCommandBase
630+ envcmd.EnvCommandBase
631 showFullKey bool
632 user string
633 }
634@@ -41,6 +42,14 @@
635 f.StringVar(&c.user, "user", "admin", "the user for which to list the keys")
636 }
637
638+func (c *ListKeysCommand) Init(args []string) error {
639+ err := c.EnvCommandBase.Init()
640+ if err != nil {
641+ return err
642+ }
643+ return cmd.CheckEmpty(args)
644+}
645+
646 func (c *ListKeysCommand) Run(context *cmd.Context) error {
647 client, err := juju.NewKeyManagerClient(c.EnvName)
648 if err != nil {
649
650=== modified file 'cmd/juju/bootstrap.go'
651--- cmd/juju/bootstrap.go 2014-03-26 03:30:35 +0000
652+++ cmd/juju/bootstrap.go 2014-04-01 14:32:48 +0000
653@@ -12,6 +12,7 @@
654
655 "launchpad.net/juju-core/charm"
656 "launchpad.net/juju-core/cmd"
657+ "launchpad.net/juju-core/cmd/envcmd"
658 "launchpad.net/juju-core/constraints"
659 "launchpad.net/juju-core/environs/bootstrap"
660 "launchpad.net/juju-core/environs/imagemetadata"
661@@ -58,7 +59,7 @@
662 // BootstrapCommand is responsible for launching the first machine in a juju
663 // environment, and setting up everything necessary to continue working.
664 type BootstrapCommand struct {
665- cmd.EnvCommandBase
666+ envcmd.EnvCommandBase
667 Constraints constraints.Value
668 UploadTools bool
669 Series []string
670@@ -82,6 +83,10 @@
671 }
672
673 func (c *BootstrapCommand) Init(args []string) (err error) {
674+ err = c.EnvCommandBase.Init()
675+ if err != nil {
676+ return
677+ }
678 if len(c.Series) > 0 && !c.UploadTools {
679 return fmt.Errorf("--series requires --upload-tools")
680 }
681
682=== modified file 'cmd/juju/bootstrap_test.go'
683--- cmd/juju/bootstrap_test.go 2014-03-25 08:47:50 +0000
684+++ cmd/juju/bootstrap_test.go 2014-04-01 14:32:48 +0000
685@@ -374,8 +374,7 @@
686 ctx2 := coretesting.Context(c)
687 code2 := cmd.Main(&BootstrapCommand{}, ctx2, nil)
688 c.Check(code2, gc.Equals, 1)
689- expectedErrText := "Bootstrap failed, destroying environment\n"
690- expectedErrText += "error: environment is already bootstrapped\n"
691+ expectedErrText := "error: environment is already bootstrapped\n"
692 c.Check(coretesting.Stderr(ctx2), gc.Equals, expectedErrText)
693 c.Check(coretesting.Stdout(ctx2), gc.Equals, "")
694 }
695@@ -542,8 +541,7 @@
696 code := cmd.Main(&BootstrapCommand{}, context, nil)
697 c.Assert(code, gc.Equals, 1)
698 errText := context.Stderr.(*bytes.Buffer).String()
699- expectedErrText := "Bootstrap failed, destroying environment\n"
700- expectedErrText += "error: cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment(.|\n)*"
701+ expectedErrText := "error: cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment(.|\n)*"
702 c.Assert(errText, gc.Matches, expectedErrText)
703 }
704
705@@ -559,7 +557,6 @@
706 c.Assert(code, gc.Equals, 1)
707 errText := context.Stderr.(*bytes.Buffer).String()
708 expectedErrText := "uploading tools for series \\[precise raring\\]\n"
709- expectedErrText += "Bootstrap failed, destroying environment\n"
710 expectedErrText += "error: cannot upload bootstrap tools: an error\n"
711 c.Assert(errText, gc.Matches, expectedErrText)
712 }
713
714=== modified file 'cmd/juju/cmd_test.go'
715--- cmd/juju/cmd_test.go 2014-03-24 10:42:51 +0000
716+++ cmd/juju/cmd_test.go 2014-04-01 14:32:48 +0000
717@@ -97,7 +97,7 @@
718 c.Logf("test %d", i)
719 com, args := cmdFunc()
720 testInit(c, com, args, "")
721- assertConnName(c, com, "")
722+ assertConnName(c, com, "peckham")
723
724 com, args = cmdFunc()
725 testInit(c, com, append(args, "-e", "walthamstow"), "")
726@@ -192,6 +192,7 @@
727 if com.RepoPath == "" {
728 com.RepoPath = "/path/to/repo"
729 }
730+ com.EnvCommandBase.EnvName = "peckham"
731 }
732
733 func initDeployCommand(args ...string) (*DeployCommand, error) {
734
735=== modified file 'cmd/juju/constraints.go'
736--- cmd/juju/constraints.go 2013-12-17 18:21:26 +0000
737+++ cmd/juju/constraints.go 2014-04-01 14:32:48 +0000
738@@ -9,6 +9,7 @@
739 "launchpad.net/gnuflag"
740
741 "launchpad.net/juju-core/cmd"
742+ "launchpad.net/juju-core/cmd/envcmd"
743 "launchpad.net/juju-core/constraints"
744 "launchpad.net/juju-core/juju"
745 "launchpad.net/juju-core/names"
746@@ -51,7 +52,7 @@
747
748 // GetConstraintsCommand shows the constraints for a service or environment.
749 type GetConstraintsCommand struct {
750- cmd.EnvCommandBase
751+ envcmd.EnvCommandBase
752 ServiceName string
753 out cmd.Output
754 }
755@@ -79,6 +80,10 @@
756 }
757
758 func (c *GetConstraintsCommand) Init(args []string) error {
759+ err := c.EnvCommandBase.Init()
760+ if err != nil {
761+ return err
762+ }
763 if len(args) > 0 {
764 if !names.IsService(args[0]) {
765 return fmt.Errorf("invalid service name %q", args[0])
766@@ -129,7 +134,7 @@
767
768 // SetConstraintsCommand shows the constraints for a service or environment.
769 type SetConstraintsCommand struct {
770- cmd.EnvCommandBase
771+ envcmd.EnvCommandBase
772 ServiceName string
773 Constraints constraints.Value
774 }
775@@ -150,6 +155,10 @@
776 }
777
778 func (c *SetConstraintsCommand) Init(args []string) (err error) {
779+ err = c.EnvCommandBase.Init()
780+ if err != nil {
781+ return
782+ }
783 if c.ServiceName != "" && !names.IsService(c.ServiceName) {
784 return fmt.Errorf("invalid service name %q", c.ServiceName)
785 }
786
787=== modified file 'cmd/juju/debuglog_test.go'
788--- cmd/juju/debuglog_test.go 2013-08-13 07:37:58 +0000
789+++ cmd/juju/debuglog_test.go 2014-04-01 14:32:48 +0000
790@@ -36,7 +36,7 @@
791 // debug-log is implemented by invoking juju ssh with the correct arguments.
792 // This test helper checks for the expected invocation.
793 func (s *DebugLogSuite) assertDebugLogInvokesSSHCommand(c *gc.C, expected string, args ...string) {
794- defer testing.MakeEmptyFakeHome(c).Restore()
795+ defer testing.MakeSampleHome(c).Restore()
796 debugLogCmd, err := runDebugLog(c, args...)
797 c.Assert(err, gc.IsNil)
798 debugCmd := debugLogCmd.sshCmd.(*dummySSHCommand)
799@@ -65,7 +65,7 @@
800 }
801
802 func (s *DebugLogSuite) TestDebugLogValidation(c *gc.C) {
803- defer testing.MakeEmptyFakeHome(c).Restore()
804+ defer testing.MakeSampleHome(c).Restore()
805 _, err := runDebugLog(c, "-n", "0")
806 c.Assert(err, gc.ErrorMatches, "invalid value \"0\" for flag -n: invalid number of lines")
807 _, err = runDebugLog(c, "-n", "-1")
808
809=== modified file 'cmd/juju/deploy.go'
810--- cmd/juju/deploy.go 2014-03-31 08:55:49 +0000
811+++ cmd/juju/deploy.go 2014-04-01 14:32:48 +0000
812@@ -13,6 +13,7 @@
813
814 "launchpad.net/juju-core/charm"
815 "launchpad.net/juju-core/cmd"
816+ "launchpad.net/juju-core/cmd/envcmd"
817 "launchpad.net/juju-core/constraints"
818 "launchpad.net/juju-core/environs"
819 "launchpad.net/juju-core/environs/config"
820@@ -24,7 +25,7 @@
821 )
822
823 type DeployCommand struct {
824- cmd.EnvCommandBase
825+ envcmd.EnvCommandBase
826 UnitCommandBase
827 CharmName string
828 ServiceName string
829@@ -109,6 +110,10 @@
830 }
831
832 func (c *DeployCommand) Init(args []string) error {
833+ err := c.EnvCommandBase.Init()
834+ if err != nil {
835+ return err
836+ }
837 switch len(args) {
838 case 2:
839 if !names.IsService(args[1]) {
840
841=== modified file 'cmd/juju/destroymachine.go'
842--- cmd/juju/destroymachine.go 2014-02-25 22:19:30 +0000
843+++ cmd/juju/destroymachine.go 2014-04-01 14:32:48 +0000
844@@ -9,6 +9,7 @@
845 "launchpad.net/gnuflag"
846
847 "launchpad.net/juju-core/cmd"
848+ "launchpad.net/juju-core/cmd/envcmd"
849 "launchpad.net/juju-core/juju"
850 "launchpad.net/juju-core/names"
851 "launchpad.net/juju-core/state/api/params"
852@@ -17,7 +18,7 @@
853
854 // DestroyMachineCommand causes an existing machine to be destroyed.
855 type DestroyMachineCommand struct {
856- cmd.EnvCommandBase
857+ envcmd.EnvCommandBase
858 MachineIds []string
859 Force bool
860 }
861@@ -45,6 +46,10 @@
862 }
863
864 func (c *DestroyMachineCommand) Init(args []string) error {
865+ err := c.EnvCommandBase.Init()
866+ if err != nil {
867+ return err
868+ }
869 if len(args) == 0 {
870 return fmt.Errorf("no machines specified")
871 }
872
873=== modified file 'cmd/juju/destroyrelation.go'
874--- cmd/juju/destroyrelation.go 2013-11-05 04:12:48 +0000
875+++ cmd/juju/destroyrelation.go 2014-04-01 14:32:48 +0000
876@@ -7,12 +7,13 @@
877 "fmt"
878
879 "launchpad.net/juju-core/cmd"
880+ "launchpad.net/juju-core/cmd/envcmd"
881 "launchpad.net/juju-core/juju"
882 )
883
884 // DestroyRelationCommand causes an existing service relation to be shut down.
885 type DestroyRelationCommand struct {
886- cmd.EnvCommandBase
887+ envcmd.EnvCommandBase
888 Endpoints []string
889 }
890
891@@ -26,6 +27,10 @@
892 }
893
894 func (c *DestroyRelationCommand) Init(args []string) error {
895+ err := c.EnvCommandBase.Init()
896+ if err != nil {
897+ return err
898+ }
899 if len(args) != 2 {
900 return fmt.Errorf("a relation must involve two services")
901 }
902
903=== modified file 'cmd/juju/destroyservice.go'
904--- cmd/juju/destroyservice.go 2014-02-25 22:19:30 +0000
905+++ cmd/juju/destroyservice.go 2014-04-01 14:32:48 +0000
906@@ -7,13 +7,14 @@
907 "fmt"
908
909 "launchpad.net/juju-core/cmd"
910+ "launchpad.net/juju-core/cmd/envcmd"
911 "launchpad.net/juju-core/juju"
912 "launchpad.net/juju-core/names"
913 )
914
915 // DestroyServiceCommand causes an existing service to be destroyed.
916 type DestroyServiceCommand struct {
917- cmd.EnvCommandBase
918+ envcmd.EnvCommandBase
919 ServiceName string
920 }
921
922@@ -28,6 +29,10 @@
923 }
924
925 func (c *DestroyServiceCommand) Init(args []string) error {
926+ err := c.EnvCommandBase.Init()
927+ if err != nil {
928+ return err
929+ }
930 if len(args) == 0 {
931 return fmt.Errorf("no service specified")
932 }
933
934=== modified file 'cmd/juju/destroyunit.go'
935--- cmd/juju/destroyunit.go 2013-12-04 15:34:47 +0000
936+++ cmd/juju/destroyunit.go 2014-04-01 14:32:48 +0000
937@@ -8,13 +8,14 @@
938 "fmt"
939
940 "launchpad.net/juju-core/cmd"
941+ "launchpad.net/juju-core/cmd/envcmd"
942 "launchpad.net/juju-core/juju"
943 "launchpad.net/juju-core/names"
944 )
945
946 // DestroyUnitCommand is responsible for destroying service units.
947 type DestroyUnitCommand struct {
948- cmd.EnvCommandBase
949+ envcmd.EnvCommandBase
950 UnitNames []string
951 }
952
953@@ -28,6 +29,10 @@
954 }
955
956 func (c *DestroyUnitCommand) Init(args []string) error {
957+ err := c.EnvCommandBase.Init()
958+ if err != nil {
959+ return err
960+ }
961 c.UnitNames = args
962 if len(c.UnitNames) == 0 {
963 return errors.New("no units specified")
964
965=== modified file 'cmd/juju/endpoint.go'
966--- cmd/juju/endpoint.go 2013-09-30 17:23:28 +0000
967+++ cmd/juju/endpoint.go 2014-04-01 14:32:48 +0000
968@@ -7,13 +7,14 @@
969 "launchpad.net/gnuflag"
970
971 "launchpad.net/juju-core/cmd"
972+ "launchpad.net/juju-core/cmd/envcmd"
973 "launchpad.net/juju-core/environs"
974 "launchpad.net/juju-core/environs/configstore"
975 )
976
977 // EndpointCommand returns the API endpoints
978 type EndpointCommand struct {
979- cmd.EnvCommandBase
980+ envcmd.EnvCommandBase
981 out cmd.Output
982 }
983
984@@ -37,6 +38,10 @@
985 }
986
987 func (c *EndpointCommand) Init(args []string) error {
988+ err := c.EnvCommandBase.Init()
989+ if err != nil {
990+ return err
991+ }
992 return cmd.CheckEmpty(args)
993 }
994
995
996=== modified file 'cmd/juju/ensureavailability.go'
997--- cmd/juju/ensureavailability.go 2014-03-28 09:55:10 +0000
998+++ cmd/juju/ensureavailability.go 2014-04-01 14:32:48 +0000
999@@ -9,12 +9,13 @@
1000 "launchpad.net/gnuflag"
1001
1002 "launchpad.net/juju-core/cmd"
1003+ "launchpad.net/juju-core/cmd/envcmd"
1004 "launchpad.net/juju-core/constraints"
1005 "launchpad.net/juju-core/juju"
1006 )
1007
1008 type EnsureAvailabilityCommand struct {
1009- cmd.EnvCommandBase
1010+ envcmd.EnvCommandBase
1011 NumStateServers int
1012 // If specified, use this series for newly created machines,
1013 // else use the environment's default-series
1014@@ -63,6 +64,10 @@
1015 }
1016
1017 func (c *EnsureAvailabilityCommand) Init(args []string) error {
1018+ err := c.EnvCommandBase.Init()
1019+ if err != nil {
1020+ return err
1021+ }
1022 if c.NumStateServers%2 != 1 || c.NumStateServers <= 0 {
1023 return fmt.Errorf("must specify a number of state servers odd and greater than zero")
1024 }
1025
1026=== modified file 'cmd/juju/environment.go'
1027--- cmd/juju/environment.go 2014-03-26 06:32:05 +0000
1028+++ cmd/juju/environment.go 2014-04-01 14:32:48 +0000
1029@@ -10,6 +10,7 @@
1030 "launchpad.net/gnuflag"
1031
1032 "launchpad.net/juju-core/cmd"
1033+ "launchpad.net/juju-core/cmd/envcmd"
1034 "launchpad.net/juju-core/juju"
1035 "launchpad.net/juju-core/state/api/params"
1036 )
1037@@ -17,7 +18,7 @@
1038 // GetEnvironmentCommand is able to output either the entire environment or
1039 // the requested value in a format of the user's choosing.
1040 type GetEnvironmentCommand struct {
1041- cmd.EnvCommandBase
1042+ envcmd.EnvCommandBase
1043 key string
1044 out cmd.Output
1045 }
1046@@ -50,6 +51,10 @@
1047 }
1048
1049 func (c *GetEnvironmentCommand) Init(args []string) (err error) {
1050+ err = c.EnvCommandBase.Init()
1051+ if err != nil {
1052+ return
1053+ }
1054 c.key, err = cmd.ZeroOrOneArgs(args)
1055 return
1056 }
1057@@ -105,7 +110,7 @@
1058
1059 // SetEnvironment
1060 type SetEnvironmentCommand struct {
1061- cmd.EnvCommandBase
1062+ envcmd.EnvCommandBase
1063 values attributes
1064 }
1065
1066@@ -127,6 +132,10 @@
1067 // SetFlags handled entirely by cmd.EnvCommandBase
1068
1069 func (c *SetEnvironmentCommand) Init(args []string) (err error) {
1070+ err = c.EnvCommandBase.Init()
1071+ if err != nil {
1072+ return
1073+ }
1074 if len(args) == 0 {
1075 return fmt.Errorf("No key, value pairs specified")
1076 }
1077@@ -184,7 +193,7 @@
1078
1079 // UnsetEnvironment
1080 type UnsetEnvironmentCommand struct {
1081- cmd.EnvCommandBase
1082+ envcmd.EnvCommandBase
1083 keys []string
1084 }
1085
1086@@ -208,6 +217,10 @@
1087 }
1088
1089 func (c *UnsetEnvironmentCommand) Init(args []string) (err error) {
1090+ err = c.EnvCommandBase.Init()
1091+ if err != nil {
1092+ return
1093+ }
1094 if len(args) == 0 {
1095 return fmt.Errorf("No keys specified")
1096 }
1097
1098=== modified file 'cmd/juju/expose.go'
1099--- cmd/juju/expose.go 2013-11-01 09:09:43 +0000
1100+++ cmd/juju/expose.go 2014-04-01 14:32:48 +0000
1101@@ -7,12 +7,13 @@
1102 "errors"
1103
1104 "launchpad.net/juju-core/cmd"
1105+ "launchpad.net/juju-core/cmd/envcmd"
1106 "launchpad.net/juju-core/juju"
1107 )
1108
1109 // ExposeCommand is responsible exposing services.
1110 type ExposeCommand struct {
1111- cmd.EnvCommandBase
1112+ envcmd.EnvCommandBase
1113 ServiceName string
1114 }
1115
1116@@ -25,6 +26,10 @@
1117 }
1118
1119 func (c *ExposeCommand) Init(args []string) error {
1120+ err := c.EnvCommandBase.Init()
1121+ if err != nil {
1122+ return err
1123+ }
1124 if len(args) == 0 {
1125 return errors.New("no service name specified")
1126 }
1127
1128=== modified file 'cmd/juju/get.go'
1129--- cmd/juju/get.go 2013-09-30 20:58:11 +0000
1130+++ cmd/juju/get.go 2014-04-01 14:32:48 +0000
1131@@ -9,12 +9,13 @@
1132 "launchpad.net/gnuflag"
1133
1134 "launchpad.net/juju-core/cmd"
1135+ "launchpad.net/juju-core/cmd/envcmd"
1136 "launchpad.net/juju-core/juju"
1137 )
1138
1139 // GetCommand retrieves the configuration of a service.
1140 type GetCommand struct {
1141- cmd.EnvCommandBase
1142+ envcmd.EnvCommandBase
1143 ServiceName string
1144 out cmd.Output
1145 }
1146@@ -36,6 +37,10 @@
1147 }
1148
1149 func (c *GetCommand) Init(args []string) error {
1150+ err := c.EnvCommandBase.Init()
1151+ if err != nil {
1152+ return err
1153+ }
1154 // TODO(dfc) add --schema-only
1155 if len(args) == 0 {
1156 return errors.New("no service name specified")
1157
1158=== modified file 'cmd/juju/plugin.go'
1159--- cmd/juju/plugin.go 2014-02-17 12:57:06 +0000
1160+++ cmd/juju/plugin.go 2014-04-01 14:32:48 +0000
1161@@ -16,6 +16,7 @@
1162 "launchpad.net/gnuflag"
1163
1164 "launchpad.net/juju-core/cmd"
1165+ "launchpad.net/juju-core/cmd/envcmd"
1166 "launchpad.net/juju-core/juju/osenv"
1167 "launchpad.net/juju-core/log"
1168 )
1169@@ -72,7 +73,7 @@
1170 }
1171
1172 type PluginCommand struct {
1173- cmd.EnvCommandBase
1174+ envcmd.EnvCommandBase
1175 name string
1176 args []string
1177 }
1178@@ -85,7 +86,7 @@
1179
1180 func (c *PluginCommand) Init(args []string) error {
1181 c.args = args
1182- return nil
1183+ return c.EnvCommandBase.Init()
1184 }
1185
1186 func (c *PluginCommand) SetFlags(f *gnuflag.FlagSet) {
1187
1188=== modified file 'cmd/juju/publish.go'
1189--- cmd/juju/publish.go 2014-03-18 05:08:25 +0000
1190+++ cmd/juju/publish.go 2014-04-01 14:32:48 +0000
1191@@ -14,11 +14,12 @@
1192 "launchpad.net/juju-core/bzr"
1193 "launchpad.net/juju-core/charm"
1194 "launchpad.net/juju-core/cmd"
1195+ "launchpad.net/juju-core/cmd/envcmd"
1196 "launchpad.net/juju-core/log"
1197 )
1198
1199 type PublishCommand struct {
1200- cmd.EnvCommandBase
1201+ envcmd.EnvCommandBase
1202 URL string
1203 CharmPath string
1204
1205@@ -60,6 +61,10 @@
1206 }
1207
1208 func (c *PublishCommand) Init(args []string) error {
1209+ err := c.EnvCommandBase.Init()
1210+ if err != nil {
1211+ return err
1212+ }
1213 if len(args) == 0 {
1214 return nil
1215 }
1216
1217=== modified file 'cmd/juju/publish_test.go'
1218--- cmd/juju/publish_test.go 2014-03-18 05:08:25 +0000
1219+++ cmd/juju/publish_test.go 2014-04-01 14:32:48 +0000
1220@@ -75,11 +75,9 @@
1221 func (s *PublishSuite) SetUpTest(c *gc.C) {
1222 s.LoggingSuite.SetUpTest(c)
1223 s.HTTPSuite.SetUpTest(c)
1224- s.home = testing.MakeFakeHomeWithFiles(c, []testing.TestFile{
1225- {
1226- Name: ".bazaar/bazaar.conf",
1227- Data: "[DEFAULT]\nemail = Test <testing@testing.invalid>\n",
1228- },
1229+ s.home = testing.MakeSampleHome(c, testing.TestFile{
1230+ Name: ".bazaar/bazaar.conf",
1231+ Data: "[DEFAULT]\nemail = Test <testing@testing.invalid>\n",
1232 })
1233
1234 s.dir = c.MkDir()
1235
1236=== added file 'cmd/juju/removeuser.go'
1237--- cmd/juju/removeuser.go 1970-01-01 00:00:00 +0000
1238+++ cmd/juju/removeuser.go 2014-04-01 14:32:48 +0000
1239@@ -0,0 +1,55 @@
1240+// Copyright 2014 Canonical Ltd.
1241+// Licensed under the AGPLv3, see LICENCE file for details.
1242+
1243+package main
1244+
1245+import (
1246+ "errors"
1247+
1248+ "launchpad.net/juju-core/cmd"
1249+ "launchpad.net/juju-core/cmd/envcmd"
1250+ "launchpad.net/juju-core/juju"
1251+)
1252+
1253+const removeUserDoc = `
1254+Remove users from an existing environment
1255+
1256+Examples:
1257+ juju remove-user foobar
1258+`
1259+
1260+type RemoveUserCommand struct {
1261+ envcmd.EnvCommandBase
1262+ User string
1263+}
1264+
1265+func (c *RemoveUserCommand) Info() *cmd.Info {
1266+ return &cmd.Info{
1267+ Name: "remove-user",
1268+ Args: "<username>",
1269+ Purpose: "removes a user",
1270+ Doc: removeUserDoc,
1271+ }
1272+}
1273+
1274+func (c *RemoveUserCommand) Init(args []string) error {
1275+ err := c.EnvCommandBase.Init()
1276+ if err != nil {
1277+ return err
1278+ }
1279+ if len(args) == 0 {
1280+ return errors.New("no username supplied")
1281+ }
1282+ c.User = args[0]
1283+
1284+ return cmd.CheckEmpty(args[1:])
1285+}
1286+
1287+func (c *RemoveUserCommand) Run(_ *cmd.Context) error {
1288+ client, err := juju.NewUserManagerClient(c.EnvName)
1289+ if err != nil {
1290+ return err
1291+ }
1292+ defer client.Close()
1293+ return client.RemoveUser(c.User)
1294+}
1295
1296=== added file 'cmd/juju/removeuser_test.go'
1297--- cmd/juju/removeuser_test.go 1970-01-01 00:00:00 +0000
1298+++ cmd/juju/removeuser_test.go 2014-04-01 14:32:48 +0000
1299@@ -0,0 +1,35 @@
1300+// Copyright 2014 Canonical Ltd.
1301+// Licensed under the AGPLv3, see LICENCE file for details.
1302+
1303+package main
1304+
1305+import (
1306+ gc "launchpad.net/gocheck"
1307+
1308+ jujutesting "launchpad.net/juju-core/juju/testing"
1309+ "launchpad.net/juju-core/testing"
1310+)
1311+
1312+type RemoveUserSuite struct {
1313+ jujutesting.RepoSuite
1314+}
1315+
1316+var _ = gc.Suite(&RemoveUserSuite{})
1317+
1318+func (s *RemoveUserSuite) TestRemoveUser(c *gc.C) {
1319+ _, err := testing.RunCommand(c, &AddUserCommand{}, []string{"foobar", "password"})
1320+ c.Assert(err, gc.IsNil)
1321+
1322+ _, err = testing.RunCommand(c, &RemoveUserCommand{}, []string{"foobar"})
1323+ c.Assert(err, gc.IsNil)
1324+}
1325+
1326+func (s *RemoveUserSuite) TestTooManyArgs(c *gc.C) {
1327+ _, err := testing.RunCommand(c, &RemoveUserCommand{}, []string{"foobar", "password"})
1328+ c.Assert(err, gc.ErrorMatches, `unrecognized args: \["password"\]`)
1329+}
1330+
1331+func (s *RemoveUserSuite) TestNotEnoughArgs(c *gc.C) {
1332+ _, err := testing.RunCommand(c, &RemoveUserCommand{}, []string{})
1333+ c.Assert(err, gc.ErrorMatches, `no username supplied`)
1334+}
1335
1336=== modified file 'cmd/juju/resolved.go'
1337--- cmd/juju/resolved.go 2013-11-01 09:26:22 +0000
1338+++ cmd/juju/resolved.go 2014-04-01 14:32:48 +0000
1339@@ -9,13 +9,14 @@
1340 "launchpad.net/gnuflag"
1341
1342 "launchpad.net/juju-core/cmd"
1343+ "launchpad.net/juju-core/cmd/envcmd"
1344 "launchpad.net/juju-core/juju"
1345 "launchpad.net/juju-core/names"
1346 )
1347
1348 // ResolvedCommand marks a unit in an error state as ready to continue.
1349 type ResolvedCommand struct {
1350- cmd.EnvCommandBase
1351+ envcmd.EnvCommandBase
1352 UnitName string
1353 Retry bool
1354 }
1355@@ -35,6 +36,10 @@
1356 }
1357
1358 func (c *ResolvedCommand) Init(args []string) error {
1359+ err := c.EnvCommandBase.Init()
1360+ if err != nil {
1361+ return err
1362+ }
1363 if len(args) > 0 {
1364 c.UnitName = args[0]
1365 if !names.IsUnit(c.UnitName) {
1366
1367=== modified file 'cmd/juju/retryprovisioning.go'
1368--- cmd/juju/retryprovisioning.go 2014-03-26 12:43:56 +0000
1369+++ cmd/juju/retryprovisioning.go 2014-04-01 14:32:48 +0000
1370@@ -7,6 +7,7 @@
1371 "fmt"
1372
1373 "launchpad.net/juju-core/cmd"
1374+ "launchpad.net/juju-core/cmd/envcmd"
1375 "launchpad.net/juju-core/juju"
1376 "launchpad.net/juju-core/names"
1377 )
1378@@ -14,7 +15,7 @@
1379 // RetryProvisioningCommand updates machines' error status to tell
1380 // the provisoner that it should try to re-provision the machine.
1381 type RetryProvisioningCommand struct {
1382- cmd.EnvCommandBase
1383+ envcmd.EnvCommandBase
1384 Machines []string
1385 }
1386
1387
1388=== modified file 'cmd/juju/run.go'
1389--- cmd/juju/run.go 2014-01-12 20:28:39 +0000
1390+++ cmd/juju/run.go 2014-04-01 14:32:48 +0000
1391@@ -14,6 +14,7 @@
1392 "launchpad.net/gnuflag"
1393
1394 "launchpad.net/juju-core/cmd"
1395+ "launchpad.net/juju-core/cmd/envcmd"
1396 "launchpad.net/juju-core/juju"
1397 "launchpad.net/juju-core/names"
1398 "launchpad.net/juju-core/state/api/params"
1399@@ -21,7 +22,7 @@
1400
1401 // RunCommand is responsible for running arbitrary commands on remote machines.
1402 type RunCommand struct {
1403- cmd.EnvCommandBase
1404+ envcmd.EnvCommandBase
1405 out cmd.Output
1406 all bool
1407 timeout time.Duration
1408@@ -79,6 +80,10 @@
1409 }
1410
1411 func (c *RunCommand) Init(args []string) error {
1412+ err := c.EnvCommandBase.Init()
1413+ if err != nil {
1414+ return err
1415+ }
1416 if len(args) == 0 {
1417 return errors.New("no commands specified")
1418 }
1419
1420=== modified file 'cmd/juju/run_test.go'
1421--- cmd/juju/run_test.go 2014-03-13 07:54:56 +0000
1422+++ cmd/juju/run_test.go 2014-04-01 14:32:48 +0000
1423@@ -321,7 +321,7 @@
1424 message: "smart (default)",
1425 stdout: "stdout\n",
1426 stderr: "stderr\n",
1427- errorMatch: "rc: 42",
1428+ errorMatch: "subprocess encountered error code 42",
1429 }, {
1430 message: "yaml output",
1431 format: "yaml",
1432
1433=== modified file 'cmd/juju/set.go'
1434--- cmd/juju/set.go 2013-12-17 18:21:26 +0000
1435+++ cmd/juju/set.go 2014-04-01 14:32:48 +0000
1436@@ -11,13 +11,14 @@
1437 "launchpad.net/gnuflag"
1438
1439 "launchpad.net/juju-core/cmd"
1440+ "launchpad.net/juju-core/cmd/envcmd"
1441 "launchpad.net/juju-core/juju"
1442 "launchpad.net/juju-core/state/api/params"
1443 )
1444
1445 // SetCommand updates the configuration of a service.
1446 type SetCommand struct {
1447- cmd.EnvCommandBase
1448+ envcmd.EnvCommandBase
1449 ServiceName string
1450 SettingsStrings map[string]string
1451 SettingsYAML cmd.FileVar
1452@@ -44,6 +45,10 @@
1453 }
1454
1455 func (c *SetCommand) Init(args []string) error {
1456+ err := c.EnvCommandBase.Init()
1457+ if err != nil {
1458+ return err
1459+ }
1460 if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 {
1461 return errors.New("no service name specified")
1462 }
1463
1464=== modified file 'cmd/juju/ssh.go'
1465--- cmd/juju/ssh.go 2014-03-20 02:30:15 +0000
1466+++ cmd/juju/ssh.go 2014-04-01 14:32:48 +0000
1467@@ -9,6 +9,7 @@
1468 "time"
1469
1470 "launchpad.net/juju-core/cmd"
1471+ "launchpad.net/juju-core/cmd/envcmd"
1472 "launchpad.net/juju-core/instance"
1473 "launchpad.net/juju-core/juju"
1474 "launchpad.net/juju-core/names"
1475@@ -25,7 +26,7 @@
1476
1477 // SSHCommon provides common methods for SSHCommand, SCPCommand and DebugHooksCommand.
1478 type SSHCommon struct {
1479- cmd.EnvCommandBase
1480+ envcmd.EnvCommandBase
1481 Target string
1482 Args []string
1483 apiClient *api.Client
1484@@ -68,6 +69,10 @@
1485 }
1486
1487 func (c *SSHCommand) Init(args []string) error {
1488+ err := c.EnvCommandBase.Init()
1489+ if err != nil {
1490+ return err
1491+ }
1492 if len(args) == 0 {
1493 return errors.New("no target name specified")
1494 }
1495
1496=== modified file 'cmd/juju/status.go'
1497--- cmd/juju/status.go 2014-03-26 12:42:03 +0000
1498+++ cmd/juju/status.go 2014-04-01 14:32:48 +0000
1499@@ -10,6 +10,7 @@
1500 "launchpad.net/gnuflag"
1501
1502 "launchpad.net/juju-core/cmd"
1503+ "launchpad.net/juju-core/cmd/envcmd"
1504 "launchpad.net/juju-core/instance"
1505 "launchpad.net/juju-core/juju"
1506 "launchpad.net/juju-core/state/api"
1507@@ -18,7 +19,7 @@
1508 )
1509
1510 type StatusCommand struct {
1511- cmd.EnvCommandBase
1512+ envcmd.EnvCommandBase
1513 out cmd.Output
1514 patterns []string
1515 }
1516@@ -57,7 +58,7 @@
1517
1518 func (c *StatusCommand) Init(args []string) error {
1519 c.patterns = args
1520- return nil
1521+ return c.EnvCommandBase.Init()
1522 }
1523
1524 var connectionError = `Unable to connect to environment "%s".
1525
1526=== modified file 'cmd/juju/switch.go'
1527--- cmd/juju/switch.go 2014-01-29 15:57:25 +0000
1528+++ cmd/juju/switch.go 2014-04-01 14:32:48 +0000
1529@@ -12,6 +12,7 @@
1530 "launchpad.net/gnuflag"
1531
1532 "launchpad.net/juju-core/cmd"
1533+ "launchpad.net/juju-core/cmd/envcmd"
1534 "launchpad.net/juju-core/environs"
1535 )
1536
1537@@ -96,7 +97,7 @@
1538 }
1539 }
1540
1541- currentEnv := cmd.ReadCurrentEnvironment()
1542+ currentEnv := envcmd.ReadCurrentEnvironment()
1543 if currentEnv == "" {
1544 currentEnv = environments.Default
1545 }
1546@@ -114,7 +115,7 @@
1547 if !validEnvironmentName(c.EnvName, names) {
1548 return fmt.Errorf("%q is not a name of an existing defined environment", c.EnvName)
1549 }
1550- if err := cmd.WriteCurrentEnvironment(c.EnvName); err != nil {
1551+ if err := envcmd.WriteCurrentEnvironment(c.EnvName); err != nil {
1552 return err
1553 }
1554 if currentEnv == "" {
1555
1556=== modified file 'cmd/juju/switch_test.go'
1557--- cmd/juju/switch_test.go 2014-01-29 13:46:37 +0000
1558+++ cmd/juju/switch_test.go 2014-04-01 14:32:48 +0000
1559@@ -8,7 +8,7 @@
1560
1561 gc "launchpad.net/gocheck"
1562
1563- "launchpad.net/juju-core/cmd"
1564+ "launchpad.net/juju-core/cmd/envcmd"
1565 _ "launchpad.net/juju-core/juju"
1566 "launchpad.net/juju-core/testing"
1567 )
1568@@ -69,7 +69,7 @@
1569 context, err := testing.RunCommand(c, &SwitchCommand{}, []string{"erewhemos-2"})
1570 c.Assert(err, gc.IsNil)
1571 c.Assert(testing.Stdout(context), gc.Equals, "erewhemos -> erewhemos-2\n")
1572- c.Assert(cmd.ReadCurrentEnvironment(), gc.Equals, "erewhemos-2")
1573+ c.Assert(envcmd.ReadCurrentEnvironment(), gc.Equals, "erewhemos-2")
1574 }
1575
1576 func (*SwitchSimpleSuite) TestSettingToUnknown(c *gc.C) {
1577
1578=== modified file 'cmd/juju/synctools.go'
1579--- cmd/juju/synctools.go 2014-03-10 00:45:41 +0000
1580+++ cmd/juju/synctools.go 2014-04-01 14:32:48 +0000
1581@@ -8,6 +8,7 @@
1582 "launchpad.net/gnuflag"
1583
1584 "launchpad.net/juju-core/cmd"
1585+ "launchpad.net/juju-core/cmd/envcmd"
1586 "launchpad.net/juju-core/environs/filestorage"
1587 "launchpad.net/juju-core/environs/sync"
1588 "launchpad.net/juju-core/version"
1589@@ -18,7 +19,7 @@
1590 // SyncToolsCommand copies all the tools from the us-east-1 bucket to the local
1591 // bucket.
1592 type SyncToolsCommand struct {
1593- cmd.EnvCommandBase
1594+ envcmd.EnvCommandBase
1595 allVersions bool
1596 versionStr string
1597 majorVersion int
1598@@ -63,6 +64,10 @@
1599 }
1600
1601 func (c *SyncToolsCommand) Init(args []string) error {
1602+ err := c.EnvCommandBase.Init()
1603+ if err != nil {
1604+ return err
1605+ }
1606 if c.destination != "" {
1607 // Override localDir with destination as localDir now replaces destination
1608 c.localDir = c.destination
1609
1610=== modified file 'cmd/juju/unexpose.go'
1611--- cmd/juju/unexpose.go 2013-11-01 09:09:43 +0000
1612+++ cmd/juju/unexpose.go 2014-04-01 14:32:48 +0000
1613@@ -7,12 +7,13 @@
1614 "errors"
1615
1616 "launchpad.net/juju-core/cmd"
1617+ "launchpad.net/juju-core/cmd/envcmd"
1618 "launchpad.net/juju-core/juju"
1619 )
1620
1621 // UnexposeCommand is responsible exposing services.
1622 type UnexposeCommand struct {
1623- cmd.EnvCommandBase
1624+ envcmd.EnvCommandBase
1625 ServiceName string
1626 }
1627
1628@@ -25,6 +26,10 @@
1629 }
1630
1631 func (c *UnexposeCommand) Init(args []string) error {
1632+ err := c.EnvCommandBase.Init()
1633+ if err != nil {
1634+ return err
1635+ }
1636 if len(args) == 0 {
1637 return errors.New("no service name specified")
1638 }
1639
1640=== modified file 'cmd/juju/unset.go'
1641--- cmd/juju/unset.go 2013-12-17 18:21:26 +0000
1642+++ cmd/juju/unset.go 2014-04-01 14:32:48 +0000
1643@@ -10,6 +10,7 @@
1644
1645 "launchpad.net/juju-core/charm"
1646 "launchpad.net/juju-core/cmd"
1647+ "launchpad.net/juju-core/cmd/envcmd"
1648 "launchpad.net/juju-core/juju"
1649 "launchpad.net/juju-core/state/api/params"
1650 )
1651@@ -17,7 +18,7 @@
1652 // UnsetCommand sets configuration values of a service back
1653 // to their default.
1654 type UnsetCommand struct {
1655- cmd.EnvCommandBase
1656+ envcmd.EnvCommandBase
1657 ServiceName string
1658 Options []string
1659 }
1660@@ -42,6 +43,10 @@
1661 }
1662
1663 func (c *UnsetCommand) Init(args []string) error {
1664+ err := c.EnvCommandBase.Init()
1665+ if err != nil {
1666+ return err
1667+ }
1668 if len(args) == 0 {
1669 return errors.New("no service name specified")
1670 }
1671
1672=== modified file 'cmd/juju/upgradecharm.go'
1673--- cmd/juju/upgradecharm.go 2014-03-18 05:08:25 +0000
1674+++ cmd/juju/upgradecharm.go 2014-04-01 14:32:48 +0000
1675@@ -12,6 +12,7 @@
1676
1677 "launchpad.net/juju-core/charm"
1678 "launchpad.net/juju-core/cmd"
1679+ "launchpad.net/juju-core/cmd/envcmd"
1680 "launchpad.net/juju-core/environs/config"
1681 "launchpad.net/juju-core/juju"
1682 "launchpad.net/juju-core/names"
1683@@ -20,7 +21,7 @@
1684
1685 // UpgradeCharm is responsible for upgrading a service's charm.
1686 type UpgradeCharmCommand struct {
1687- cmd.EnvCommandBase
1688+ envcmd.EnvCommandBase
1689 ServiceName string
1690 Force bool
1691 RepoPath string // defaults to JUJU_REPOSITORY
1692@@ -84,6 +85,10 @@
1693 }
1694
1695 func (c *UpgradeCharmCommand) Init(args []string) error {
1696+ err := c.EnvCommandBase.Init()
1697+ if err != nil {
1698+ return err
1699+ }
1700 switch len(args) {
1701 case 1:
1702 if !names.IsService(args[0]) {
1703
1704=== modified file 'cmd/juju/upgradejuju.go'
1705--- cmd/juju/upgradejuju.go 2014-03-18 23:44:18 +0000
1706+++ cmd/juju/upgradejuju.go 2014-04-01 14:32:48 +0000
1707@@ -12,6 +12,7 @@
1708 "launchpad.net/gnuflag"
1709
1710 "launchpad.net/juju-core/cmd"
1711+ "launchpad.net/juju-core/cmd/envcmd"
1712 "launchpad.net/juju-core/environs"
1713 "launchpad.net/juju-core/environs/bootstrap"
1714 "launchpad.net/juju-core/environs/config"
1715@@ -28,7 +29,7 @@
1716
1717 // UpgradeJujuCommand upgrades the agents in a juju installation.
1718 type UpgradeJujuCommand struct {
1719- cmd.EnvCommandBase
1720+ envcmd.EnvCommandBase
1721 vers string
1722 Version version.Number
1723 UploadTools bool
1724@@ -84,6 +85,10 @@
1725 }
1726
1727 func (c *UpgradeJujuCommand) Init(args []string) error {
1728+ err := c.EnvCommandBase.Init()
1729+ if err != nil {
1730+ return err
1731+ }
1732 if c.vers != "" {
1733 vers, err := version.Parse(c.vers)
1734 if err != nil {
1735
1736=== modified file 'cmd/jujud/run_test.go'
1737--- cmd/jujud/run_test.go 2014-03-13 07:54:56 +0000
1738+++ cmd/jujud/run_test.go 2014-04-01 14:32:48 +0000
1739@@ -105,7 +105,7 @@
1740 go func() {
1741 ctx, err := testing.RunCommand(c, &RunCommand{}, params)
1742 c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError)
1743- c.Assert(err, gc.ErrorMatches, "rc: 0")
1744+ c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0")
1745 resultChannel <- ctx
1746 close(resultChannel)
1747 }()
1748@@ -118,7 +118,7 @@
1749
1750 ctx, err := testing.RunCommand(c, &RunCommand{}, []string{"--no-context", "echo done"})
1751 c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError)
1752- c.Assert(err, gc.ErrorMatches, "rc: 0")
1753+ c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 0")
1754 c.Assert(testing.Stdout(ctx), gc.Equals, "done\n")
1755 }
1756
1757@@ -168,7 +168,7 @@
1758
1759 ctx, err := testing.RunCommand(c, &RunCommand{}, []string{"foo", "bar"})
1760 c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue)
1761- c.Assert(err, gc.ErrorMatches, "rc: 42")
1762+ c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 42")
1763 c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout")
1764 c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr")
1765 }
1766
1767=== modified file 'cmd/plugins/juju-metadata/imagemetadata.go'
1768--- cmd/plugins/juju-metadata/imagemetadata.go 2014-03-17 03:45:21 +0000
1769+++ cmd/plugins/juju-metadata/imagemetadata.go 2014-04-01 14:32:48 +0000
1770@@ -11,6 +11,7 @@
1771 "launchpad.net/gnuflag"
1772
1773 "launchpad.net/juju-core/cmd"
1774+ "launchpad.net/juju-core/cmd/envcmd"
1775 "launchpad.net/juju-core/environs"
1776 "launchpad.net/juju-core/environs/config"
1777 "launchpad.net/juju-core/environs/configstore"
1778@@ -23,7 +24,7 @@
1779
1780 // ImageMetadataCommand is used to write out simplestreams image metadata information.
1781 type ImageMetadataCommand struct {
1782- cmd.EnvCommandBase
1783+ envcmd.EnvCommandBase
1784 Dir string
1785 Series string
1786 Arch string
1787
1788=== modified file 'cmd/plugins/juju-metadata/toolsmetadata.go'
1789--- cmd/plugins/juju-metadata/toolsmetadata.go 2014-03-21 03:27:16 +0000
1790+++ cmd/plugins/juju-metadata/toolsmetadata.go 2014-04-01 14:32:48 +0000
1791@@ -10,6 +10,7 @@
1792 "launchpad.net/gnuflag"
1793
1794 "launchpad.net/juju-core/cmd"
1795+ "launchpad.net/juju-core/cmd/envcmd"
1796 "launchpad.net/juju-core/environs/filestorage"
1797 "launchpad.net/juju-core/environs/simplestreams"
1798 "launchpad.net/juju-core/environs/storage"
1799@@ -22,7 +23,7 @@
1800
1801 // ToolsMetadataCommand is used to generate simplestreams metadata for juju tools.
1802 type ToolsMetadataCommand struct {
1803- cmd.EnvCommandBase
1804+ envcmd.EnvCommandBase
1805 fetch bool
1806 metadataDir string
1807 public bool
1808@@ -41,6 +42,14 @@
1809 f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information")
1810 }
1811
1812+func (c *ToolsMetadataCommand) Init(args []string) (err error) {
1813+ err = c.EnvCommandBase.Init()
1814+ if err != nil {
1815+ return
1816+ }
1817+ return cmd.CheckEmpty(args)
1818+}
1819+
1820 func (c *ToolsMetadataCommand) Run(context *cmd.Context) error {
1821 loggo.RegisterWriter("toolsmetadata", cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr), loggo.INFO)
1822 defer loggo.RemoveWriter("toolsmetadata")
1823
1824=== modified file 'cmd/plugins/juju-metadata/validateimagemetadata.go'
1825--- cmd/plugins/juju-metadata/validateimagemetadata.go 2014-03-21 03:27:16 +0000
1826+++ cmd/plugins/juju-metadata/validateimagemetadata.go 2014-04-01 14:32:48 +0000
1827@@ -12,6 +12,7 @@
1828 "launchpad.net/gnuflag"
1829
1830 "launchpad.net/juju-core/cmd"
1831+ "launchpad.net/juju-core/cmd/envcmd"
1832 "launchpad.net/juju-core/environs"
1833 "launchpad.net/juju-core/environs/config"
1834 "launchpad.net/juju-core/environs/configstore"
1835@@ -22,7 +23,7 @@
1836
1837 // ValidateImageMetadataCommand
1838 type ValidateImageMetadataCommand struct {
1839- cmd.EnvCommandBase
1840+ envcmd.EnvCommandBase
1841 out cmd.Output
1842 providerType string
1843 metadataDir string
1844@@ -91,6 +92,10 @@
1845 }
1846
1847 func (c *ValidateImageMetadataCommand) Init(args []string) error {
1848+ err := c.EnvCommandBase.Init()
1849+ if err != nil {
1850+ return err
1851+ }
1852 if c.providerType != "" {
1853 if c.series == "" {
1854 return fmt.Errorf("series required if provider type is specified")
1855@@ -102,7 +107,7 @@
1856 return fmt.Errorf("metadata directory required if provider type is specified")
1857 }
1858 }
1859- return c.EnvCommandBase.Init(args)
1860+ return nil
1861 }
1862
1863 var _ environs.ConfigGetter = (*overrideEnvStream)(nil)
1864
1865=== modified file 'cmd/plugins/juju-metadata/validatetoolsmetadata.go'
1866--- cmd/plugins/juju-metadata/validatetoolsmetadata.go 2014-03-21 03:27:16 +0000
1867+++ cmd/plugins/juju-metadata/validatetoolsmetadata.go 2014-04-01 14:32:48 +0000
1868@@ -11,6 +11,7 @@
1869 "launchpad.net/gnuflag"
1870
1871 "launchpad.net/juju-core/cmd"
1872+ "launchpad.net/juju-core/cmd/envcmd"
1873 "launchpad.net/juju-core/environs"
1874 "launchpad.net/juju-core/environs/configstore"
1875 "launchpad.net/juju-core/environs/simplestreams"
1876@@ -22,7 +23,7 @@
1877
1878 // ValidateToolsMetadataCommand
1879 type ValidateToolsMetadataCommand struct {
1880- cmd.EnvCommandBase
1881+ envcmd.EnvCommandBase
1882 out cmd.Output
1883 providerType string
1884 metadataDir string
1885@@ -117,6 +118,10 @@
1886 }
1887
1888 func (c *ValidateToolsMetadataCommand) Init(args []string) error {
1889+ err := c.EnvCommandBase.Init()
1890+ if err != nil {
1891+ return err
1892+ }
1893 if c.providerType != "" {
1894 if c.region == "" {
1895 return fmt.Errorf("region required if provider type is specified")
1896@@ -134,7 +139,7 @@
1897 return err
1898 }
1899 }
1900- return c.EnvCommandBase.Init(args)
1901+ return nil
1902 }
1903
1904 func (c *ValidateToolsMetadataCommand) Run(context *cmd.Context) error {
1905
1906=== modified file 'cmd/plugins/juju-restore/restore.go'
1907--- cmd/plugins/juju-restore/restore.go 2014-03-26 12:38:12 +0000
1908+++ cmd/plugins/juju-restore/restore.go 2014-04-01 14:32:48 +0000
1909@@ -19,6 +19,7 @@
1910 "launchpad.net/goyaml"
1911
1912 "launchpad.net/juju-core/cmd"
1913+ "launchpad.net/juju-core/cmd/envcmd"
1914 "launchpad.net/juju-core/constraints"
1915 "launchpad.net/juju-core/environs"
1916 "launchpad.net/juju-core/environs/bootstrap"
1917@@ -64,7 +65,7 @@
1918 `
1919
1920 type restoreCommand struct {
1921- cmd.EnvCommandBase
1922+ envcmd.EnvCommandBase
1923 Log cmd.Log
1924 Constraints constraints.Value
1925 backupFile string
1926@@ -88,6 +89,10 @@
1927 }
1928
1929 func (c *restoreCommand) Init(args []string) error {
1930+ err := c.EnvCommandBase.Init()
1931+ if err != nil {
1932+ return err
1933+ }
1934 if c.showDescription {
1935 return cmd.CheckEmpty(args)
1936 }
1937
1938=== modified file 'cmd/supercommand.go'
1939--- cmd/supercommand.go 2014-03-20 06:01:50 +0000
1940+++ cmd/supercommand.go 2014-04-01 14:32:48 +0000
1941@@ -13,7 +13,6 @@
1942
1943 "github.com/juju/loggo"
1944 "launchpad.net/gnuflag"
1945-
1946 "launchpad.net/juju-core/version"
1947 )
1948
1949
1950=== modified file 'environs/configstore/disk.go'
1951--- environs/configstore/disk.go 2014-03-12 10:59:17 +0000
1952+++ environs/configstore/disk.go 2014-04-01 14:32:48 +0000
1953@@ -30,6 +30,14 @@
1954 dir string
1955 }
1956
1957+type EnvironInfoData struct {
1958+ User string
1959+ Password string
1960+ StateServers []string `json:"state-servers" yaml:"state-servers"`
1961+ CACert string `json:"ca-cert" yaml:"ca-cert"`
1962+ Config map[string]interface{} `json:"bootstrap-config,omitempty" yaml:"bootstrap-config,omitempty"`
1963+}
1964+
1965 type environInfo struct {
1966 path string
1967 // initialized signifies whether the info has been written.
1968@@ -37,12 +45,9 @@
1969
1970 // created signifies whether the info was returned from
1971 // a CreateInfo call.
1972- created bool
1973- User string
1974- Password string
1975- StateServers []string `yaml:"state-servers"`
1976- CACert string `yaml:"ca-cert"`
1977- Config map[string]interface{} `yaml:"bootstrap-config,omitempty"`
1978+ created bool
1979+
1980+ EnvInfo EnvironInfoData
1981 }
1982
1983 // NewDisk returns a ConfigStorage implementation that stores
1984@@ -106,7 +111,7 @@
1985 if len(data) == 0 {
1986 return &info, nil
1987 }
1988- if err := goyaml.Unmarshal(data, &info); err != nil {
1989+ if err := goyaml.Unmarshal(data, &info.EnvInfo); err != nil {
1990 return nil, fmt.Errorf("error unmarshalling %q: %v", path, err)
1991 }
1992 info.initialized = true
1993@@ -120,22 +125,22 @@
1994
1995 // BootstrapConfig implements EnvironInfo.BootstrapConfig.
1996 func (info *environInfo) BootstrapConfig() map[string]interface{} {
1997- return info.Config
1998+ return info.EnvInfo.Config
1999 }
2000
2001 // APICredentials implements EnvironInfo.APICredentials.
2002 func (info *environInfo) APICredentials() APICredentials {
2003 return APICredentials{
2004- User: info.User,
2005- Password: info.Password,
2006+ User: info.EnvInfo.User,
2007+ Password: info.EnvInfo.Password,
2008 }
2009 }
2010
2011 // APIEndpoint implements EnvironInfo.APIEndpoint.
2012 func (info *environInfo) APIEndpoint() APIEndpoint {
2013 return APIEndpoint{
2014- Addresses: info.StateServers,
2015- CACert: info.CACert,
2016+ Addresses: info.EnvInfo.StateServers,
2017+ CACert: info.EnvInfo.CACert,
2018 }
2019 }
2020
2021@@ -144,19 +149,19 @@
2022 if !info.created {
2023 panic("bootstrap config set on environment info that has not just been created")
2024 }
2025- info.Config = attrs
2026+ info.EnvInfo.Config = attrs
2027 }
2028
2029 // SetAPIEndpoint implements EnvironInfo.SetAPIEndpoint.
2030 func (info *environInfo) SetAPIEndpoint(endpoint APIEndpoint) {
2031- info.StateServers = endpoint.Addresses
2032- info.CACert = endpoint.CACert
2033+ info.EnvInfo.StateServers = endpoint.Addresses
2034+ info.EnvInfo.CACert = endpoint.CACert
2035 }
2036
2037 // SetAPICredentials implements EnvironInfo.SetAPICredentials.
2038 func (info *environInfo) SetAPICredentials(creds APICredentials) {
2039- info.User = creds.User
2040- info.Password = creds.Password
2041+ info.EnvInfo.User = creds.User
2042+ info.EnvInfo.Password = creds.Password
2043 }
2044
2045 // Location returns the location of the environInfo in human readable format.
2046@@ -166,7 +171,7 @@
2047
2048 // Write implements EnvironInfo.Write.
2049 func (info *environInfo) Write() error {
2050- data, err := goyaml.Marshal(info)
2051+ data, err := goyaml.Marshal(info.EnvInfo)
2052 if err != nil {
2053 return errgo.Annotate(err, "cannot marshal environment info")
2054 }
2055
2056=== modified file 'environs/configstore/mem.go'
2057--- environs/configstore/mem.go 2014-03-06 12:50:38 +0000
2058+++ environs/configstore/mem.go 2014-04-01 14:32:48 +0000
2059@@ -28,10 +28,10 @@
2060 // references, which makes this OK to do.
2061 info1 := *info
2062 newAttrs := make(map[string]interface{})
2063- for name, attr := range info.Config {
2064+ for name, attr := range info.EnvInfo.Config {
2065 newAttrs[name] = attr
2066 }
2067- info1.Config = newAttrs
2068+ info1.EnvInfo.Config = newAttrs
2069 info1.created = false
2070 return &info1
2071 }
2072
2073=== modified file 'environs/manual/init_test.go'
2074--- environs/manual/init_test.go 2014-03-13 07:54:56 +0000
2075+++ environs/manual/init_test.go 2014-04-01 14:32:48 +0000
2076@@ -43,7 +43,7 @@
2077 // will return an error. stderr will be included in the error message.
2078 defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "oh noes"}, 33)()
2079 hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname")
2080- c.Assert(err, gc.ErrorMatches, "rc: 33 \\(oh noes\\)")
2081+ c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 33 \\(oh noes\\)")
2082 // if the script doesn't fail, stderr is simply ignored.
2083 defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "non-empty-stderr"}, 0)()
2084 hc, _, err = manual.DetectSeriesAndHardwareCharacteristics("hostname")
2085@@ -134,7 +134,7 @@
2086 // will return an error. stderr will be included in the error message.
2087 defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"non-empty-stdout", "non-empty-stderr"}, 255)()
2088 _, err = manual.CheckProvisioned("example.com")
2089- c.Assert(err, gc.ErrorMatches, "rc: 255 \\(non-empty-stderr\\)")
2090+ c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 255 \\(non-empty-stderr\\)")
2091 }
2092
2093 func (s *initialisationSuite) TestInitUbuntuUserNonExisting(c *gc.C) {
2094@@ -153,5 +153,5 @@
2095 defer installFakeSSH(c, "", []string{"", "failed to create ubuntu user"}, 123)()
2096 defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
2097 err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
2098- c.Assert(err, gc.ErrorMatches, "rc: 123 \\(failed to create ubuntu user\\)")
2099+ c.Assert(err, gc.ErrorMatches, "subprocess encountered error code 123 \\(failed to create ubuntu user\\)")
2100 }
2101
2102=== modified file 'environs/manual/provisioner_test.go'
2103--- environs/manual/provisioner_test.go 2014-03-17 03:17:04 +0000
2104+++ environs/manual/provisioner_test.go 2014-04-01 14:32:48 +0000
2105@@ -75,7 +75,7 @@
2106 }.install(c).Restore()
2107 machineId, err = manual.ProvisionMachine(args)
2108 if errorCode != 0 {
2109- c.Assert(err, gc.ErrorMatches, fmt.Sprintf("rc: %d", errorCode))
2110+ c.Assert(err, gc.ErrorMatches, fmt.Sprintf("subprocess encountered error code %d", errorCode))
2111 c.Assert(machineId, gc.Equals, "")
2112 } else {
2113 c.Assert(err, gc.IsNil)
2114@@ -109,7 +109,7 @@
2115 SkipProvisionAgent: true,
2116 }.install(c).Restore()
2117 _, err = manual.ProvisionMachine(args)
2118- c.Assert(err, gc.ErrorMatches, "error checking if provisioned: rc: 255")
2119+ c.Assert(err, gc.ErrorMatches, "error checking if provisioned: subprocess encountered error code 255")
2120 }
2121
2122 func (s *provisionerSuite) TestFinishMachineConfig(c *gc.C) {
2123
2124=== modified file 'juju/api.go'
2125--- juju/api.go 2014-03-31 12:24:52 +0000
2126+++ juju/api.go 2014-04-01 14:32:48 +0000
2127@@ -19,6 +19,7 @@
2128 "launchpad.net/juju-core/names"
2129 "launchpad.net/juju-core/state/api"
2130 "launchpad.net/juju-core/state/api/keymanager"
2131+ "launchpad.net/juju-core/state/api/usermanager"
2132 "launchpad.net/juju-core/utils/parallel"
2133 )
2134
2135@@ -102,6 +103,14 @@
2136 return keymanager.NewClient(st), nil
2137 }
2138
2139+func NewUserManagerClient(envName string) (*usermanager.Client, error) {
2140+ st, err := newAPIClient(envName)
2141+ if err != nil {
2142+ return nil, err
2143+ }
2144+ return usermanager.NewClient(st), nil
2145+}
2146+
2147 // NewAPIFromName returns an api.State connected to the API Server for
2148 // the named environment. If envName is "", the default environment will
2149 // be used.
2150
2151=== modified file 'state/api/params/params.go'
2152--- state/api/params/params.go 2014-03-31 12:30:31 +0000
2153+++ state/api/params/params.go 2014-04-01 14:32:48 +0000
2154@@ -361,6 +361,12 @@
2155 Keys []string
2156 }
2157
2158+// ModifyUser stores the parameters used for a UserManager.Add|Remove call
2159+type ModifyUser struct {
2160+ Tag string
2161+ Password string
2162+}
2163+
2164 // MarshalJSON implements json.Marshaler.
2165 func (d *Delta) MarshalJSON() ([]byte, error) {
2166 b, err := json.Marshal(d.Entity)
2167
2168=== modified file 'state/apiserver/client/api_test.go'
2169--- state/apiserver/client/api_test.go 2014-03-13 13:42:50 +0000
2170+++ state/apiserver/client/api_test.go 2014-04-01 14:32:48 +0000
2171@@ -267,7 +267,7 @@
2172 setDefaultPassword(c, u)
2173 add(u)
2174
2175- u, err = s.State.AddUser("other", "")
2176+ u, err = s.State.AddUser("other", "password")
2177 c.Assert(err, gc.IsNil)
2178 setDefaultPassword(c, u)
2179 add(u)
2180
2181=== modified file 'state/user_test.go'
2182--- state/user_test.go 2014-03-13 13:42:50 +0000
2183+++ state/user_test.go 2014-04-01 14:32:48 +0000
2184@@ -58,7 +58,7 @@
2185 }
2186
2187 func (s *UserSuite) TestSetPassword(c *gc.C) {
2188- u, err := s.State.AddUser("someuser", "")
2189+ u, err := s.State.AddUser("someuser", "password")
2190 c.Assert(err, gc.IsNil)
2191
2192 testSetPassword(c, func() (state.Authenticator, error) {
2193@@ -92,7 +92,7 @@
2194 }
2195
2196 func (s *UserSuite) TestSetPasswordHash(c *gc.C) {
2197- u, err := s.State.AddUser("someuser", "")
2198+ u, err := s.State.AddUser("someuser", "password")
2199 c.Assert(err, gc.IsNil)
2200
2201 err = u.SetPasswordHash(utils.UserPasswordHash("foo", utils.CompatSalt), utils.CompatSalt)
2202@@ -111,7 +111,7 @@
2203 }
2204
2205 func (s *UserSuite) TestSetPasswordHashWithSalt(c *gc.C) {
2206- u, err := s.State.AddUser("someuser", "")
2207+ u, err := s.State.AddUser("someuser", "password")
2208 c.Assert(err, gc.IsNil)
2209
2210 err = u.SetPasswordHash(utils.UserPasswordHash("foo", "salted"), "salted")
2211@@ -124,7 +124,7 @@
2212 }
2213
2214 func (s *UserSuite) TestPasswordValidUpdatesSalt(c *gc.C) {
2215- u, err := s.State.AddUser("someuser", "")
2216+ u, err := s.State.AddUser("someuser", "password")
2217 c.Assert(err, gc.IsNil)
2218
2219 compatHash := utils.UserPasswordHash("foo", utils.CompatSalt)
2220@@ -152,7 +152,7 @@
2221 }
2222
2223 func (s *UserSuite) TestName(c *gc.C) {
2224- u, err := s.State.AddUser("someuser", "")
2225+ u, err := s.State.AddUser("someuser", "password")
2226 c.Assert(err, gc.IsNil)
2227
2228 c.Assert(u.Name(), gc.Equals, "someuser")
2229@@ -160,7 +160,7 @@
2230 }
2231
2232 func (s *UserSuite) TestDeactivate(c *gc.C) {
2233- u, err := s.State.AddUser("someuser", "")
2234+ u, err := s.State.AddUser("someuser", "password")
2235 c.Assert(err, gc.IsNil)
2236 c.Assert(u.IsDeactivated(), gc.Equals, false)
2237
2238
2239=== modified file 'testing/environ.go'
2240--- testing/environ.go 2014-02-18 17:15:06 +0000
2241+++ testing/environ.go 2014-04-01 14:32:48 +0000
2242@@ -240,8 +240,10 @@
2243 return fake
2244 }
2245
2246-func MakeSampleHome(c *gc.C) *FakeHome {
2247- return MakeFakeHome(c, SingleEnvConfig, SampleCertName)
2248+func MakeSampleHome(c *gc.C, files ...TestFile) *FakeHome {
2249+ fake := MakeFakeHome(c, SingleEnvConfig, SampleCertName)
2250+ fake.AddFiles(c, files)
2251+ return fake
2252 }
2253
2254 func MakeMultipleEnvHome(c *gc.C) *FakeHome {

Subscribers

People subscribed via source and target branches

to status/vote changes: