// updateAllMachines finds all machines and resets the stored state address // in each of them. The address does not include the port. func updateAllMachines(apiState *api.State, stateAddr string) error { client := apiState.Client() status, err := client.Status(nil) if err != nil { return errors.Annotate(err, "cannot get status") } pendingMachineCount := 0 done := make(chan error) for _, machineStatus := range status.Machines { // A newly resumed state server requires no updating, and more // than one state server is not yet support by this plugin. if machineStatus.HasVote || machineStatus.WantsVote || params.Life(machineStatus.Life) == params.Dead { continue } pendingMachineCount++ machine := machineStatus go func() { err := runMachineUpdate(client, machine.Id, setAgentAddressScript(stateAddr)) if err != nil { logger.Errorf("failed to update machine %s: %v", machine.Id, err) } else { progress("updated machine %s", machine.Id) } done <- err }() } err = nil for ; pendingMachineCount > 0; pendingMachineCount-- { if updateErr := <-done; updateErr != nil && err == nil { err = errors.Annotate(updateErr, "machine update failed") } } return err }
// ModelInfo returns information about the current model. func (c *Client) ModelInfo() (params.ModelInfo, error) { if err := c.checkCanWrite(); err != nil { return params.ModelInfo{}, err } state := c.api.stateAccessor conf, err := state.ModelConfig() if err != nil { return params.ModelInfo{}, err } model, err := state.Model() if err != nil { return params.ModelInfo{}, err } info := params.ModelInfo{ DefaultSeries: config.PreferredSeries(conf), CloudTag: names.NewCloudTag(model.Cloud()).String(), CloudRegion: model.CloudRegion(), ProviderType: conf.Type(), Name: conf.Name(), UUID: model.UUID(), OwnerTag: model.Owner().String(), Life: params.Life(model.Life().String()), } if tag, ok := model.CloudCredential(); ok { info.CloudCredentialTag = tag.String() } return info, nil }
func (lg *LifeGetter) oneLife(tag names.Tag) (params.Life, error) { entity0, err := lg.st.FindEntity(tag) if err != nil { return "", err } entity, ok := entity0.(state.Lifer) if !ok { return "", NotSupportedError(tag, "life cycles") } return params.Life(entity.Life().String()), nil }
func (m *mockClient) EnvironInfo() (params.UndertakerEnvironInfoResult, error) { defer m.mockCall("EnvironInfo") result := params.UndertakerEnvironInfo{ Life: params.Life(m.mockEnviron.Life.String()), UUID: m.mockEnviron.UUID, Name: "dummy", GlobalName: "bob/dummy", IsSystem: m.mockEnviron.IsSystem, TimeOfDeath: m.mockEnviron.TimeOfDeath, } return params.UndertakerEnvironInfoResult{Result: result}, nil }
func (*FacadeSuite) TestLifeNotFound(c *gc.C) { backend := &mockBackend{} facade, err := lifeflag.NewFacade(backend, nil, auth(true)) c.Assert(err, jc.ErrorIsNil) results, err := facade.Life(modelEntity()) c.Check(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Check(result.Life, gc.Equals, params.Life("")) c.Check(result.Error, jc.Satisfies, params.IsCodeNotFound) }
func (*FacadeSuite) TestLifeAuthFailure(c *gc.C) { backend := &mockBackend{} facade, err := lifeflag.NewFacade(backend, nil, auth(true)) c.Assert(err, jc.ErrorIsNil) results, err := facade.Life(entities("unit-foo-1")) c.Check(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Check(result.Life, gc.Equals, params.Life("")) c.Check(result.Error, jc.Satisfies, params.IsCodeUnauthorized) }
func (c *ModelStatusAPI) modelStatus(tag string) (params.ModelStatus, error) { var status params.ModelStatus modelTag, err := names.ParseModelTag(tag) if err != nil { return status, errors.Trace(err) } st := c.backend if modelTag != c.backend.ModelTag() { if st, err = c.backend.ForModel(modelTag); err != nil { return status, errors.Trace(err) } defer st.Close() } model, err := st.Model() if err != nil { return status, errors.Trace(err) } if err := c.modelAuthCheck(modelTag, model.Owner()); err != nil { return status, errors.Trace(err) } machines, err := st.AllMachines() if err != nil { return status, errors.Trace(err) } var hostedMachines []Machine for _, m := range machines { if !m.IsManager() { hostedMachines = append(hostedMachines, m) } } applications, err := st.AllApplications() if err != nil { return status, errors.Trace(err) } modelMachines, err := ModelMachineInfo(st) if err != nil { return status, errors.Trace(err) } return params.ModelStatus{ ModelTag: tag, OwnerTag: model.Owner().String(), Life: params.Life(model.Life().String()), HostedMachineCount: len(hostedMachines), ApplicationCount: len(applications), Machines: modelMachines, }, nil }
func (svc *backingService) updated(st *State, store *multiwatcher.Store, id interface{}) error { if svc.CharmURL == nil { return errors.Errorf("charm url is nil") } env, err := st.Environment() if err != nil { return errors.Trace(err) } info := ¶ms.ServiceInfo{ Name: svc.Name, Exposed: svc.Exposed, CharmURL: svc.CharmURL.String(), OwnerTag: svc.fixOwnerTag(env), Life: params.Life(svc.Life.String()), MinUnits: svc.MinUnits, } oldInfo := store.Get(info.EntityId()) needConfig := false if oldInfo == nil { // We're adding the entry for the first time, // so fetch the associated child documents. c, err := readConstraints(st, serviceGlobalKey(svc.Name)) if err != nil { return err } info.Constraints = c needConfig = true } else { // The entry already exists, so preserve the current status. oldInfo := oldInfo.(*params.ServiceInfo) info.Constraints = oldInfo.Constraints if info.CharmURL == oldInfo.CharmURL { // The charm URL remains the same - we can continue to // use the same config settings. info.Config = oldInfo.Config } else { // The charm URL has changed - we need to fetch the // settings from the new charm's settings doc. needConfig = true } } if needConfig { var err error info.Config, _, err = readSettingsDoc(st, serviceSettingsKey(svc.Name, svc.CharmURL)) if err != nil { return err } } store.Update(info) return nil }
func (c *ControllerAPI) modelStatus(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) } } applications, err := st.AllApplications() if err != nil { return status, errors.Trace(err) } model, err := st.Model() if err != nil { return status, errors.Trace(err) } if err != nil { return status, errors.Trace(err) } modelMachines, err := common.ModelMachineInfo(common.NewModelManagerBackend(st)) if err != nil { return status, errors.Trace(err) } return params.ModelStatus{ ModelTag: tag, OwnerTag: model.Owner().String(), Life: params.Life(model.Life().String()), HostedMachineCount: len(hostedMachines), ApplicationCount: len(applications), Machines: modelMachines, }, nil }
func (s *InstancePollerSuite) TestMachineCallsLife(c *gc.C) { // We have tested separately the Life method, here we just check // it's called internally. var called int expectedResults := params.LifeResults{ Results: []params.LifeResult{{Life: "working"}}, } apiCaller := successAPICaller(c, "Life", entitiesArgs, expectedResults, &called) api := instancepoller.NewAPI(apiCaller) m, err := api.Machine(names.NewMachineTag("42")) c.Assert(err, jc.ErrorIsNil) c.Assert(called, gc.Equals, 1) c.Assert(m.Life(), gc.Equals, params.Life("working")) c.Assert(m.Id(), gc.Equals, "42") }
func (*FacadeSuite) TestLifeBadEntity(c *gc.C) { backend := &mockBackend{} facade, err := lifeflag.NewFacade(backend, nil, auth(true)) c.Assert(err, jc.ErrorIsNil) results, err := facade.Life(entities("archibald snookums")) c.Check(err, jc.ErrorIsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Check(result.Life, gc.Equals, params.Life("")) // TODO(fwereade): this is DUMB. should just be a parse error. // but I'm not fixing the underlying implementation as well. c.Check(result.Error, jc.Satisfies, params.IsCodeUnauthorized) }
func (c *ControllerAPI) environStatus(tag string) (params.EnvironmentStatus, error) { var status params.EnvironmentStatus envTag, err := names.ParseEnvironTag(tag) if err != nil { return status, errors.Trace(err) } st, err := c.state.ForEnviron(envTag) 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.Environment() if err != nil { return status, errors.Trace(err) } if err != nil { return status, errors.Trace(err) } return params.EnvironmentStatus{ EnvironTag: tag, OwnerTag: env.Owner().String(), Life: params.Life(env.Life().String()), HostedMachineCount: len(hostedMachines), ServiceCount: len(services), }, nil }
// ModelInfo returns information on the model needed by the undertaker worker. func (u *UndertakerAPI) ModelInfo() (params.UndertakerModelInfoResult, error) { result := params.UndertakerModelInfoResult{} env, err := u.st.Model() if err != nil { return result, errors.Trace(err) } result.Result = params.UndertakerModelInfo{ UUID: env.UUID(), GlobalName: env.Owner().String() + "/" + env.Name(), Name: env.Name(), IsSystem: u.st.IsController(), Life: params.Life(env.Life().String()), } return result, nil }
func (u *UniterAPI) prepareRelationResult(rel *state.Relation, unit *state.Unit) (params.RelationResult, error) { nothing := params.RelationResult{} ep, err := rel.Endpoint(unit.ServiceName()) if err != nil { // An error here means the unit's service is not part of the // relation. return nothing, err } return params.RelationResult{ Id: rel.Id(), Key: rel.String(), Life: params.Life(rel.Life().String()), Endpoint: params.Endpoint{ ServiceName: ep.ServiceName, Relation: ep.Relation, }, }, nil }
func (u *UniterAPIV3) prepareRelationResult(rel *state.Relation, unit *state.Unit) (params.RelationResult, error) { nothing := params.RelationResult{} ep, err := rel.Endpoint(unit.ApplicationName()) if err != nil { // An error here means the unit's service is not part of the // relation. return nothing, err } return params.RelationResult{ Id: rel.Id(), Key: rel.String(), Life: params.Life(rel.Life().String()), Endpoint: multiwatcher.Endpoint{ ApplicationName: ep.ApplicationName, Relation: multiwatcher.NewCharmRelation(ep.Relation), }, }, nil }
// StorageAttachmentLife returns the lifecycle state of the storage attachments // with the specified tags. func (s *StorageAPI) StorageAttachmentLife(args params.StorageAttachmentIds) (params.LifeResults, error) { canAccess, err := s.accessUnit() if err != nil { return params.LifeResults{}, err } result := params.LifeResults{ Results: make([]params.LifeResult, len(args.Ids)), } for i, id := range args.Ids { stateStorageAttachment, err := s.getOneStateStorageAttachment(canAccess, id) if err == nil { life := stateStorageAttachment.Life() result.Results[i].Life = params.Life(life.String()) } result.Results[i].Error = common.ServerError(err) } return result, nil }
func (m *backingMachine) updated(st *State, store *multiwatcher.Store, id interface{}) error { info := ¶ms.MachineInfo{ Id: m.Id, Life: params.Life(m.Life.String()), Series: m.Series, Jobs: paramsJobsFromJobs(m.Jobs), Addresses: mergedAddresses(m.MachineAddresses, m.Addresses), SupportedContainers: m.SupportedContainers, SupportedContainersKnown: m.SupportedContainersKnown, } oldInfo := store.Get(info.EntityId()) if oldInfo == nil { // We're adding the entry for the first time, // so fetch the associated machine status. sdoc, err := getStatus(st, machineGlobalKey(m.Id)) if err != nil { return err } info.Status = sdoc.Status info.StatusInfo = sdoc.StatusInfo } else { // The entry already exists, so preserve the current status and // instance data. oldInfo := oldInfo.(*params.MachineInfo) info.Status = oldInfo.Status info.StatusInfo = oldInfo.StatusInfo info.InstanceId = oldInfo.InstanceId info.HardwareCharacteristics = oldInfo.HardwareCharacteristics } // If the machine is been provisioned, fetch the instance id as required, // and set instance id and hardware characteristics. if m.Nonce != "" && info.InstanceId == "" { instanceData, err := getInstanceData(st, m.Id) if err == nil { info.InstanceId = string(instanceData.InstanceId) info.HardwareCharacteristics = hardwareCharacteristics(instanceData) } else if !errors.IsNotFound(err) { return err } } store.Update(info) return nil }
// AttachmentLife returns the lifecycle state of each specified machine // storage attachment. func (s *StorageProvisionerAPI) AttachmentLife(args params.MachineStorageIds) (params.LifeResults, error) { canAccess, err := s.getAttachmentAuthFunc() if err != nil { return params.LifeResults{}, err } results := params.LifeResults{ Results: make([]params.LifeResult, len(args.Ids)), } one := func(arg params.MachineStorageId) (params.Life, error) { machineTag, err := names.ParseMachineTag(arg.MachineTag) if err != nil { return "", err } attachmentTag, err := names.ParseTag(arg.AttachmentTag) if err != nil { return "", err } if !canAccess(machineTag, attachmentTag) { return "", common.ErrPerm } var lifer state.Lifer switch attachmentTag := attachmentTag.(type) { case names.VolumeTag: lifer, err = s.st.VolumeAttachment(machineTag, attachmentTag) case names.FilesystemTag: lifer, err = s.st.FilesystemAttachment(machineTag, attachmentTag) } if errors.IsNotFound(err) { return "", common.ErrPerm } else if err != nil { return "", errors.Trace(err) } return params.Life(lifer.Life().String()), nil } for i, arg := range args.Ids { life, err := one(arg) if err != nil { results.Results[i].Error = common.ServerError(err) } else { results.Results[i].Life = life } } return results, nil }
// MachinesWithTransientErrors returns status data for machines with provisioning // errors which are transient. func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) { var results params.StatusResults canAccessFunc, err := p.getAuthFunc() if err != nil { return results, err } // TODO (wallyworld) - add state.State API for more efficient machines query machines, err := p.st.AllMachines() if err != nil { return results, err } for _, machine := range machines { if !canAccessFunc(machine.Tag()) { continue } if _, provisionedErr := machine.InstanceId(); provisionedErr == nil { // Machine may have been provisioned but machiner hasn't set the // status to Started yet. continue } var result params.StatusResult statusInfo, err := machine.Status() if err != nil { continue } result.Status = statusInfo.Status.String() result.Info = statusInfo.Message result.Data = statusInfo.Data if statusInfo.Status != status.Error { continue } // Transient errors are marked as such in the status data. if transient, ok := result.Data["transient"].(bool); !ok || !transient { continue } result.Id = machine.Id() result.Life = params.Life(machine.Life().String()) results.Results = append(results.Results, result) } return results, nil }
func (s *StorageAPI) fromStateStorageAttachment(stateStorageAttachment state.StorageAttachment) (params.StorageAttachment, error) { machineTag, err := s.st.UnitAssignedMachine(stateStorageAttachment.Unit()) if err != nil { return params.StorageAttachment{}, err } info, err := common.StorageAttachmentInfo(s.st, stateStorageAttachment, machineTag) if err != nil { return params.StorageAttachment{}, err } stateStorageInstance, err := s.st.StorageInstance(stateStorageAttachment.StorageInstance()) if err != nil { return params.StorageAttachment{}, err } return params.StorageAttachment{ stateStorageAttachment.StorageInstance().String(), stateStorageInstance.Owner().String(), stateStorageAttachment.Unit().String(), params.StorageKind(stateStorageInstance.Kind()), info.Location, params.Life(stateStorageAttachment.Life().String()), }, nil }
// ModelInfo returns information on the model needed by the undertaker worker. func (u *UndertakerAPI) ModelInfo() (params.UndertakerModelInfoResult, error) { result := params.UndertakerModelInfoResult{} env, err := u.st.Model() if err != nil { return result, errors.Trace(err) } tod := env.TimeOfDeath() result.Result = params.UndertakerModelInfo{ UUID: env.UUID(), GlobalName: env.Owner().String() + "/" + env.Name(), Name: env.Name(), IsSystem: u.st.IsStateServer(), Life: params.Life(env.Life().String()), TimeOfDeath: &tod, } if tod.IsZero() { result.Result.TimeOfDeath = nil } return result, nil }
func (api *AgentAPIV2) getEntity(tag names.Tag) (result params.AgentGetEntitiesResult, err error) { // Allow only for the owner agent. // Note: having a bulk API call for this is utter madness, given that // this check means we can only ever return a single object. if !api.auth.AuthOwner(tag) { err = common.ErrPerm return } entity0, err := api.st.FindEntity(tag) if err != nil { return } entity, ok := entity0.(state.Lifer) if !ok { err = common.NotSupportedError(tag, "life cycles") return } result.Life = params.Life(entity.Life().String()) if machine, ok := entity.(*state.Machine); ok { result.Jobs = stateJobsToAPIParamsJobs(machine.Jobs()) result.ContainerType = machine.ContainerType() } return }
func (m *ModelManagerAPI) getModelInfo(tag names.ModelTag) (params.ModelInfo, error) { st, err := m.state.ForModel(tag) if errors.IsNotFound(err) { return params.ModelInfo{}, common.ErrPerm } else if err != nil { return params.ModelInfo{}, errors.Trace(err) } defer st.Close() model, err := st.Model() if errors.IsNotFound(err) { return params.ModelInfo{}, common.ErrPerm } else if err != nil { return params.ModelInfo{}, errors.Trace(err) } cfg, err := model.Config() if err != nil { return params.ModelInfo{}, errors.Trace(err) } controllerCfg, err := st.ControllerConfig() if err != nil { return params.ModelInfo{}, errors.Trace(err) } users, err := model.Users() if err != nil { return params.ModelInfo{}, errors.Trace(err) } status, err := model.Status() if err != nil { return params.ModelInfo{}, errors.Trace(err) } owner := model.Owner() info := params.ModelInfo{ Name: cfg.Name(), UUID: cfg.UUID(), ControllerUUID: controllerCfg.ControllerUUID(), OwnerTag: owner.String(), Life: params.Life(model.Life().String()), Status: common.EntityStatusFromState(status), ProviderType: cfg.Type(), DefaultSeries: config.PreferredSeries(cfg), CloudTag: names.NewCloudTag(model.Cloud()).String(), CloudRegion: model.CloudRegion(), } if cloudCredentialTag, ok := model.CloudCredential(); ok { info.CloudCredentialTag = cloudCredentialTag.String() } 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, st) 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 } canSeeMachines := authorizedOwner if !canSeeMachines { if canSeeMachines, err = m.hasWriteAccess(tag); err != nil { return params.ModelInfo{}, errors.Trace(err) } } if canSeeMachines { if info.Machines, err = common.ModelMachineInfo(st); err != nil { return params.ModelInfo{}, err } } return info, nil }
// 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 }
func (s *subnetShim) Life() params.Life { return params.Life(s.subnet.Life().String()) }