Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
cmd/juju/controller: allow registering public controller #6382
Conversation
reedobrien
suggested changes
Oct 5, 2016
Mostly LGTM. A few trivial nits.
One big issue: the register test in featuretests fails.
I like the testing.Prompter too. Nice work.
I'll see if I can figure out how to do the qa steps.
| +To complete the user registration process, you should have been provided | ||
| +with a base64-encoded blob of data (the output of 'juju add-user') which | ||
| +can be copied and pasted as the <string> argument to 'register'. | ||
| +You will be prompted for a password, which, once set, causes the |
reedobrien
Oct 5, 2016
Contributor
nit: These paragraphs look like they are wrapped inconsistenly. Maybe just how they look in the review bits.
| registration string to be voided. In order to start using Juju the user can now | ||
| either add a model or wait for a model to be shared with them. Some machine | ||
| providers will require the user to be in possession of certain credentials in | ||
| order to add a model. | ||
| +When adding a controller at a public address, authentication via some | ||
| +external third party (for example Ubuntu USSO) will be required, usually |
| @@ -87,7 +99,7 @@ See also: | ||
| func (c *registerCommand) Info() *cmd.Info { | ||
| return &cmd.Info{ | ||
| Name: "register", | ||
| - Args: "<string>", | ||
| + Args: "<string>|<controller host name>", |
reedobrien
Oct 5, 2016
Contributor
If I'm reading this right it means a base64 encoded registration string or controller hostname. If I didn't know anything I would think any string would suffice. Maybe make this <registration string>|<cotroller host name> or something.
| + return jujuclient.ControllerDetails{ | ||
| + APIEndpoints: []string{host}, | ||
| + ControllerUUID: conn.ControllerTag().Id(), | ||
| + // TODO(rogpeppe) it might be good to update the non-official CA cert here too |
reedobrien
Oct 5, 2016
Contributor
I am being told in reviews to use the format:
// TODO(id) yyyy-mm-dd [#bugnumber] <comment>
So I suppose it should be used here, too.
rogpeppe
Oct 6, 2016
Owner
I've removed the TODO. I'm not sure it's the right thing to do anyway and - it doesn't justify a bug report. (BTW only 12% of TODOs in the source code fit this format. I'm not entirely convinced it's worth having an issue for every TODO).
| - return errors.Annotate(err, "storing model details") | ||
| + for name, ctl := range all { | ||
| + if ctl.ControllerUUID == controllerDetails.ControllerUUID { | ||
| + // TODO(rogpeppe) Succeed but override the account details in this case? |
reedobrien
Oct 5, 2016
•
Contributor
TODO format... Also, have you ansered your question? I don't think I quite understand it in context.
| @@ -233,74 +348,81 @@ func (c *registerCommand) maybeSetCurrentModel(ctx *cmd.Context, store jujuclien | ||
| if err != nil { | ||
| return errors.Trace(err) | ||
| } | ||
| - fmt.Fprintf(ctx.Stderr, "\nCurrent model set to %q.\n\n", modelName) | ||
| - } else { |
| - fmt.Fprintf(ctx.Stderr, "\nCurrent model set to %q.\n\n", modelName) | ||
| - } else { | ||
| - fmt.Fprintf(ctx.Stderr, ` | ||
| + fmt.Fprintf(ctx.Stderr, "\nCurrent model set to %s.\n", modelName) |
reedobrien
Oct 5, 2016
Contributor
Is there a particular reason to stop quoting the model name in output?
rogpeppe
Oct 6, 2016
Owner
Well, we know the model name is well formed (because it's been verified and it's a very restricted syntax) so there's no danger of ambiguity, and it looks slightly cleaner unquoted IMHO.
axw
Oct 6, 2016
Member
Please revert, so we are consistent with use of %q for controller names in sentences.
| + // Looks like a host name - no URL-encoded base64 string should | ||
| + // contain a dot and every public controller name should. | ||
| + // Allow localhost for development purposes. | ||
| + params.publicHost = c.Arg |
rogpeppe
Oct 6, 2016
Owner
Yup, good catch - it's a hangover from when we fetched the controller name in both cases.
| - return nil, errors.Trace(err) | ||
| + if params.publicHost != "" { | ||
| + // No need for password shenanigans if we're using a public controller. | ||
| + return ¶ms, nil |
reedobrien
Oct 5, 2016
•
Contributor
Can't this just return up where params.publicHost is set ~line 400?
|
Unable to QA. I was unable to resolve the endpoints in http://paste.ubuntu.com/23080517/ to bootstrap. Updating them to be https://ec2..amazonaws.com/... resovled them but then I failed for other reasons (not authorized to perform action on one machine and |
|
Tried on a 3rd computer and I could bootstrap. I guess that my local DNS here is hiding the upstream changes. I bootstrapped with a domain name I had control of, then had to add DNS pointing at the external IP of the bootstrapped controller. Then I got a link to a paste in my browser which took me to a page, "login successful as user ". So IIUC, QA checks out. |
| Examples: | ||
| juju register MFATA3JvZDAnExMxMDQuMTU0LjQyLjQ0OjE3MDcwExAxMC4xMjguMC4yOjE3MDcwBCBEFCaXerhNImkKKabuX5ULWf2Bp4AzPNJEbXVWgraLrAA= | ||
| + juju register public-controller.somewhere.com |
axw
approved these changes
Oct 6, 2016
LGTM with a bunch of little things. Please also verify that add-user/register still works.
| @@ -55,27 +56,38 @@ type registerCommand struct { | ||
| apiOpen api.OpenFunc | ||
| listModelsFunc func(_ jujuclient.ClientStore, controller, user string) ([]base.UserModel, error) | ||
| store jujuclient.ClientStore | ||
| - EncodedData string | ||
| + Arg string | ||
| + ControllerName string |
| -referred to in Usage. | ||
| - | ||
| -The user will be prompted for a password, which, once set, causes the | ||
| +The register command adds a details of a controller to the local system. |
| + return c.maybeSetCurrentModel(ctx, store, controllerName, accountDetails.User, models) | ||
| +} | ||
| + | ||
| +func friendlyUserName(user string) string { |
axw
Oct 6, 2016
•
Member
Ian's got a branch in the works that gets rid of @Local, so you'll be able to drop this.
| + errRet := func(err error) (jujuclient.ControllerDetails, jujuclient.AccountDetails, error) { | ||
| + return jujuclient.ControllerDetails{}, jujuclient.AccountDetails{}, err | ||
| + } | ||
| + if !strings.Contains(host, ":") { |
axw
Oct 6, 2016
Member
I think it would be a bit clearer if we assigned host to another var, say "apiAddr".
apiAddr := host
if !strings.Contains(apiAddr, ":") {
apiAddr += ":443"
}
...
| - return errors.Annotate(err, "storing model details") | ||
| + for name, ctl := range all { | ||
| + if ctl.ControllerUUID == controllerDetails.ControllerUUID { | ||
| + // TODO(rogpeppe) Succeed but override the account details in this case? |
axw
Oct 6, 2016
Member
I think that would be sensible, if a little unusual. Can you please file a bug for that and reference?
| - fmt.Fprintf(ctx.Stderr, "\nCurrent model set to %q.\n\n", modelName) | ||
| - } else { | ||
| - fmt.Fprintf(ctx.Stderr, ` | ||
| + fmt.Fprintf(ctx.Stderr, "\nCurrent model set to %s.\n", modelName) |
reedobrien
Oct 5, 2016
Contributor
Is there a particular reason to stop quoting the model name in output?
rogpeppe
Oct 6, 2016
Owner
Well, we know the model name is well formed (because it's been verified and it's a very restricted syntax) so there's no danger of ambiguity, and it looks slightly cleaner unquoted IMHO.
axw
Oct 6, 2016
Member
Please revert, so we are consistent with use of %q for controller names in sentences.
| + // Looks like a host name - no URL-encoded base64 string should | ||
| + // contain a dot and every public controller name should. | ||
| + // Allow localhost for development purposes. | ||
| + params.publicHost = c.Arg |
rogpeppe
Oct 6, 2016
Owner
Yup, good catch - it's a hangover from when we fetched the controller name in both cases.
| -const errControllerConflicts = `WARNING: You already have a controller registered with the name %q. Please choose a different name for the new controller. | ||
| - | ||
| -` | ||
| - | ||
| func (c *registerCommand) promptControllerName(store jujuclient.ClientStore, suggestedName string, stderr io.Writer, stdin io.Reader) (string, error) { | ||
| _, err := store.ControllerByName(suggestedName) |
axw
Oct 6, 2016
Member
Please skip this if suggestedName == "". It would previously always be non-empty, but that's not the case now.
| + } | ||
| + name = suggestedName | ||
| + } | ||
| + _, err = store.ControllerByName(name) |
rogpeppe
Oct 6, 2016
Owner
I think I prefer to keep the logic simpler - it does no harm to check in all cases.
| @@ -0,0 +1,220 @@ | ||
| +package testing |
| + gc "gopkg.in/check.v1" | ||
| +) | ||
| + | ||
| +var logger = loggo.GetLogger("cmd.testing") |
| + | ||
| +var logger = loggo.GetLogger("cmd.testing") | ||
| + | ||
| +// NewSeqPrompter returns a prompter that expected prompt text matching |
| +// all the items in expectPrompts in sequence, and will reply with | ||
| +// the associated text in replies when requested. | ||
| +// | ||
| +// The expectPrompts slice may be one longer than replies, |
| +// marker on that line, the checker will cause io.EOF to be returned for | ||
| +// that prompt. | ||
| +// | ||
| +// The returned interactionChecker wraps a Prompter and checks that each |
| +// The returned interactionChecker wraps a Prompter and checks that each | ||
| +// read and write corresponds to the expected action in the sequence. | ||
| +// | ||
| +// After all interaction is done, CheckDone or AssertDone should be closed to |
|
$$merge$$ |
|
Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju |
rogpeppe commentedOct 5, 2016
•
Edited 1 time
-
rogpeppe
Oct 5, 2016
This allows a controller with an officially signed certificate to be
registered with:
Because it's got an officially signed certificate, we can trust it to
obtain the controller UUID, and external user authentication lets us
know what user has been chosen without needing to select one explicitly.
This caused a significant reshuffle of the register command. In
particular, we now ask for the password before asking for
the controller name because we can't get the controller UUID
without the password. That moves us in the direction of fixing
https://bugs.launchpad.net/juju/+bug/1614010 because we no longer prompt
for the controller name if we know that the controller cannot be stored.
We also update the controller-name input code so that it loops until a
correct controller name is supplied rather than immediately returning
an error.
The tests needed significant change because all the tests that previously
assumed that the controller name was prompted before any interaction
with the server now need a mock server.
To make this a bit more straightforward, we add a the Prompter types
in cmd/testing which means that the interaction requirements can be
specified more declaratively. This has the side effect of testing that
the read/write interleaving is exactly as expected.
QA
Bootstrap a controller with an officially signed certificate and externally configured users:
Grant everyone permission to access the controller and create models:
Then configure the DNS record for your domain name to point to the IP address of the newly
bootstrapped controller.
Then, on a fresh juju client instance (or with the new controller entry removed from controller.yaml and accounts.yaml) run:
That should succeed and log in as your USSO username@external. You should then be able to create models and list models etc.