cmd/modelcmd: per-controller cookie jars #7294

Merged
merged 1 commit into from May 2, 2017

Conversation

Projects
None yet
6 participants
Owner

rogpeppe commented Apr 28, 2017

This makes all macaroons per-controller, so you can potentially
be logged into as a separate external user to every controller.

The cookies are stored in $JUJU_DATA/cookies-$controllername
in the same format already used for the $HOME/.go-cookies.

This disturbed quite a few invariants that tests were used to.
Here's a summary of some of the changes:

  • add cookiejars to jujuclient.ClientStore so that tests weren't
    inadvertently creating cookie files.

  • there is now no access to ClientStore in commands before Run.
    ControllerName and ModelName now return errors
    because they resolve the current model or controller
    name when called if needed. If a command tries to
    use any of these methods during Init (or any method that relies
    on them), it will panic.

  • since we now have modelcmd.InnerCommand because the modelcmd
    Wrap functions now return more specific interfaces, we can simplify tests
    in quite a few places. In particular, many functions that returned a pair
    of objects (the wrapped command and the inner command) now
    just return the wrapped command, leaving it to specific test logic
    to obtain the inner command if needed (for example to test the values
    of instance variables after Init)

  • the register command now needs to prompt for a controller
    name before connecting otherwise it can't create the
    cookie jar because it doesn't know where to store the cookies.

  • some tests (cmd/juju/subnet, cmd/juju/space) were
    reusing the same command instance several times.

jrwren approved these changes Apr 28, 2017

Nice cleaning

a few smallish things, otherwise LGTM

cmd/juju/model/dump.go
@@ -19,6 +19,7 @@ func NewDumpCommand() cmd.Command {
}
type dumpCommand struct {
+ // TODO shouldn't this be ModelCommandBase ?
@axw

axw May 1, 2017

Member

Yes, I think it should be, like "show-model".

@rogpeppe

rogpeppe May 2, 2017

Owner

adjusted TODO, but leaving for another PR.

cmd/juju/romulus/budget/budget_test.go
@@ -15,6 +15,7 @@ import (
"github.com/juju/juju/cmd/juju/romulus/budget"
"github.com/juju/juju/jujuclient"
coretesting "github.com/juju/juju/testing"
+ "github.com/juju/persistent-cookiejar"
cmd/modelcmd/base.go
+}
+
+func (c *CommandBase) setRunStarted() {
+ c.runStarted = true
}
// closeContext closes the command's API context
@axw

axw May 1, 2017

Member

comment needs adjusting

return c.store
}
-// SetControllerName implements ControllerCommand.SetControllerName.
-func (c *ControllerCommandBase) SetControllerName(controllerName string, allowDefault bool) error {
+func (c *ControllerCommandBase) initController() error {
@axw

axw May 1, 2017

Member

This initController/initModel business feels pretty gross, but I guess it's all we can do for now.

I think we eventually need to move away from the inheritance pattern we currently have around commands, and towards composition so that the ModelCommandBase (or whatever it's called now) Run method comes first, and delegates to the command-specific code with a separate signature, like:

package modelcmd

type ModelCommand interface {
    Init(...)
    SetFlags(...)
    Run(modelcmd.ModelContext) error
}

type ControllerContext struct {
    Store jujuclient.ClientStore
    APIDialer APIDialer
    ControllerName string
}

type ModelContext struct {
    ControllerContext
    ModelName string
}

type APIDialer interface {
    DialAPI(controllerName, modelName string) (api.Connection, error)
}

(implementations of ModelCommand wouldn't embed anything)

@rogpeppe

rogpeppe May 2, 2017

Owner

I agree that it would be good to move towards a more composition-based model. FWIW I started by making it avoid calling Run if the context couldn't be initialised, but then did this lazy implementation when I realised just how many tests rely on being able to call Run without any models or controllers in the store.

cmd/modelcmd/modelcommand.go
@@ -84,17 +86,25 @@ type ModelCommand interface {
// unqualified, in which case it will be assumed to be within the
// current controller.
//
+ // Passing an empty model name will choose the default
+ // mode, or return an error if there isn't one.
@axw

axw May 1, 2017

Member

s/mode/model/

@@ -703,3 +707,40 @@ func (s *store) BootstrapConfigForController(controllerName string) (*BootstrapC
}
return &cfg, nil
}
+
+// CookieJar returns the cookie jar associated with the given controller.
+func (s *store) CookieJar(controllerName string) (CookieJar, error) {
@axw

axw May 1, 2017

Member

The RemoveController method of ClientStore implementations is expected to clean up any controller-related resources. Is there any reason to keep the cookie files around after a controller has been destroyed? If not, please remove the cookie jar file towards the bottom of that method. If it should be kept, please add a comment explaining why.

@rogpeppe

rogpeppe May 2, 2017

Owner

Good point. Done.

jujuclient/file.go
+// JujuCookiePath is the location where cookies associated
+// with the given controller are expected to be found.
+func JujuCookiePath(controllerName string) string {
+ return osenv.JujuXDGDataHomePath("cookies-" + controllerName + ".json")
@axw

axw May 1, 2017

Member

can we please make this a sub-directory, to avoid polluting the top level directory with too many dynamically named entries? i.e. ~/.local/share/juju/cookies/controller.json

@@ -81,6 +82,7 @@ func (s *CmdSuite) TestDeployCommandInit(c *gc.C) {
c.Assert(err, gc.ErrorMatches, t.expectError)
continue
}
+ c.Assert(err, jc.ErrorIsNil)
@cmars

cmars May 1, 2017

Owner

Nice catch!

- c.Assert(err, gc.ErrorMatches, "no application name specified")
-
- // missing application name
- err = cmdtesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"name=foo"})
@cmars

cmars May 1, 2017

Owner

This test case needs to be added to the refactored table form below.

@rogpeppe

rogpeppe May 2, 2017

Owner

good catch. done.

+ "testconfig.yaml",
+ }, s.dir)
+ c.Assert(err, gc.ErrorMatches, `(.|\n)*All operations that change model have been disabled(.|\n)*`)
+ c.Check(c.GetTestLog(), gc.Matches, "(.|\n)*TestBlockSetConfig(.|\n)*")
@cmars

cmars May 1, 2017

Owner

Could prefix the regexp with (?s) and then . should match \n.

@rogpeppe

rogpeppe May 2, 2017

Owner

I can never remember the meaning of all those flags! I think the explicit form is probably better because it's more obvious what's going on.

Owner

wallyworld commented May 2, 2017

I didn't look in detail - does this handle upgrading existing controllers where the cookies are stored in $HOME/.go-cookies (in fact, it may not be home dir, there's an env var that can place the cookie jar anywhere the user wants).

Owner

rogpeppe commented May 2, 2017

I didn't look in detail - does this handle upgrading existing controllers where the cookies are stored in $HOME/.go-cookies (in fact, it may not be home dir, there's an env var that can place the cookie jar anywhere the user wants).

