// 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 }
// UserInfo returns information on a user. func (api *UserManagerAPI) UserInfo(args params.Entities) (UserInfoResults, error) { results := UserInfoResults{ Results: make([]UserInfoResult, len(args.Entities)), } for i, userArg := range args.Entities { tag, err := names.ParseUserTag(userArg.Tag) if err != nil { results.Results[0].Error = common.ServerError(err) continue } user, err := api.state.User(tag) var result UserInfoResult if err != nil { if errors.IsNotFound(err) { result.Error = common.ServerError(common.ErrPerm) } else { result.Error = common.ServerError(err) } } else { info := UserInfo{ Username: tag.Name(), DisplayName: user.DisplayName(), CreatedBy: user.CreatedBy(), DateCreated: user.DateCreated(), LastConnection: user.LastLogin(), } result.Result = &info } results.Results[i] = result } return results, 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 }
// 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 (em *EnvironmentManagerAPI) ListEnvironments(user params.Entity) (params.EnvironmentList, error) { result := params.EnvironmentList{} stateServerEnv, err := em.state.StateServerEnvironment() if err != nil { return result, errors.Trace(err) } adminUser := stateServerEnv.Owner() userTag, err := names.ParseUserTag(user.Tag) if err != nil { return result, errors.Trace(err) } err = em.authCheck(userTag, adminUser) if err != nil { return result, errors.Trace(err) } environments, err := em.state.EnvironmentsForUser(userTag) if err != nil { return result, errors.Trace(err) } for _, env := range environments { result.Environments = append(result.Environments, params.Environment{ Name: env.Name(), UUID: env.UUID(), OwnerTag: env.Owner().String(), }) logger.Debugf("list env: %s, %s, %s", env.Name(), env.UUID(), env.Owner()) } return result, nil }
// EnvironmentStatus returns a status summary for each environment tag passed in. func (c *Client) EnvironmentStatus(tags ...names.EnvironTag) ([]base.EnvironmentStatus, error) { result := params.EnvironmentStatusResults{} envs := make([]params.Entity, len(tags)) for i, tag := range tags { envs[i] = params.Entity{Tag: tag.String()} } req := params.Entities{ Entities: envs, } if err := c.facade.FacadeCall("EnvironmentStatus", req, &result); err != nil { return nil, err } results := make([]base.EnvironmentStatus, len(result.Results)) for i, r := range result.Results { env, err := names.ParseEnvironTag(r.EnvironTag) if err != nil { return nil, errors.Annotatef(err, "EnvironTag %q at position %d", r.EnvironTag, i) } owner, err := names.ParseUserTag(r.OwnerTag) if err != nil { return nil, errors.Annotatef(err, "OwnerTag %q at position %d", r.OwnerTag, i) } results[i] = base.EnvironmentStatus{ UUID: env.Id(), Life: r.Life, Owner: owner.Canonical(), HostedMachineCount: r.HostedMachineCount, ServiceCount: r.ServiceCount, } } return results, nil }
// cacheAPIInfo updates the local environment settings (.jenv file) // with the provided apiInfo, assuming we've just successfully // connected to the API server. func cacheAPIInfo(info configstore.EnvironInfo, apiInfo *api.Info) (err error) { defer errors.Contextf(&err, "failed to cache API credentials") environUUID := "" if apiInfo.EnvironTag != "" { tag, err := names.ParseEnvironTag(apiInfo.Tag) if err != nil { return err } environUUID = tag.Id() } info.SetAPIEndpoint(configstore.APIEndpoint{ Addresses: apiInfo.Addrs, CACert: string(apiInfo.CACert), EnvironUUID: environUUID, }) tag, err := names.ParseUserTag(apiInfo.Tag) if err != nil { return err } info.SetAPICredentials(configstore.APICredentials{ User: tag.Id(), Password: apiInfo.Password, }) return info.Write() }
// authenticate parses HTTP basic authentication and authorizes the // request by looking up the provided tag and password against state. func (h *httpHandler) authenticate(r *http.Request) error { parts := strings.Fields(r.Header.Get("Authorization")) if len(parts) != 2 || parts[0] != "Basic" { // Invalid header format or no header provided. return fmt.Errorf("invalid request format") } // Challenge is a base64-encoded "tag:pass" string. // See RFC 2617, Section 2. challenge, err := base64.StdEncoding.DecodeString(parts[1]) if err != nil { return fmt.Errorf("invalid request format") } tagPass := strings.SplitN(string(challenge), ":", 2) if len(tagPass) != 2 { return fmt.Errorf("invalid request format") } // Only allow users, not agents. if _, err := names.ParseUserTag(tagPass[0]); err != nil { return common.ErrBadCreds } // Ensure the credentials are correct. _, err = checkCreds(h.state, params.Creds{ AuthTag: tagPass[0], Password: tagPass[1], }) return err }
// ModifyModelAccess changes the model access granted to users. func (m *ModelManagerAPI) ModifyModelAccess(args params.ModifyModelAccessRequest) (result params.ErrorResults, err error) { result = params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } for i, arg := range args.Changes { modelAccess, err := FromModelAccessParam(arg.Access) if err != nil { err = errors.Annotate(err, "could not modify model access") result.Results[i].Error = common.ServerError(err) continue } targetUserTag, err := names.ParseUserTag(arg.UserTag) if err != nil { result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access")) continue } modelTag, err := names.ParseModelTag(arg.ModelTag) if err != nil { result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access")) continue } result.Results[i].Error = common.ServerError( ChangeModelAccess(m.state, modelTag, m.apiUser, targetUserTag, arg.Action, modelAccess, m.isAdmin)) } return result, nil }
func (api *UserManagerAPI) getUser(tag string) (*state.User, error) { userTag, err := names.ParseUserTag(tag) if err != nil { return nil, errors.Trace(err) } user, err := api.state.User(userTag) if err != nil { return nil, errors.Wrap(err, common.ErrPerm) } return user, nil }
func (s *userSuite) TestParseUserTag(c *gc.C) { for i, t := range parseUserTagTests { c.Logf("test %d: %s", i, t.tag) got, err := names.ParseUserTag(t.tag) if err != nil || t.err != nil { c.Check(err, gc.DeepEquals, t.err) continue } c.Check(got, gc.FitsTypeOf, t.expected) c.Check(got, gc.Equals, t.expected) } }
// TargetInfo implements ModelMigration. func (mig *modelMigration) TargetInfo() (*migration.TargetInfo, error) { authTag, err := names.ParseUserTag(mig.doc.TargetAuthTag) if err != nil { return nil, errors.Trace(err) } return &migration.TargetInfo{ ControllerTag: names.NewModelTag(mig.doc.TargetController), Addrs: mig.doc.TargetAddrs, CACert: mig.doc.TargetCACert, AuthTag: authTag, Password: mig.doc.TargetPassword, }, nil }
func (s *Service) ownerTag() (names.UserTag, error) { var invalidTag names.UserTag var result params.StringResult args := params.Entities{ Entities: []params.Entity{{Tag: s.tag.String()}}, } err := s.st.facade.FacadeCall("GetOwnerTag", args, &result) if err != nil { return invalidTag, err } if result.Error != nil { return invalidTag, result.Error } return names.ParseUserTag(result.Result) }
// 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 (c *ControllerAPI) initiateOneModelMigration(spec params.ModelMigrationSpec) (string, error) { modelTag, err := names.ParseModelTag(spec.ModelTag) if err != nil { return "", errors.Annotate(err, "model tag") } // Ensure the model exists. if _, err := c.state.GetModel(modelTag); err != nil { return "", errors.Annotate(err, "unable to read model") } // Get State for model. hostedState, err := c.state.ForModel(modelTag) if err != nil { return "", errors.Trace(err) } defer hostedState.Close() // Start the migration. targetInfo := spec.TargetInfo controllerTag, err := names.ParseModelTag(targetInfo.ControllerTag) if err != nil { return "", errors.Annotate(err, "controller tag") } authTag, err := names.ParseUserTag(targetInfo.AuthTag) if err != nil { return "", errors.Annotate(err, "auth tag") } args := state.ModelMigrationSpec{ InitiatedBy: c.apiUser, TargetInfo: migration.TargetInfo{ ControllerTag: controllerTag, Addrs: targetInfo.Addrs, CACert: targetInfo.CACert, AuthTag: authTag, Password: targetInfo.Password, }, } mig, err := hostedState.CreateModelMigration(args) if err != nil { return "", errors.Trace(err) } return mig.Id(), nil }
func (c *showModelCommand) apiModelInfoToModelInfoMap(modelInfo []params.ModelInfo) (map[string]ModelInfo, error) { output := make(map[string]ModelInfo) for _, info := range modelInfo { tag, err := names.ParseUserTag(info.OwnerTag) if err != nil { return nil, errors.Trace(err) } output[info.Name] = ModelInfo{ UUID: info.UUID, ControllerUUID: info.ControllerUUID, Owner: tag.Id(), ProviderType: info.ProviderType, Users: apiUsersToUserInfoMap(info.Users), } } return output, nil }
func (w *migrationMasterWatcher) loop() error { w.newResult = func() interface{} { return new(params.ModelMigrationTargetInfo) } w.call = makeWatcherAPICaller(w.caller, "MigrationMasterWatcher", w.id) w.commonWatcher.init() go w.commonLoop() for { var data interface{} var ok bool select { case data, ok = <-w.in: if !ok { // The tomb is already killed with the correct error // at this point, so just return. return nil } case <-w.tomb.Dying(): return nil } info := data.(*params.ModelMigrationTargetInfo) controllerTag, err := names.ParseModelTag(info.ControllerTag) if err != nil { return errors.Annotatef(err, "unable to parse %q", info.ControllerTag) } authTag, err := names.ParseUserTag(info.AuthTag) if err != nil { return errors.Annotatef(err, "unable to parse %q", info.AuthTag) } outInfo := migration.TargetInfo{ ControllerTag: controllerTag, Addrs: info.Addrs, CACert: info.CACert, AuthTag: authTag, Password: info.Password, } select { case w.out <- outInfo: case <-w.tomb.Dying(): return nil } } }
// ModifyModelAccess changes the model access granted to users. func (em *ModelManagerAPI) ModifyModelAccess(args params.ModifyModelAccessRequest) (result params.ErrorResults, err error) { // API user must be a controller admin. createdBy, _ := em.authorizer.GetAuthTag().(names.UserTag) isAdmin, err := em.state.IsControllerAdministrator(createdBy) if err != nil { return result, errors.Trace(err) } if !isAdmin { return result, errors.New("only controller admins can grant or revoke model access") } result = params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } for i, arg := range args.Changes { modelAccess, err := FromModelAccessParam(arg.Access) if err != nil { err = errors.Annotate(err, "could not modify model access") result.Results[i].Error = common.ServerError(err) continue } userTagString := arg.UserTag user, err := names.ParseUserTag(userTagString) if err != nil { result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access")) continue } modelTagString := arg.ModelTag model, err := names.ParseModelTag(modelTagString) if err != nil { result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not modify model access")) continue } result.Results[i].Error = common.ServerError( ChangeModelAccess(em.state, model, createdBy, user, arg.Action, modelAccess)) } return result, nil }
func (s *Service) serviceOwnerTag() (names.UserTag, error) { var invalidTag names.UserTag var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: s.tag.String()}}, } err := s.st.facade.FacadeCall("ServiceOwner", args, &results) if err != nil { return invalidTag, err } if len(results.Results) != 1 { return invalidTag, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return invalidTag, result.Error } return names.ParseUserTag(result.Result) }
// CreateEnvironment creates a new environment using the account and // environment config specified in the args. func (em *EnvironmentManagerAPI) CreateEnvironment(args params.EnvironmentCreateArgs) (params.Environment, error) { result := params.Environment{} // Get the state server environment first. We need it both for the state // server owner and the ability to get the config. stateServerEnv, err := em.state.StateServerEnvironment() if err != nil { return result, errors.Trace(err) } adminUser := stateServerEnv.Owner() ownerTag, err := names.ParseUserTag(args.OwnerTag) if err != nil { return result, errors.Trace(err) } // Any user is able to create themselves an environment (until real fine // grain permissions are available), and admins (the creator of the state // server environment) are able to create environments for other people. err = em.authCheck(ownerTag, adminUser) if err != nil { return result, errors.Trace(err) } newConfig, err := em.newEnvironmentConfig(args, stateServerEnv) if err != nil { return result, errors.Trace(err) } // NOTE: check the agent-version of the config, and if it is > the current // version, it is not supported, also check existing tools, and if we don't // have tools for that version, also die. env, st, err := em.state.NewEnvironment(newConfig, ownerTag) if err != nil { return result, errors.Annotate(err, "failed to create new environment") } defer st.Close() result.Name = env.Name() result.UUID = env.UUID() result.OwnerTag = env.Owner().String() return result, nil }
// GetMigrationStatus implements Client. func (c *client) GetMigrationStatus() (MigrationStatus, error) { var empty MigrationStatus var status params.FullMigrationStatus err := c.caller.FacadeCall("GetMigrationStatus", nil, &status) if err != nil { return empty, errors.Trace(err) } modelTag, err := names.ParseModelTag(status.Spec.ModelTag) if err != nil { return empty, errors.Annotatef(err, "parsing model tag") } phase, ok := migration.ParsePhase(status.Phase) if !ok { return empty, errors.New("unable to parse phase") } target := status.Spec.TargetInfo controllerTag, err := names.ParseModelTag(target.ControllerTag) if err != nil { return empty, errors.Annotatef(err, "parsing controller tag") } authTag, err := names.ParseUserTag(target.AuthTag) if err != nil { return empty, errors.Annotatef(err, "unable to parse auth tag") } return MigrationStatus{ ModelUUID: modelTag.Id(), Attempt: status.Attempt, Phase: phase, TargetInfo: migration.TargetInfo{ ControllerTag: controllerTag, Addrs: target.Addrs, CACert: target.CACert, AuthTag: authTag, Password: target.Password, }, }, 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 (em *ModelManagerAPI) ListModels(user params.Entity) (params.UserModelList, error) { result := params.UserModelList{} userTag, err := names.ParseUserTag(user.Tag) if err != nil { return result, errors.Trace(err) } err = em.authCheck(userTag) if err != nil { return result, errors.Trace(err) } models, err := em.state.ModelsForUser(userTag) if err != nil { return result, errors.Trace(err) } for _, model := range models { var lastConn *time.Time userLastConn, err := model.LastConnection() if err != nil { if !state.IsNeverConnectedError(err) { return result, errors.Trace(err) } } else { lastConn = &userLastConn } result.UserModels = append(result.UserModels, params.UserModel{ Model: params.Model{ Name: model.Name(), UUID: model.UUID(), OwnerTag: model.Owner().String(), }, LastConnection: lastConn, }) logger.Debugf("list models: %s, %s, %s", model.Name(), model.UUID(), model.Owner()) } return result, nil }
// CreateModel creates a new model using the account and // model config specified in the args. func (mm *ModelManagerAPI) CreateModel(args params.ModelCreateArgs) (params.Model, error) { result := params.Model{} // Get the controller model first. We need it both for the state // server owner and the ability to get the config. controllerModel, err := mm.state.ControllerModel() if err != nil { return result, errors.Trace(err) } ownerTag, err := names.ParseUserTag(args.OwnerTag) if err != nil { return result, errors.Trace(err) } // Any user is able to create themselves an model (until real fine // grain permissions are available), and admins (the creator of the state // server model) are able to create models for other people. err = mm.authCheck(ownerTag) if err != nil { return result, errors.Trace(err) } newConfig, err := mm.newModelConfig(args, controllerModel) if err != nil { return result, errors.Annotate(err, "failed to create config") } // NOTE: check the agent-version of the config, and if it is > the current // version, it is not supported, also check existing tools, and if we don't // have tools for that version, also die. model, st, err := mm.state.NewModel(state.ModelArgs{Config: newConfig, Owner: ownerTag}) if err != nil { return result, errors.Annotate(err, "failed to create new model") } defer st.Close() result.Name = model.Name() result.UUID = model.UUID() result.OwnerTag = model.Owner().String() 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 (em *EnvironmentManagerAPI) ListEnvironments(user params.Entity) (params.UserEnvironmentList, error) { result := params.UserEnvironmentList{} userTag, err := names.ParseUserTag(user.Tag) if err != nil { return result, errors.Trace(err) } err = em.authCheck(userTag) if err != nil { return result, errors.Trace(err) } environments, err := em.state.EnvironmentsForUser(userTag) if err != nil { return result, errors.Trace(err) } for _, env := range environments { var lastConn *time.Time userLastConn, err := env.LastConnection() if err != nil { if !state.IsNeverConnectedError(err) { return result, errors.Trace(err) } } else { lastConn = &userLastConn } result.UserEnvironments = append(result.UserEnvironments, params.UserEnvironment{ Environment: params.Environment{ Name: env.Name(), UUID: env.UUID(), OwnerTag: env.Owner().String(), }, LastConnection: lastConn, }) logger.Debugf("list env: %s, %s, %s", env.Name(), env.UUID(), env.Owner()) } return result, nil }
// ShareEnvironment manages allowing and denying the given user(s) access to the environment. func (c *Client) ShareEnvironment(args params.ModifyEnvironUsers) (result params.ErrorResults, err error) { var createdBy names.UserTag var ok bool if createdBy, ok = c.api.auth.GetAuthTag().(names.UserTag); !ok { return result, errors.Errorf("api connection is not through a user") } result = params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } for i, arg := range args.Changes { userTagString := arg.UserTag user, err := names.ParseUserTag(userTagString) if err != nil { result.Results[i].Error = common.ServerError(errors.Annotate(err, "could not share environment")) continue } switch arg.Action { case params.AddEnvUser: _, err := c.api.stateAccessor.AddEnvironmentUser( state.EnvUserSpec{User: user, CreatedBy: createdBy}) if err != nil { err = errors.Annotate(err, "could not share environment") result.Results[i].Error = common.ServerError(err) } case params.RemoveEnvUser: err := c.api.stateAccessor.RemoveEnvironmentUser(user) if err != nil { err = errors.Annotate(err, "could not unshare environment") result.Results[i].Error = common.ServerError(err) } default: result.Results[i].Error = common.ServerError(errors.Errorf("unknown action %q", arg.Action)) } } return result, nil }
// AllEnvironments allows system administrators to get the list of all the // environments in the system. func (c *Client) AllEnvironments() ([]base.UserEnvironment, error) { var environments params.UserEnvironmentList err := c.facade.FacadeCall("AllEnvironments", nil, &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 }
// AllModels allows controller administrators to get the list of all the // models in the controller. func (c *Client) AllModels() ([]base.UserModel, error) { var models params.UserModelList err := c.facade.FacadeCall("AllModels", nil, &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 }
// ModelInfoFromParams translates a params.ModelInfo to ModelInfo. func ModelInfoFromParams(info params.ModelInfo, now time.Time) (ModelInfo, error) { tag, err := names.ParseUserTag(info.OwnerTag) if err != nil { return ModelInfo{}, errors.Trace(err) } status := ModelStatus{ Current: info.Status.Status, Message: info.Status.Info, } if info.Status.Since != nil { status.Since = UserFriendlyDuration(*info.Status.Since, now) } return ModelInfo{ Name: info.Name, UUID: info.UUID, ControllerUUID: info.ControllerUUID, Owner: tag.Id(), Life: string(info.Life), Status: status, ProviderType: info.ProviderType, Users: ModelUserInfoFromParams(info.Users, now), }, nil }
func (s *userSuite) TestParseUserTag(c *gc.C) { for i, t := range []struct { tag string expected names.Tag err error }{{ tag: "", err: names.InvalidTagError("", ""), }, { tag: "user-dave", expected: names.NewUserTag("dave"), }, { tag: "user-dave@local", expected: names.NewUserTag("dave@local"), }, { tag: "user-dave@foobar", expected: names.NewUserTag("dave@foobar"), }, { tag: "dave", err: names.InvalidTagError("dave", ""), }, { tag: "unit-dave", err: names.InvalidTagError("unit-dave", names.UnitTagKind), // not a valid unit name either }, { tag: "service-dave", err: names.InvalidTagError("service-dave", names.UserTagKind), }} { c.Logf("test %d: %s", i, t.tag) got, err := names.ParseUserTag(t.tag) if err != nil || t.err != nil { c.Check(err, gc.DeepEquals, t.err) continue } c.Check(got, gc.FitsTypeOf, t.expected) c.Check(got, gc.Equals, t.expected) } }
func (s *KillSuite) TestControllerStatus(c *gc.C) { s.api.allEnvs = []base.UserModel{ {Name: "admin", UUID: "123", Owner: names.NewUserTag("admin").String(), }, {Name: "env1", UUID: "456", Owner: names.NewUserTag("bob").String(), }, {Name: "env2", UUID: "789", Owner: names.NewUserTag("jo").String(), }, } s.api.envStatus = make(map[string]base.ModelStatus) for _, env := range s.api.allEnvs { owner, err := names.ParseUserTag(env.Owner) c.Assert(err, jc.ErrorIsNil) s.api.envStatus[env.UUID] = base.ModelStatus{ UUID: env.UUID, Life: params.Dying, HostedMachineCount: 2, ServiceCount: 1, Owner: owner.Canonical(), } } ctrStatus, envsStatus, err := controller.NewData(s.api, "123") c.Assert(err, jc.ErrorIsNil) c.Assert(ctrStatus.HostedModelCount, gc.Equals, 2) c.Assert(ctrStatus.HostedMachineCount, gc.Equals, 6) c.Assert(ctrStatus.ServiceCount, gc.Equals, 3) c.Assert(envsStatus, gc.HasLen, 2) for i, expected := range []struct { Owner string Name string Life params.Life HostedMachineCount int ServiceCount int }{ { Owner: "bob@local", Name: "env1", Life: params.Dying, HostedMachineCount: 2, ServiceCount: 1, }, { Owner: "jo@local", Name: "env2", Life: params.Dying, HostedMachineCount: 2, ServiceCount: 1, }, } { c.Assert(envsStatus[i].Owner, gc.Equals, expected.Owner) c.Assert(envsStatus[i].Name, gc.Equals, expected.Name) c.Assert(envsStatus[i].Life, gc.Equals, expected.Life) c.Assert(envsStatus[i].HostedMachineCount, gc.Equals, expected.HostedMachineCount) c.Assert(envsStatus[i].ServiceCount, gc.Equals, expected.ServiceCount) } }