// UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms // and records this information in state. func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) { // First get the uuid for the environment to use when querying the charm store. env, err := api.state.Environment() if err != nil { return params.ErrorResult{Error: common.ServerError(err)}, nil } uuid := env.UUID() deployedCharms, err := fetchAllDeployedCharms(api.state) if err != nil { return params.ErrorResult{Error: common.ServerError(err)}, nil } // Look up the revision information for all the deployed charms. curls, err := retrieveLatestCharmInfo(deployedCharms, uuid) if err != nil { return params.ErrorResult{Error: common.ServerError(err)}, nil } // Add the charms and latest revision info to state as charm placeholders. for _, curl := range curls { if err = api.state.AddStoreCharmPlaceholder(curl); err != nil { return params.ErrorResult{Error: common.ServerError(err)}, nil } } return params.ErrorResult{}, nil }
func (api *UserManagerAPI) RemoveUser(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { return result, err } for i, arg := range args.Entities { if !canWrite(arg.Tag) { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } user, err := api.state.User(arg.Tag) if err != nil { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = user.Deactivate() if err != nil { result.Results[i].Error = common.ServerError(fmt.Errorf("Failed to remove user: %s", err)) continue } } return result, nil }
// SetSupportedContainers updates the list of containers supported by the machines passed in args. func (p *ProvisionerAPI) SetSupportedContainers( args params.MachineContainersParams) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Params)), } for i, arg := range args.Params { canAccess, err := p.getAuthFunc() if err != nil { return result, err } machine, err := p.getMachine(canAccess, arg.MachineTag) if err != nil { result.Results[i].Error = common.ServerError(err) continue } if len(arg.ContainerTypes) == 0 { err = machine.SupportsNoContainers() } else { err = machine.SetSupportedContainers(arg.ContainerTypes) } if err != nil { result.Results[i].Error = common.ServerError(err) } } return result, nil }
// WatchAuthorisedKeys starts a watcher to track changes to the authorised ssh keys // for the specified machines. // The current implementation relies on global authorised keys being stored in the environment config. // This will change as new user management and authorisation functionality is added. func (api *KeyUpdaterAPI) WatchAuthorisedKeys(arg params.Entities) (params.NotifyWatchResults, error) { results := make([]params.NotifyWatchResult, len(arg.Entities)) canRead, err := api.getCanRead() if err != nil { return params.NotifyWatchResults{}, err } for i, entity := range arg.Entities { // 1. Check permissions if !canRead(entity.Tag) { results[i].Error = common.ServerError(common.ErrPerm) continue } // 2. Check entity exists if _, err := api.state.FindEntity(entity.Tag); err != nil { if errors.IsNotFound(err) { results[i].Error = common.ServerError(common.ErrPerm) } else { results[i].Error = common.ServerError(err) } continue } // 3. Watch fr changes var err error watch := api.state.WatchForEnvironConfigChanges() // Consume the initial event. if _, ok := <-watch.Changes(); ok { results[i].NotifyWatcherId = api.resources.Register(watch) } else { err = watcher.MustErr(watch) } results[i].Error = common.ServerError(err) } return params.NotifyWatchResults{Results: results}, nil }
func (api *UserManagerAPI) AddUser(args params.EntityPasswords) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { result.Results[0].Error = common.ServerError(err) return result, err } for i, arg := range args.Changes { if !canWrite(arg.Tag) { result.Results[0].Error = common.ServerError(common.ErrPerm) continue } _, err := api.state.AddUser(arg.Tag, arg.Password) if err != nil { err = fmt.Errorf("Failed to create user: %v", err) result.Results[i].Error = common.ServerError(err) continue } } return result, nil }
// DesiredVersion reports the Agent Version that we want that agent to be running func (u *UpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { results := make([]params.VersionResult, len(args.Entities)) if len(args.Entities) == 0 { return params.VersionResults{}, nil } agentVersion, _, err := u.getGlobalAgentVersion() if err != nil { return params.VersionResults{}, common.ServerError(err) } // Is the desired version greater than the current API server version? isNewerVersion := agentVersion.Compare(version.Current.Number) > 0 for i, entity := range args.Entities { err := common.ErrPerm if u.authorizer.AuthOwner(entity.Tag) { if !isNewerVersion || u.entityIsManager(entity.Tag) { results[i].Version = &agentVersion } else { logger.Debugf("desired version is %s, but current version is %s and agent is not a manager node", agentVersion, version.Current.Number) results[i].Version = &version.Current.Number } err = nil } results[i].Error = common.ServerError(err) } return params.VersionResults{Results: results}, nil }
// SetInstanceInfo sets the provider specific machine id, nonce, // metadata and network info for each given machine. Once set, the // instance id cannot be changed. func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Machines)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, arg := range args.Machines { machine, err := p.getMachine(canAccess, arg.Tag) if err == nil { var networks []state.NetworkInfo var interfaces []state.NetworkInterfaceInfo networks, interfaces, err = networkParamsToStateParams(arg.Networks, arg.Interfaces) if err == nil { err = machine.SetInstanceInfo( arg.InstanceId, arg.Nonce, arg.Characteristics, networks, interfaces) } if err != nil { // Give the user more context about the error. err = fmt.Errorf("aborted instance %q: %v", arg.InstanceId, err) } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// RequestedNetworks returns the requested networks for each given // machine entity. Each entry in both lists is returned with its // provider specific id. func (p *ProvisionerAPI) RequestedNetworks(args params.Entities) (params.RequestedNetworksResults, error) { result := params.RequestedNetworksResults{ Results: make([]params.RequestedNetworkResult, len(args.Entities)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, entity := range args.Entities { machine, err := p.getMachine(canAccess, entity.Tag) if err == nil { var includeNetworks []string var excludeNetworks []string includeNetworks, excludeNetworks, err = machine.RequestedNetworks() if err == nil { // TODO(dimitern) For now, since network names and // provider ids are the same, we return what we got // from state. In the future, when networks can be // added before provisioning, we should convert both // slices from juju network names to provider-specific // ids before returning them. result.Results[i].IncludeNetworks = includeNetworks result.Results[i].ExcludeNetworks = excludeNetworks } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// DistributionGroup returns, for each given machine entity, // a slice of instance.Ids that belong to the same distribution // group as that machine. This information may be used to // distribute instances for high availability. func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) { result := params.DistributionGroupResults{ Results: make([]params.DistributionGroupResult, len(args.Entities)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, entity := range args.Entities { machine, err := p.getMachine(canAccess, entity.Tag) if err == nil { // If the machine is an environment manager, return // environment manager instances. Otherwise, return // instances with services in common with the machine // being provisioned. if machine.IsManager() { result.Results[i].Result, err = environManagerInstances(p.st) } else { result.Results[i].Result, err = commonServiceInstances(p.st, machine) } } result.Results[i].Error = common.ServerError(err) } return result, nil }
func (api *RsyslogAPI) SetRsyslogCert(args params.SetRsyslogCertParams) (params.ErrorResult, error) { var result params.ErrorResult if !api.canModify { result.Error = common.ServerError(common.ErrBadCreds) return result, nil } if _, err := cert.ParseCert(string(args.CACert)); err != nil { result.Error = common.ServerError(err) return result, nil } attrs := map[string]interface{}{"rsyslog-ca-cert": string(args.CACert)} if err := api.st.UpdateEnvironConfig(attrs, nil, nil); err != nil { result.Error = common.ServerError(err) } return result, nil }
// CharmURL returns the charm URL for all given units or services. func (u *UniterAPI) CharmURL(args params.Entities) (params.StringBoolResults, error) { result := params.StringBoolResults{ Results: make([]params.StringBoolResult, len(args.Entities)), } accessUnitOrService := common.AuthEither(u.accessUnit, u.accessService) canAccess, err := accessUnitOrService() if err != nil { return params.StringBoolResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unitOrService state.Entity unitOrService, err = u.st.FindEntity(entity.Tag) if err == nil { charmURLer := unitOrService.(interface { CharmURL() (*charm.URL, bool) }) curl, ok := charmURLer.CharmURL() if curl != nil { result.Results[i].Result = curl.String() result.Results[i].Ok = ok } } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// ReadRemoteSettings returns the remote settings of each given set of // relation/local unit/remote unit. func (u *UniterAPI) ReadRemoteSettings(args params.RelationUnitPairs) (params.RelationSettingsResults, error) { result := params.RelationSettingsResults{ Results: make([]params.RelationSettingsResult, len(args.RelationUnitPairs)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationSettingsResults{}, err } for i, arg := range args.RelationUnitPairs { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.LocalUnit) if err == nil { remoteUnit := "" remoteUnit, err = u.checkRemoteUnit(relUnit, arg.RemoteUnit) if err == nil { var settings map[string]interface{} settings, err = relUnit.ReadSettings(remoteUnit) if err == nil { result.Results[i].Settings, err = convertRelationSettings(settings) } } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// GetPrincipal returns the result of calling PrincipalName() and // converting it to a tag, on each given unit. func (u *UniterAPI) GetPrincipal(args params.Entities) (params.StringBoolResults, error) { result := params.StringBoolResults{ Results: make([]params.StringBoolResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.StringBoolResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { principal, ok := unit.PrincipalName() if principal != "" { result.Results[i].Result = names.UnitTag(principal) } result.Results[i].Ok = ok } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// CharmArchiveURL returns the URL, corresponding to the charm archive // (bundle) in the provider storage for each given charm URL, along // with the DisableSSLHostnameVerification flag. func (u *UniterAPI) CharmArchiveURL(args params.CharmURLs) (params.CharmArchiveURLResults, error) { result := params.CharmArchiveURLResults{ Results: make([]params.CharmArchiveURLResult, len(args.URLs)), } // Get the SSL hostname verification environment setting. envConfig, err := u.st.EnvironConfig() if err != nil { return result, err } // SSLHostnameVerification defaults to true, so we need to // invert that, for backwards-compatibility (older versions // will have DisableSSLHostnameVerification: false by default). disableSSLHostnameVerification := !envConfig.SSLHostnameVerification() for i, arg := range args.URLs { curl, err := charm.ParseURL(arg.URL) if err != nil { err = common.ErrPerm } else { var sch *state.Charm sch, err = u.st.Charm(curl) if errors.IsNotFound(err) { err = common.ErrPerm } if err == nil { result.Results[i].Result = sch.BundleURL().String() result.Results[i].DisableSSLHostnameVerification = disableSSLHostnameVerification } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// PublicAddress returns the public address for each given unit, if set. func (u *UniterAPI) PublicAddress(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.StringResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { address, ok := unit.PublicAddress() if ok { result.Results[i].Result = address } else { err = common.NoAddressSetError(entity.Tag, "public") } } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// UpdateSettings persists all changes made to the local settings of // all given pairs of relation and unit. Keys with empty values are // considered a signal to delete these values. func (u *UniterAPI) UpdateSettings(args params.RelationUnitsSettings) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { var settings *state.Settings settings, err = relUnit.Settings() if err == nil { for k, v := range arg.Settings { if v == "" { settings.Delete(k) } else { settings.Set(k, v) } } _, err = settings.Write() } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// ConfigSettings returns the complete set of service charm config // settings available to each given unit. func (u *UniterAPI) ConfigSettings(args params.Entities) (params.ConfigSettingsResults, error) { result := params.ConfigSettingsResults{ Results: make([]params.ConfigSettingsResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ConfigSettingsResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { var settings charm.Settings settings, err = unit.ConfigSettings() if err == nil { result.Results[i].Settings = params.ConfigSettings(settings) } } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// SetCharmURL sets the charm URL for each given unit. An error will // be returned if a unit is dead, or the charm URL is not know. func (u *UniterAPI) SetCharmURL(args params.EntitiesCharmURL) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { var curl *charm.URL curl, err = charm.ParseURL(entity.CharmURL) if err == nil { err = unit.SetCharmURL(curl) } } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// AuthorisedKeys reports the authorised ssh keys for the specified machines. // The current implementation relies on global authorised keys being stored in the environment config. // This will change as new user management and authorisation functionality is added. func (api *KeyUpdaterAPI) AuthorisedKeys(arg params.Entities) (params.StringsResults, error) { if len(arg.Entities) == 0 { return params.StringsResults{}, nil } results := make([]params.StringsResult, len(arg.Entities)) // For now, authorised keys are global, common to all machines. var keys []string config, configErr := api.state.EnvironConfig() if configErr == nil { keys = ssh.SplitAuthorisedKeys(config.AuthorizedKeys()) } canRead, err := api.getCanRead() if err != nil { return params.StringsResults{}, err } for i, entity := range arg.Entities { // 1. Check permissions if !canRead(entity.Tag) { results[i].Error = common.ServerError(common.ErrPerm) continue } // 2. Check entity exists if _, err := api.state.FindEntity(entity.Tag); err != nil { if errors.IsNotFound(err) { results[i].Error = common.ServerError(common.ErrPerm) } else { results[i].Error = common.ServerError(err) } continue } // 3. Get keys var err error if configErr == nil { results[i].Result = keys } else { err = configErr } results[i].Error = common.ServerError(err) } return params.StringsResults{Results: results}, nil }
// WatchContainers starts a StringsWatcher to watch containers deployed to // any machine passed in args. func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) { result := params.StringsWatchResults{ Results: make([]params.StringsWatchResult, len(args.Params)), } for i, arg := range args.Params { watcherResult, err := p.watchOneMachineContainers(arg) result.Results[i] = watcherResult result.Results[i].Error = common.ServerError(err) } return result, nil }
func (api *API) GetEntities(args params.Entities) params.AgentGetEntitiesResults { results := params.AgentGetEntitiesResults{ Entities: make([]params.AgentGetEntitiesResult, len(args.Entities)), } for i, entity := range args.Entities { result, err := api.getEntity(entity.Tag) result.Error = common.ServerError(err) results.Entities[i] = result } return results }
// ListKeys returns the authorised ssh keys for the specified users. func (api *KeyManagerAPI) ListKeys(arg params.ListSSHKeys) (params.StringsResults, error) { if len(arg.Entities.Entities) == 0 { return params.StringsResults{}, nil } results := make([]params.StringsResult, len(arg.Entities.Entities)) // For now, authorised keys are global, common to all users. var keyInfo []string cfg, configErr := api.state.EnvironConfig() if configErr == nil { keys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()) keyInfo = parseKeys(keys, arg.Mode) } canRead, err := api.getCanRead() if err != nil { return params.StringsResults{}, err } for i, entity := range arg.Entities.Entities { if !canRead(entity.Tag) { results[i].Error = common.ServerError(common.ErrPerm) continue } if _, err := api.state.User(entity.Tag); err != nil { if errors.IsNotFound(err) { results[i].Error = common.ServerError(common.ErrPerm) } else { results[i].Error = common.ServerError(err) } continue } var err error if configErr == nil { results[i].Result = keyInfo } else { err = configErr } results[i].Error = common.ServerError(err) } return params.StringsResults{Results: results}, nil }
// Tools finds the tools necessary for the given agents. func (u *UnitUpgraderAPI) Tools(args params.Entities) (params.ToolsResults, error) { result := params.ToolsResults{ Results: make([]params.ToolsResult, len(args.Entities)), } for i, entity := range args.Entities { result.Results[i].Error = common.ServerError(common.ErrPerm) if u.authorizer.AuthOwner(entity.Tag) { result.Results[i] = u.getMachineTools(entity.Tag) } } return result, nil }
func (u *UnitUpgraderAPI) getMachineTools(tag string) params.ToolsResult { var result params.ToolsResult machine, err := u.getAssignedMachine(tag) if err != nil { result.Error = common.ServerError(err) return result } machineTools, err := machine.AgentTools() if err != nil { result.Error = common.ServerError(err) return result } // For older 1.16 upgrader workers, we need to supply a tools URL since the worker will attempt to // download the tools even though they already have been fetched by the machine agent. Newer upgrader // workers do not have this problem. So to be compatible across all versions, we return the full // tools metadata. // TODO (wallyworld) - remove in 1.20, just return machineTools cfg, err := u.st.EnvironConfig() if err != nil { result.Error = common.ServerError(err) return result } // SSLHostnameVerification defaults to true, so we need to // invert that, for backwards-compatibility (older versions // will have DisableSSLHostnameVerification: false by default). result.DisableSSLHostnameVerification = !cfg.SSLHostnameVerification() env, err := environs.New(cfg) if err != nil { result.Error = common.ServerError(err) return result } agentTools, err := envtools.FindExactTools( env, machineTools.Version.Number, machineTools.Version.Series, machineTools.Version.Arch) if err != nil { result.Error = common.ServerError(err) return result } result.Tools = agentTools return result }
// AddMachinesV2 adds new machines with the supplied parameters. func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) { results := params.AddMachinesResults{ Machines: make([]params.AddMachinesResult, len(args.MachineParams)), } for i, p := range args.MachineParams { m, err := c.addOneMachine(p) results.Machines[i].Error = common.ServerError(err) if err == nil { results.Machines[i].Machine = m.Id() } } return results, nil }
// RelationById returns information about all given relations, // specified by their ids, including their key and the local // endpoint. func (u *UniterAPI) RelationById(args params.RelationIds) (params.RelationResults, error) { result := params.RelationResults{ Results: make([]params.RelationResult, len(args.RelationIds)), } for i, relId := range args.RelationIds { relParams, err := u.getOneRelationById(relId) if err == nil { result.Results[i] = relParams } result.Results[i].Error = common.ServerError(err) } return result, nil }
// DesiredVersion reports the Agent Version that we want that unit to be running. // The desired version is what the unit's assigned machine is running. func (u *UnitUpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { result := make([]params.VersionResult, len(args.Entities)) if len(args.Entities) == 0 { return params.VersionResults{}, nil } for i, entity := range args.Entities { err := common.ErrPerm if u.authorizer.AuthOwner(entity.Tag) { result[i].Version, err = u.getMachineToolsVersion(entity.Tag) } result[i].Error = common.ServerError(err) } return params.VersionResults{Results: result}, nil }
func (s *errorsSuite) TestErrorTransform(c *gc.C) { for _, t := range errorTransformTests { err1 := common.ServerError(t.err) if t.err == nil { c.Assert(err1, gc.IsNil) } else { c.Assert(err1.Message, gc.Equals, t.err.Error()) c.Assert(err1.Code, gc.Equals, t.code) if t.helperFunc != nil { c.Assert(err1, jc.Satisfies, t.helperFunc) } } } }
// WatchRelationUnits returns a RelationUnitsWatcher for observing // changes to every unit in the supplied relation that is visible to // the supplied unit. See also state/watcher.go:RelationUnit.Watch(). func (u *UniterAPI) WatchRelationUnits(args params.RelationUnits) (params.RelationUnitsWatchResults, error) { result := params.RelationUnitsWatchResults{ Results: make([]params.RelationUnitsWatchResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationUnitsWatchResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { result.Results[i], err = u.watchOneRelationUnit(relUnit) } result.Results[i].Error = common.ServerError(err) } return result, nil }
// Relation returns information about all given relation/unit pairs, // including their id, key and the local endpoint. func (u *UniterAPI) Relation(args params.RelationUnits) (params.RelationResults, error) { result := params.RelationResults{ Results: make([]params.RelationResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationResults{}, err } for i, rel := range args.RelationUnits { relParams, err := u.getOneRelation(canAccess, rel.Relation, rel.Unit) if err == nil { result.Results[i] = relParams } result.Results[i].Error = common.ServerError(err) } return result, nil }