// 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 }
// CleanupOldMetrics removes old metrics from the collection. // The single arg params is expected to contain and environment uuid. // Even though the call will delete all metrics across environments // it serves to validate that the connection has access to at least one environment. func (api *MetricsManagerAPI) CleanupOldMetrics(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canAccess, err := api.accessEnviron() if err != nil { return result, err } for i, arg := range args.Entities { tag, err := names.ParseModelTag(arg.Tag) if err != nil { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } if !canAccess(tag) { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = api.state.CleanupOldMetrics() if err != nil { err = errors.Annotate(err, "failed to cleanup old metrics") result.Results[i].Error = common.ServerError(err) } } return result, nil }
// ModelStatus returns a status summary for each model tag passed in. func (c *Client) ModelStatus(tags ...names.ModelTag) ([]base.ModelStatus, error) { result := params.ModelStatusResults{} models := make([]params.Entity, len(tags)) for i, tag := range tags { models[i] = params.Entity{Tag: tag.String()} } req := params.Entities{ Entities: models, } if err := c.facade.FacadeCall("ModelStatus", req, &result); err != nil { return nil, err } results := make([]base.ModelStatus, len(result.Results)) for i, r := range result.Results { model, err := names.ParseModelTag(r.ModelTag) if err != nil { return nil, errors.Annotatef(err, "ModelTag %q at position %d", r.ModelTag, 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.ModelStatus{ UUID: model.Id(), Life: r.Life, Owner: owner.Canonical(), HostedMachineCount: r.HostedMachineCount, ServiceCount: r.ServiceCount, } } return results, nil }
// SendMetrics will send any unsent metrics onto the metric collection service. func (api *MetricsManagerAPI) SendMetrics(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canAccess, err := api.accessEnviron() if err != nil { return result, err } for i, arg := range args.Entities { tag, err := names.ParseModelTag(arg.Tag) if err != nil { result.Results[i].Error = common.ServerError(err) continue } if !canAccess(tag) { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = metricsender.SendMetrics(api.state, sender, maxBatchesPerSend) if err != nil { err = errors.Annotate(err, "failed to send metrics") logger.Warningf("%v", err) result.Results[i].Error = common.ServerError(err) continue } } return result, 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 }
// auth is very simplistic: it only accepts the model tag reported by // the backend. func (facade *Facade) auth(tagString string) error { tag, err := names.ParseModelTag(tagString) if err != nil { return errors.Trace(err) } if tag.Id() != facade.backend.ModelUUID() { return common.ErrPerm } return 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 }
func (api *API) getModel(args params.ModelArgs) (*state.Model, error) { tag, err := names.ParseModelTag(args.ModelTag) if err != nil { return nil, errors.Trace(err) } model, err := api.state.GetModel(tag) if err != nil { return nil, errors.Trace(err) } if model.MigrationMode() != state.MigrationModeImporting { return nil, errors.New("migration mode for the model is not importing") } return model, nil }
// EnableHASingle applies a single ControllersServersSpec specification to the current environment. // Exported so it can be called by the legacy client API in the client package. func EnableHASingle(st *state.State, spec params.ControllersSpec) (params.ControllersChanges, error) { if !st.IsController() { return params.ControllersChanges{}, errors.New("unsupported with hosted models") } // Check if changes are allowed and the command may proceed. blockChecker := common.NewBlockChecker(st) if err := blockChecker.ChangeAllowed(); err != nil { return params.ControllersChanges{}, errors.Trace(err) } // Validate the environment tag if present. if spec.ModelTag != "" { tag, err := names.ParseModelTag(spec.ModelTag) if err != nil { return params.ControllersChanges{}, errors.Errorf("invalid model tag: %v", err) } if _, err := st.FindEntity(tag); err != nil { return params.ControllersChanges{}, err } } series := spec.Series if series == "" { ssi, err := st.ControllerInfo() if err != nil { return params.ControllersChanges{}, err } // We should always have at least one voting machine // If we *really* wanted we could just pick whatever series is // in the majority, but really, if we always copy the value of // the first one, then they'll stay in sync. if len(ssi.VotingMachineIds) == 0 { // Better than a panic()? return params.ControllersChanges{}, fmt.Errorf("internal error, failed to find any voting machines") } templateMachine, err := st.Machine(ssi.VotingMachineIds[0]) if err != nil { return params.ControllersChanges{}, err } series = templateMachine.Series() } changes, err := st.EnableHA(spec.NumControllers, spec.Constraints, series, spec.Placement) if err != nil { return params.ControllersChanges{}, err } return controllersChanges(changes), nil }
func (c *ControllerAPI) environStatus(tag string) (params.ModelStatus, error) { var status params.ModelStatus modelTag, err := names.ParseModelTag(tag) if err != nil { return status, errors.Trace(err) } st, err := c.state.ForModel(modelTag) if err != nil { return status, errors.Trace(err) } defer st.Close() machines, err := st.AllMachines() if err != nil { return status, errors.Trace(err) } var hostedMachines []*state.Machine for _, m := range machines { if !m.IsManager() { hostedMachines = append(hostedMachines, m) } } services, err := st.AllServices() if err != nil { return status, errors.Trace(err) } env, err := st.Model() if err != nil { return status, errors.Trace(err) } if err != nil { return status, errors.Trace(err) } return params.ModelStatus{ ModelTag: tag, OwnerTag: env.Owner().String(), Life: params.Life(env.Life().String()), HostedMachineCount: len(hostedMachines), ServiceCount: len(services), }, 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 (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 } } }
// EnableHASingle applies a single ControllersServersSpec specification to the current environment. // Exported so it can be called by the legacy client API in the client package. func EnableHASingle(st *state.State, spec params.ControllersSpec) (params.ControllersChanges, error) { if !st.IsController() { return params.ControllersChanges{}, errors.New("unsupported with hosted models") } // Check if changes are allowed and the command may proceed. blockChecker := common.NewBlockChecker(st) if err := blockChecker.ChangeAllowed(); err != nil { return params.ControllersChanges{}, errors.Trace(err) } // Validate the environment tag if present. if spec.ModelTag != "" { tag, err := names.ParseModelTag(spec.ModelTag) if err != nil { return params.ControllersChanges{}, errors.Errorf("invalid model tag: %v", err) } if _, err := st.FindEntity(tag); err != nil { return params.ControllersChanges{}, err } } series := spec.Series if series == "" { ssi, err := st.ControllerInfo() if err != nil { return params.ControllersChanges{}, err } // We should always have at least one voting machine // If we *really* wanted we could just pick whatever series is // in the majority, but really, if we always copy the value of // the first one, then they'll stay in sync. if len(ssi.VotingMachineIds) == 0 { // Better than a panic()? return params.ControllersChanges{}, errors.Errorf("internal error, failed to find any voting machines") } templateMachine, err := st.Machine(ssi.VotingMachineIds[0]) if err != nil { return params.ControllersChanges{}, err } series = templateMachine.Series() } if constraints.IsEmpty(&spec.Constraints) { // No constraints specified, so we'll use the constraints off // a running controller. controllerInfo, err := st.ControllerInfo() if err != nil { return params.ControllersChanges{}, err } // We'll sort the controller ids to find the smallest. // This will typically give the initial bootstrap machine. var controllerIds []int for _, id := range controllerInfo.MachineIds { idNum, err := strconv.Atoi(id) if err != nil { logger.Warningf("ignoring non numeric controller id %v", id) continue } controllerIds = append(controllerIds, idNum) } if len(controllerIds) == 0 { errors.Errorf("internal error, failed to find any controllers") } sort.Ints(controllerIds) // Load the controller machine and get its constraints. controllerId := controllerIds[0] controller, err := st.Machine(strconv.Itoa(controllerId)) if err != nil { return params.ControllersChanges{}, errors.Annotatef(err, "reading controller id %v", controllerId) } spec.Constraints, err = controller.Constraints() if err != nil { return params.ControllersChanges{}, errors.Annotatef(err, "reading constraints for controller id %v", controllerId) } } changes, err := st.EnableHA(spec.NumControllers, spec.Constraints, series, spec.Placement) if err != nil { return params.ControllersChanges{}, err } return controllersChanges(changes), nil }
func (s *fakeHomeSuite) TestModelTagValid(c *gc.C) { asString := testing.ModelTag.String() tag, err := names.ParseModelTag(asString) c.Assert(err, jc.ErrorIsNil) c.Assert(tag, gc.Equals, testing.ModelTag) }
func (formatter_1_18) unmarshal(data []byte) (*configInternal, error) { // NOTE: this needs to handle the absence of StatePort and get it from the // address var format format_1_18Serialization if err := goyaml.Unmarshal(data, &format); err != nil { return nil, err } if format.UpgradedToVersion == nil || *format.UpgradedToVersion == version.Zero { // Assume we upgrade from 1.16. upgradedToVersion := version.MustParse("1.16.0") format.UpgradedToVersion = &upgradedToVersion } tag, err := names.ParseTag(format.Tag) if err != nil { return nil, err } var modelTag names.ModelTag if format.Model != "" { modelTag, err = names.ParseModelTag(format.Model) if err != nil { return nil, errors.Trace(err) } } config := &configInternal{ tag: tag, paths: NewPathsWithDefaults(Paths{ DataDir: format.DataDir, LogDir: format.LogDir, MetricsSpoolDir: format.MetricsSpoolDir, }), jobs: format.Jobs, upgradedToVersion: *format.UpgradedToVersion, nonce: format.Nonce, model: modelTag, caCert: format.CACert, oldPassword: format.OldPassword, values: format.Values, preferIPv6: format.PreferIPv6, } if len(format.StateAddresses) > 0 { config.stateDetails = &connectionDetails{ format.StateAddresses, format.StatePassword, } } if len(format.APIAddresses) > 0 { config.apiDetails = &connectionDetails{ format.APIAddresses, format.APIPassword, } } if len(format.ControllerKey) != 0 { config.servingInfo = ¶ms.StateServingInfo{ Cert: format.ControllerCert, PrivateKey: format.ControllerKey, CAPrivateKey: format.CAPrivateKey, APIPort: format.APIPort, StatePort: format.StatePort, SharedSecret: format.SharedSecret, SystemIdentity: format.SystemIdentity, } // There's a private key, then we need the state port, // which wasn't always in the 1.18 format. If it's not present // we can infer it from the ports in the state addresses. if config.servingInfo.StatePort == 0 { if len(format.StateAddresses) == 0 { return nil, fmt.Errorf("server key found but no state port") } _, portString, err := net.SplitHostPort(format.StateAddresses[0]) if err != nil { return nil, err } statePort, err := strconv.Atoi(portString) if err != nil { return nil, err } config.servingInfo.StatePort = statePort } } // Mongo version is set, we might be running a version other than default. if format.MongoVersion != "" { config.mongoVersion = format.MongoVersion } return config, nil }
// AddUser adds a user with a username, and either a password or // a randomly generated secret key which will be returned. func (api *UserManagerAPI) AddUser(args params.AddUsers) (params.AddUserResults, error) { result := params.AddUserResults{ Results: make([]params.AddUserResult, len(args.Users)), } if err := api.check.ChangeAllowed(); err != nil { return result, errors.Trace(err) } if len(args.Users) == 0 { return result, nil } loggedInUser, err := api.getLoggedInUser() if err != nil { return result, errors.Wrap(err, common.ErrPerm) } // TODO(thumper): PERMISSIONS Change this permission check when we have // real permissions. For now, only the owner of the initial model is // able to add users. if err := api.permissionCheck(loggedInUser); err != nil { return result, errors.Trace(err) } for i, arg := range args.Users { var user *state.User if arg.Password != "" { user, err = api.state.AddUser(arg.Username, arg.DisplayName, arg.Password, loggedInUser.Id()) } else { user, err = api.state.AddUserWithSecretKey(arg.Username, arg.DisplayName, loggedInUser.Id()) } if err != nil { err = errors.Annotate(err, "failed to create user") result.Results[i].Error = common.ServerError(err) continue } else { result.Results[i] = params.AddUserResult{ Tag: user.Tag().String(), SecretKey: user.SecretKey(), } } if len(arg.SharedModelTags) > 0 { modelAccess, err := modelmanager.FromModelAccessParam(arg.ModelAccess) if err != nil { err = errors.Annotatef(err, "user %q created but models not shared", arg.Username) result.Results[i].Error = common.ServerError(err) continue } userTag := user.Tag().(names.UserTag) for _, modelTagStr := range arg.SharedModelTags { modelTag, err := names.ParseModelTag(modelTagStr) if err != nil { err = errors.Annotatef(err, "user %q created but model %q not shared", arg.Username, modelTagStr) result.Results[i].Error = common.ServerError(err) break } err = modelmanager.ChangeModelAccess(api.state, modelTag, loggedInUser, userTag, params.GrantModelAccess, modelAccess) if err != nil { err = errors.Annotatef(err, "user %q created but model %q not shared", arg.Username, modelTagStr) result.Results[i].Error = common.ServerError(err) break } } } } return result, nil }
// ControllerTag returns the tag of the server we are connected to. func (s *state) ControllerTag() (names.ModelTag, error) { return names.ParseModelTag(s.controllerTag) }
// ModelTag returns the tag of the model we are connected to. func (s *state) ModelTag() (names.ModelTag, error) { return names.ParseModelTag(s.modelTag) }
// ModelInfo returns information about the specified models. func (m *ModelManagerAPI) ModelInfo(args params.Entities) (params.ModelInfoResults, error) { results := params.ModelInfoResults{ Results: make([]params.ModelInfoResult, len(args.Entities)), } getModelInfo := func(arg params.Entity) (params.ModelInfo, error) { tag, err := names.ParseModelTag(arg.Tag) if err != nil { return params.ModelInfo{}, err } st, err := m.state.ForModel(tag) if errors.IsNotFound(err) { return params.ModelInfo{}, common.ErrPerm } else if err != nil { return params.ModelInfo{}, err } defer st.Close() model, err := st.Model() if errors.IsNotFound(err) { return params.ModelInfo{}, common.ErrPerm } else if err != nil { return params.ModelInfo{}, err } cfg, err := model.Config() if err != nil { return params.ModelInfo{}, err } users, err := model.Users() if err != nil { return params.ModelInfo{}, err } status, err := model.Status() if err != nil { return params.ModelInfo{}, err } owner := model.Owner() info := params.ModelInfo{ Name: cfg.Name(), UUID: cfg.UUID(), ControllerUUID: cfg.ControllerUUID(), OwnerTag: owner.String(), Life: params.Life(model.Life().String()), Status: common.EntityStatusFromState(status), ProviderType: cfg.Type(), DefaultSeries: config.PreferredSeries(cfg), } authorizedOwner := m.authCheck(owner) == nil for _, user := range users { if !authorizedOwner && m.authCheck(user.UserTag()) != nil { // The authenticated user is neither the owner // nor administrator, nor the model user, so // has no business knowing about the model user. continue } userInfo, err := common.ModelUserInfo(user) if err != nil { return params.ModelInfo{}, errors.Trace(err) } info.Users = append(info.Users, userInfo) } if len(info.Users) == 0 { // No users, which means the authenticated user doesn't // have access to the model. return params.ModelInfo{}, common.ErrPerm } return info, nil } for i, arg := range args.Entities { modelInfo, err := getModelInfo(arg) if err != nil { results.Results[i].Error = common.ServerError(err) continue } results.Results[i].Result = &modelInfo } return results, nil }
// AddUser adds a user with a username, and either a password or // a randomly generated secret key which will be returned. func (api *UserManagerAPI) AddUser(args params.AddUsers) (params.AddUserResults, error) { result := params.AddUserResults{ Results: make([]params.AddUserResult, len(args.Users)), } if err := api.check.ChangeAllowed(); err != nil { return result, errors.Trace(err) } if len(args.Users) == 0 { return result, nil } if !api.isAdmin { return result, common.ErrPerm } for i, arg := range args.Users { var user *state.User var err error if arg.Password != "" { user, err = api.state.AddUser(arg.Username, arg.DisplayName, arg.Password, api.apiUser.Id()) } else { user, err = api.state.AddUserWithSecretKey(arg.Username, arg.DisplayName, api.apiUser.Id()) } if err != nil { err = errors.Annotate(err, "failed to create user") result.Results[i].Error = common.ServerError(err) continue } else { result.Results[i] = params.AddUserResult{ Tag: user.Tag().String(), SecretKey: user.SecretKey(), } } if len(arg.SharedModelTags) > 0 { modelAccess, err := modelmanager.FromModelAccessParam(arg.ModelAccess) if err != nil { err = errors.Annotatef(err, "user %q created but models not shared", arg.Username) result.Results[i].Error = common.ServerError(err) continue } userTag := user.Tag().(names.UserTag) for _, modelTagStr := range arg.SharedModelTags { modelTag, err := names.ParseModelTag(modelTagStr) if err != nil { err = errors.Annotatef(err, "user %q created but model %q not shared", arg.Username, modelTagStr) result.Results[i].Error = common.ServerError(err) break } err = modelmanager.ChangeModelAccess( modelmanager.NewStateBackend(api.state), modelTag, api.apiUser, userTag, params.GrantModelAccess, modelAccess, api.isAdmin) if err != nil { err = errors.Annotatef(err, "user %q created but model %q not shared", arg.Username, modelTagStr) result.Results[i].Error = common.ServerError(err) break } } } } return result, nil }