No it doesn't, but I'm not entirely sure that that's needed - the macaroons can always expire anyway. This change could just be considered an early expiry.

All points addressed.

Owner

rogpeppe commented May 2, 2017

$$merge$$

Contributor

jujubot commented May 2, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

Contributor

jujubot commented May 2, 2017

Build failed: Tests failed
build url: http://juju-ci.vapour.ws:8080/job/github-merge-juju/10812

Owner

rogpeppe commented May 2, 2017

$$merge$$

Contributor

jujubot commented May 2, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

Contributor

jujubot commented May 2, 2017

Build failed: Tests failed
build url: http://juju-ci.vapour.ws:8080/job/github-merge-juju/10813

Owner

rogpeppe commented May 2, 2017

$$merge$$

Contributor

jujubot commented May 2, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

Contributor

jujubot commented May 2, 2017

Build failed: Tests failed
build url: http://juju-ci.vapour.ws:8080/job/github-merge-juju/10814

Owner

rogpeppe commented May 2, 2017

$$merge$$

Contributor

jujubot commented May 2, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

@jujubot jujubot merged commit 1c6c069 into juju:develop May 2, 2017

1 check passed

github-check-merge-juju Built PR, ran unit tests, and tested LXD deploy. Use !!.*!! to request another build. IE, !!build!!, !!retry!!
Details

rogpeppe added a commit to rogpeppe/juju that referenced this pull request May 26, 2017

cmd/modelcmd: respect JUJU_MODEL env var
In juju#7294, we accidentally
removed the logic that used the JUJU_MODEL
environment variable to set the current model.

This restores that, removes the unused GetCurrentModel
function, and improves the test coverage for this area
so that it's tested.

jujubot added a commit that referenced this pull request May 26, 2017

Merge pull request #7394 from rogpeppe/163-fix-juju-model-env
cmd/modelcmd: respect JUJU_MODEL env var

In #7294, we accidentally
removed the logic that used the JUJU_MODEL
environment variable to set the current model.

This restores that, removes the unused GetCurrentModel
function, and improves the test coverage for this area
so that it's tested.

Fixes https://bugs.launchpad.net/juju/+bug/1693356

To QA, check that JUJU_MODEL is respected when running
juju commands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment