// AddUser creates a new local user in the juju server. func (c *Client) AddUser(username, displayName, password string) (names.UserTag, error) { if !names.IsValidUser(username) { return names.UserTag{}, fmt.Errorf("invalid user name %q", username) } userArgs := params.AddUsers{ Users: []params.AddUser{{Username: username, DisplayName: displayName, Password: password}}, } var results params.AddUserResults err := c.facade.FacadeCall("AddUser", userArgs, &results) if err != nil { return names.UserTag{}, errors.Trace(err) } if count := len(results.Results); count != 1 { logger.Errorf("expected 1 result, got %#v", results) return names.UserTag{}, errors.Errorf("expected 1 result, got %d", count) } result := results.Results[0] if result.Error != nil { return names.UserTag{}, errors.Trace(result.Error) } tag, err := names.ParseUserTag(result.Tag) if err != nil { return names.UserTag{}, errors.Trace(err) } logger.Infof("created user %s", result.Tag) return tag, nil }
// ListModels returns the models that the specified user // has access to in the current server. Only that controller owner // can list models for any user (at this stage). Other users // can only ask about their own models. func (c *Client) ListModels(user string) ([]base.UserModel, error) { var models params.UserModelList if !names.IsValidUser(user) { return nil, errors.Errorf("invalid user name %q", user) } entity := params.Entity{names.NewUserTag(user).String()} err := c.facade.FacadeCall("ListModels", entity, &models) if err != nil { return nil, errors.Trace(err) } result := make([]base.UserModel, len(models.UserModels)) for i, model := range models.UserModels { owner, err := names.ParseUserTag(model.OwnerTag) if err != nil { return nil, errors.Annotatef(err, "OwnerTag %q at position %d", model.OwnerTag, i) } result[i] = base.UserModel{ Name: model.Name, UUID: model.UUID, Owner: owner.Canonical(), LastConnection: model.LastConnection, } } return result, nil }
// ListEnvironments returns the environments that the specified user // has access to in the current server. Only that state server owner // can list environments for any user (at this stage). Other users // can only ask about their own environments. func (c *Client) ListEnvironments(user string) ([]base.UserEnvironment, error) { var environments params.UserEnvironmentList if !names.IsValidUser(user) { return nil, errors.Errorf("invalid user name %q", user) } entity := params.Entity{names.NewUserTag(user).String()} err := c.facade.FacadeCall("ListEnvironments", entity, &environments) if err != nil { return nil, errors.Trace(err) } result := make([]base.UserEnvironment, len(environments.UserEnvironments)) for i, env := range environments.UserEnvironments { owner, err := names.ParseUserTag(env.OwnerTag) if err != nil { return nil, errors.Annotatef(err, "OwnerTag %q at position %d", env.OwnerTag, i) } result[i] = base.UserEnvironment{ Name: env.Name, UUID: env.UUID, Owner: owner.Username(), LastConnection: env.LastConnection, } } return result, nil }
func writeServerFile(endpointProvider EndpointProvider, ctx *cmd.Context, username, password, outPath string) error { outPath = ctx.AbsPath(outPath) endpoint, err := endpointProvider.ConnectionEndpoint() if err != nil { return errors.Trace(err) } if !names.IsValidUser(username) { return errors.Errorf("%q is not a valid username", username) } outputInfo := modelcmd.ServerFile{ Username: username, Password: password, Addresses: endpoint.Addresses, CACert: endpoint.CACert, } yaml, err := cmd.FormatYaml(outputInfo) if err != nil { return errors.Trace(err) } if err := ioutil.WriteFile(outPath, yaml, 0644); err != nil { return errors.Trace(err) } serverFileNotify(outPath) ctx.Infof("server file written to %s", outPath) return nil }
// SetFlags implements Command.Init. func (c *useEnvironmentCommand) Init(args []string) error { if len(args) == 0 || strings.TrimSpace(args[0]) == "" { return errors.New("no environment supplied") } name, args := args[0], args[1:] // First check to see if an owner has been specified. bits := strings.SplitN(name, "/", 2) switch len(bits) { case 1: // No user specified c.EnvName = bits[0] case 2: owner := bits[0] if names.IsValidUser(owner) { c.Owner = owner } else { return errors.Errorf("%q is not a valid user", owner) } c.EnvName = bits[1] } // Environment names can generally be anything, but we take a good // stab at trying to determine if the user has specified a UUID // instead of a name. For now, we only accept a properly formatted UUID, // which means one with dashes in the right place. if names.IsValidEnvironment(c.EnvName) { c.EnvUUID, c.EnvName = c.EnvName, "" } return cmd.CheckEmpty(args) }
func (c *JenvCommand) Init(args []string) error { if len(args) == 0 { return errors.New("no jenv file provided") } // Store the path to the jenv file. if err := c.jenvFile.Set(args[0]); err != nil { return errors.Annotate(err, "invalid jenv path") } args = args[1:] if len(args) > 0 { // Store and validate the provided environment name. c.envName, args = args[0], args[1:] if !names.IsValidUser(c.envName) { return errors.Errorf("invalid model name %q", c.envName) } } else { // Retrieve the environment name from the jenv file name. base := filepath.Base(c.jenvFile.Path) c.envName = base[:len(base)-len(filepath.Ext(base))] } // No other arguments are expected. return cmd.CheckEmpty(args) }
func (c *createModelCommand) Init(args []string) error { if len(args) == 0 { return errors.New("model name is required") } c.Name, args = args[0], args[1:] if c.Owner != "" && !names.IsValidUser(c.Owner) { return errors.Errorf("%q is not a valid user", c.Owner) } if c.CredentialSpec != "" { parts := strings.Split(c.CredentialSpec, ":") if len(parts) < 2 { return errors.Errorf("invalid cloud credential %s, expected <cloud>:<credential-name>", c.CredentialSpec) } c.CloudName = parts[0] if cloud, err := common.CloudOrProvider(c.CloudName, cloud.CloudByName); err != nil { return errors.Trace(err) } else { c.CloudType = cloud.Type } c.CredentialName = parts[1] } return nil }
// AddUser adds a user to the state. func (st *State) AddUser(username, displayName, password, creator string) (*User, error) { if !names.IsValidUser(username) { return nil, errors.Errorf("invalid user name %q", username) } salt, err := utils.RandomSalt() if err != nil { return nil, err } timestamp := time.Now().Round(time.Second).UTC() u := &User{ st: st, doc: userDoc{ Name: username, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: timestamp, }, } ops := []txn.Op{{ C: usersC, Id: username, Assert: txn.DocMissing, Insert: &u.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("user already exists") } if err != nil { return nil, errors.Trace(err) } return u, nil }
func validateUser(name string) error { if !names.IsValidUser(name) { return errors.NotValidf("account name %q", name) } if tag := names.NewUserTag(name); tag.Id() != tag.Canonical() { return errors.NotValidf("unqualified account name %q", name) } return nil }
// ListEnvironments returns the environments that the specified user // has access to in the current server. Only that state server owner // can list environments for any user (at this stage). Other users // can only ask about their own environments. func (c *Client) ListEnvironments(user string) ([]params.Environment, error) { var result params.EnvironmentList if !names.IsValidUser(user) { return nil, fmt.Errorf("invalid user name %q", user) } entity := params.Entity{names.NewUserTag(user).String()} err := c.facade.FacadeCall("ListEnvironments", entity, &result) if err != nil { return nil, errors.Trace(err) } return result.Environments, nil }
// Init initializes the command for running. func (c *BootstrapCommand) Init(args []string) error { if len(c.EnvConfig) == 0 { return cmdutil.RequiredError("model-config") } if c.InstanceId == "" { return cmdutil.RequiredError("instance-id") } if !names.IsValidUser(c.AdminUsername) { return errors.Errorf("%q is not a valid username", c.AdminUsername) } return c.AgentConf.CheckArgs(args) }
func (c *unshareCommand) Init(args []string) (err error) { if len(args) == 0 { return errors.New("no users specified") } for _, arg := range args { if !names.IsValidUser(arg) { return errors.Errorf("invalid username: %q", arg) } c.Users = append(c.Users, names.NewUserTag(arg)) } return nil }
func (c *Client) AddUser(username, displayName, password string) error { if !names.IsValidUser(username) { return fmt.Errorf("invalid user name %q", username) } userArgs := usermanager.ModifyUsers{ Changes: []usermanager.ModifyUser{{Username: username, DisplayName: displayName, Password: password}}, } results := new(params.ErrorResults) err := c.facade.FacadeCall("AddUser", userArgs, results) if err != nil { return errors.Trace(err) } return results.OneError() }
func (c *Client) userCall(username string, methodCall string) error { if !names.IsValidUser(username) { return errors.Errorf("%q is not a valid username", username) } tag := names.NewUserTag(username) var results params.ErrorResults args := params.Entities{ []params.Entity{{tag.String()}}, } err := c.facade.FacadeCall(methodCall, args, &results) if err != nil { return errors.Trace(err) } return results.OneError() }
// CreateModel creates a new model using the account and // model config specified in the args. func (c *Client) CreateModel(owner string, account, config map[string]interface{}) (params.Model, error) { var result params.Model if !names.IsValidUser(owner) { return result, errors.Errorf("invalid owner name %q", owner) } createArgs := params.ModelCreateArgs{ OwnerTag: names.NewUserTag(owner).String(), Account: account, Config: config, } err := c.facade.FacadeCall("CreateModel", createArgs, &result) if err != nil { return result, errors.Trace(err) } return result, nil }
// AddUser creates a new local user in the controller, sharing with that user any specified models. func (c *Client) AddUser( username, displayName, password, access string, modelUUIDs ...string, ) (_ names.UserTag, secretKey []byte, _ error) { if !names.IsValidUser(username) { return names.UserTag{}, nil, fmt.Errorf("invalid user name %q", username) } modelTags := make([]string, len(modelUUIDs)) for i, uuid := range modelUUIDs { modelTags[i] = names.NewModelTag(uuid).String() } var accessPermission params.ModelAccessPermission var err error if len(modelTags) > 0 { accessPermission, err = modelmanager.ParseModelAccess(access) if err != nil { return names.UserTag{}, nil, errors.Trace(err) } } userArgs := params.AddUsers{ Users: []params.AddUser{{ Username: username, DisplayName: displayName, Password: password, SharedModelTags: modelTags, ModelAccess: accessPermission}}, } var results params.AddUserResults err = c.facade.FacadeCall("AddUser", userArgs, &results) if err != nil { return names.UserTag{}, nil, errors.Trace(err) } if count := len(results.Results); count != 1 { logger.Errorf("expected 1 result, got %#v", results) return names.UserTag{}, nil, errors.Errorf("expected 1 result, got %d", count) } result := results.Results[0] if result.Error != nil { return names.UserTag{}, nil, errors.Trace(result.Error) } tag, err := names.ParseUserTag(result.Tag) if err != nil { return names.UserTag{}, nil, errors.Trace(err) } return tag, result.SecretKey, nil }
func (s *userSuite) TestIsValidUser(c *gc.C) { for i, t := range []struct { string string expect bool }{ {"", false}, {"bob", true}, {"Bob", true}, {"bOB", true}, {"b^b", false}, {"bob1", true}, {"bob-1", true}, {"bob+1", true}, {"bob+", false}, {"+bob", false}, {"bob.1", true}, {"1bob", true}, {"1-bob", true}, {"1+bob", true}, {"1.bob", true}, {"jim.bob+99-1.", false}, {"a", false}, {"0foo", true}, {"foo bar", false}, {"bar{}", false}, {"bar+foo", true}, {"bar_foo", false}, {"bar!", false}, {"bar^", false}, {"bar*", false}, {"foo=bar", false}, {"foo?", false}, {"[bar]", false}, {"'foo'", false}, {"%bar", false}, {"&bar", false}, {"#1foo", false}, {"[email protected]", true}, {"bar@", false}, {"@local", false}, {"not/valid", false}, } { c.Logf("test %d: %s", i, t.string) c.Assert(names.IsValidUser(t.string), gc.Equals, t.expect, gc.Commentf("%s", t.string)) } }
// CreateEnvironment creates a new environment using the account and // environment config specified in the args. func (c *Client) CreateEnvironment(owner string, account, config map[string]interface{}) (params.Environment, error) { var result params.Environment if !names.IsValidUser(owner) { return result, errors.Errorf("invalid owner name %q", owner) } createArgs := params.EnvironmentCreateArgs{ OwnerTag: names.NewUserTag(owner).String(), Account: account, Config: config, } err := c.facade.FacadeCall("CreateEnvironment", createArgs, &result) if err != nil { return result, errors.Trace(err) } logger.Infof("created environment %s (%s)", result.Name, result.UUID) return result, nil }
// SetPassword changes the password for the specified user. func (c *Client) SetPassword(username, password string) error { if !names.IsValidUser(username) { return errors.Errorf("%q is not a valid username", username) } tag := names.NewUserTag(username) args := params.EntityPasswords{ Changes: []params.EntityPassword{{ Tag: tag.String(), Password: password}}, } var results params.ErrorResults err := c.facade.FacadeCall("SetPassword", args, &results) if err != nil { return err } return results.OneError() }
func (c *CreateEnvironmentCommand) Init(args []string) error { if len(args) == 0 { return errors.New("environment name is required") } c.name, args = args[0], args[1:] values, err := keyvalues.Parse(args, true) if err != nil { return err } c.confValues = values if c.owner != "" && !names.IsValidUser(c.owner) { return errors.Errorf("%q is not a valid user", c.owner) } return nil }
// UserInfo returns information about the specified users. If no users are // specified, the call should return all users. If includeDisabled is set to // ActiveUsers, only enabled users are returned. func (c *Client) UserInfo(usernames []string, all IncludeDisabled) ([]params.UserInfo, error) { var results params.UserInfoResults var entities []params.Entity for _, username := range usernames { if !names.IsValidUser(username) { return nil, errors.Errorf("%q is not a valid username", username) } tag := names.NewUserTag(username) entities = append(entities, params.Entity{Tag: tag.String()}) } args := params.UserInfoRequest{ Entities: entities, IncludeDisabled: bool(all), } err := c.facade.FacadeCall("UserInfo", args, &results) if err != nil { return nil, errors.Trace(err) } // Only need to look for errors if users were explicitly specified, because // if we didn't ask for any, we should get all, and we shouldn't get any // errors for listing all. We care here because we index into the users // slice. if len(results.Results) == len(usernames) { var errorStrings []string for i, result := range results.Results { if result.Error != nil { annotated := errors.Annotate(result.Error, usernames[i]) errorStrings = append(errorStrings, annotated.Error()) } } if len(errorStrings) > 0 { return nil, errors.New(strings.Join(errorStrings, ", ")) } } info := []params.UserInfo{} for i, result := range results.Results { if result.Result == nil { return nil, errors.Errorf("unexpected nil result at position %d", i) } info = append(info, *result.Result) } return info, nil }
func (c *Client) modifyModelUser(action params.ModelAction, user, access string, modelUUIDs []string) error { var args params.ModifyModelAccessRequest if !names.IsValidUser(user) { return errors.Errorf("invalid username: %q", user) } userTag := names.NewUserTag(user) accessPermission, err := ParseModelAccess(access) if err != nil { return errors.Trace(err) } for _, model := range modelUUIDs { if !names.IsValidModel(model) { return errors.Errorf("invalid model: %q", model) } modelTag := names.NewModelTag(model) args.Changes = append(args.Changes, params.ModifyModelAccess{ UserTag: userTag.String(), Action: action, Access: accessPermission, ModelTag: modelTag.String(), }) } var result params.ErrorResults err = c.facade.FacadeCall("ModifyModelAccess", args, &result) if err != nil { return errors.Trace(err) } if len(result.Results) != len(args.Changes) { return errors.Errorf("expected %d results, got %d", len(args.Changes), len(result.Results)) } for i, r := range result.Results { if r.Error != nil && r.Error.Code == params.CodeAlreadyExists { logger.Warningf("model %q is already shared with %q", modelUUIDs[i], userTag.Canonical()) result.Results[i].Error = nil } } return result.Combine() }
// Validate performs sanity checks on the migration configuration it // holds. func (s *ModelMigrationSpec) Validate() error { if !names.IsValidModel(s.ModelUUID) { return errors.NotValidf("model UUID") } if !names.IsValidModel(s.TargetControllerUUID) { return errors.NotValidf("controller UUID") } if len(s.TargetAddrs) < 1 { return errors.NotValidf("empty target API addresses") } if s.TargetCACert == "" { return errors.NotValidf("empty target CA cert") } if !names.IsValidUser(s.TargetUser) { return errors.NotValidf("target user") } if s.TargetPassword == "" { return errors.NotValidf("empty target password") } return nil }
func (c *createModelCommand) Init(args []string) error { if len(args) == 0 { return errors.New("model name is required") } c.Name, args = args[0], args[1:] values, err := keyvalues.Parse(args, true) if err != nil { return err } c.ConfValues = values if c.Owner != "" && !names.IsValidUser(c.Owner) { return errors.Errorf("%q is not a valid user", c.Owner) } if c.configParser == nil { c.configParser = common.ConformYAML } return nil }
// AddIdentity adds an identity to the database. func (st *State) AddIdentity(name, displayName, password, creator string) (*Identity, error) { // The name of the identity happens to match the regex we use to confirm user names. // Identities do not have tags, so there is no special function for identities. Given // the relationships between users and identities it seems reasonable to use the same // validation check. if !names.IsValidUser(name) { return nil, errors.Errorf("invalid identity name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } identity := &Identity{ st: st, doc: identityDoc{ Name: name, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: nowToTheSecond(), }, } ops := []txn.Op{{ C: identityCollectionName, Id: name, Assert: txn.DocMissing, Insert: &identity.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("identity already exists") } if err != nil { return nil, errors.Trace(err) } return identity, nil }
// Authenticate authenticates the provided entity. If there is no macaroon provided, it will // return a *DischargeRequiredError containing a macaroon that can be used to grant access. func (m *ExternalMacaroonAuthenticator) Authenticate(entityFinder EntityFinder, _ names.Tag, req params.LoginRequest) (state.Entity, error) { declared, err := m.Service.CheckAny(req.Macaroons, nil, checkers.New(checkers.TimeBefore)) if _, ok := errors.Cause(err).(*bakery.VerificationError); ok { return nil, m.newDischargeRequiredError(err) } if err != nil { return nil, errors.Trace(err) } username := declared[usernameKey] var tag names.UserTag if names.IsValidUserName(username) { // The name is a local name without an explicit @local suffix. // In this case, for compatibility with 3rd parties that don't // care to add their own domain, we add an @external domain // to ensure there is no confusion between local and external // users. // TODO(rog) remove this logic when deployed dischargers // always add an @ domain. tag = names.NewLocalUserTag(username).WithDomain("external") } else { // We have a name with an explicit domain (or an invalid user name). if !names.IsValidUser(username) { return nil, errors.Errorf("%q is an invalid user name", username) } tag = names.NewUserTag(username) if tag.IsLocal() { return nil, errors.Errorf("external identity provider has provided ostensibly local name %q", username) } } entity, err := entityFinder.FindEntity(tag) if errors.IsNotFound(err) { return nil, errors.Trace(common.ErrBadCreds) } else if err != nil { return nil, errors.Trace(err) } return entity, nil }
// Run implements Command.Run func (c *LoginCommand) Run(ctx *cmd.Context) error { // TODO(thumper): as we support the user and address // change this check here. if c.Server.Path == "" { return errors.New("no server file specified") } serverYAML, err := c.Server.Read(ctx) if err != nil { return errors.Trace(err) } var serverDetails envcmd.ServerFile if err := goyaml.Unmarshal(serverYAML, &serverDetails); err != nil { return errors.Trace(err) } // Construct the api.Info struct from the provided values // and attempt to connect to the remote server before we do anything else. if !names.IsValidUser(serverDetails.Username) { return errors.Errorf("%q is not a valid username", serverDetails.Username) } userTag := names.NewUserTag(serverDetails.Username) if userTag.Provider() != names.LocalProvider { // Remove users do not have their passwords stored in Juju // so we never attempt to change them. c.KeepPassword = true } info := api.Info{ Addrs: serverDetails.Addresses, CACert: serverDetails.CACert, Tag: userTag, Password: serverDetails.Password, } apiState, err := c.apiOpen(&info, api.DefaultDialOpts()) if err != nil { return errors.Trace(err) } defer apiState.Close() // If we get to here, the credentials supplied were sufficient to connect // to the Juju System and login. Now we cache the details. serverInfo, err := c.cacheConnectionInfo(serverDetails, apiState) if err != nil { return errors.Trace(err) } ctx.Infof("cached connection details as system %q", c.Name) // If we get to here, we have been able to connect to the API server, and // also have been able to write the cached information. Now we can change // the user's password to a new randomly generated strong password, and // update the cached information knowing that the likelihood of failure is // minimal. if !c.KeepPassword { if err := c.updatePassword(ctx, apiState, userTag, serverInfo); err != nil { return errors.Trace(err) } } return errors.Trace(envcmd.SetCurrentSystem(ctx, c.Name)) }
// Validate returns an error if the ModelMigrationSpec contains bad // data. Nil is returned otherwise. func (spec *ModelMigrationSpec) Validate() error { if !names.IsValidUser(spec.InitiatedBy.Id()) { return errors.NotValidf("InitiatedBy") } return spec.TargetInfo.Validate() }
// ParseReference parses the provided charm Reference string into its // respective structure and the targeted series, if present. func ParseReference(url string) (Reference, string, error) { r := Reference{Schema: "cs"} series := "" i := strings.Index(url, ":") if i >= 0 { r.Schema = url[:i] i++ } else { i = 0 } // cs: or local: if r.Schema != "cs" && r.Schema != "local" { return Reference{}, "", fmt.Errorf("charm URL has invalid schema: %q", url) } parts := strings.Split(url[i:], "/") if len(parts) < 1 || len(parts) > 3 { return Reference{}, "", fmt.Errorf("charm URL has invalid form: %q", url) } // ~<username> if strings.HasPrefix(parts[0], "~") { if r.Schema == "local" { return Reference{}, "", fmt.Errorf("local charm URL with user name: %q", url) } r.User = parts[0][1:] if !names.IsValidUser(r.User) { return Reference{}, "", fmt.Errorf("charm URL has invalid user name: %q", url) } parts = parts[1:] } // <series> if len(parts) == 2 { series = parts[0] if !IsValidSeries(series) { return Reference{}, "", fmt.Errorf("charm URL has invalid series: %q", url) } parts = parts[1:] } if len(parts) < 1 { return Reference{}, "", fmt.Errorf("charm URL without charm name: %q", url) } // <name>[-<revision>] r.Name = parts[0] r.Revision = -1 for i := len(r.Name) - 1; i > 0; i-- { c := r.Name[i] if c >= '0' && c <= '9' { continue } if c == '-' && i != len(r.Name)-1 { var err error r.Revision, err = strconv.Atoi(r.Name[i+1:]) if err != nil { panic(err) // We just checked it was right. } r.Name = r.Name[:i] } break } if !IsValidName(r.Name) { return Reference{}, "", fmt.Errorf("charm URL has invalid charm name: %q", url) } return r, series, nil }
func (c *createModelCommand) Run(ctx *cmd.Context) error { client, err := c.getAPI() if err != nil { return errors.Trace(err) } defer client.Close() store := c.ClientStore() controllerName := c.ControllerName() accountName, err := store.CurrentAccount(controllerName) if err != nil { return errors.Trace(err) } currentAccount, err := store.AccountByName(controllerName, accountName) if err != nil { return errors.Trace(err) } modelOwner := currentAccount.User if c.Owner != "" { if !names.IsValidUser(c.Owner) { return errors.Errorf("%q is not a valid user name", c.Owner) } modelOwner = names.NewUserTag(c.Owner).Canonical() } serverSkeleton, err := client.ConfigSkeleton(c.CloudType, "") if err != nil { return errors.Trace(err) } attrs, err := c.getConfigValues(ctx, serverSkeleton) if err != nil { return errors.Trace(err) } accountDetails := map[string]interface{}{} if c.CredentialName != "" { cred, _, _, err := modelcmd.GetCredentials( c.credentialStore, "", c.CredentialName, c.CloudName, c.CloudType, ) if err != nil { return errors.Trace(err) } for k, v := range cred.Attributes() { accountDetails[k] = v } } model, err := client.CreateModel(modelOwner, accountDetails, attrs) if err != nil { return errors.Trace(err) } if modelOwner == currentAccount.User { controllerName := c.ControllerName() accountName := c.AccountName() if err := store.UpdateModel(controllerName, accountName, c.Name, jujuclient.ModelDetails{ model.UUID, }); err != nil { return errors.Trace(err) } if err := store.SetCurrentModel(controllerName, accountName, c.Name); err != nil { return errors.Trace(err) } ctx.Infof("created model %q", c.Name) } else { ctx.Infof("created model %q for %q", c.Name, c.Owner) } return nil }