func networkParamsToStateParams(networks []params.Network, ifaces []params.NetworkInterface) ( []state.NetworkInfo, []state.NetworkInterfaceInfo, error, ) { stateNetworks := make([]state.NetworkInfo, len(networks)) for i, network := range networks { tag, err := names.ParseTag(network.Tag, names.NetworkTagKind) if err != nil { return nil, nil, err } stateNetworks[i] = state.NetworkInfo{ Name: tag.Id(), ProviderId: network.ProviderId, CIDR: network.CIDR, VLANTag: network.VLANTag, } } stateInterfaces := make([]state.NetworkInterfaceInfo, len(ifaces)) for i, iface := range ifaces { tag, err := names.ParseTag(iface.NetworkTag, names.NetworkTagKind) if err != nil { return nil, nil, err } stateInterfaces[i] = state.NetworkInterfaceInfo{ MACAddress: iface.MACAddress, NetworkName: tag.Id(), InterfaceName: iface.InterfaceName, IsVirtual: iface.IsVirtual, } } return stateNetworks, stateInterfaces, 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.ParseTag(apiInfo.Tag, names.EnvironTagKind) if err != nil { return err } environUUID = tag.Id() } info.SetAPIEndpoint(configstore.APIEndpoint{ Addresses: apiInfo.Addrs, CACert: string(apiInfo.CACert), EnvironUUID: environUUID, }) tag, err := names.ParseTag(apiInfo.Tag, names.UserTagKind) if err != nil { return err } info.SetAPICredentials(configstore.APICredentials{ User: tag.Id(), Password: apiInfo.Password, }) return info.Write() }
// UpdateStatus updates the status data of each given entity. // TODO(fwereade): WTF. This method exists *only* for the convenience of the // *client* API -- and is itself completely broken -- but we still expose it // in every facade with a StatusSetter? FFS. func (s *StatusSetter) UpdateStatus(args params.SetStatus) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canModify, err := s.getCanModify() if err != nil { return params.ErrorResults{}, errors.Trace(err) } for i, arg := range args.Entities { tag, err := names.ParseTag(arg.Tag) if err != nil { result.Results[i].Error = ServerError(ErrPerm) continue } err = ErrPerm if canModify(tag) { err = s.updateEntityStatusData(tag, arg.Data) } result.Results[i].Error = ServerError(err) } return result, nil }
// checkCreds validates the entities credentials in the current model. // If the entity is a user, and lookForModelUser is true, a model user must exist // for the model. In the case of a user logging in to the server, but // not a model, there is no env user needed. While we have the env // user, if we do have it, update the last login time. // // Note that when logging in with lookForModelUser true, the returned // entity will be modelUserEntity, not *state.User (external users // don't have user entries) or *state.ModelUser (we // don't want to lose the local user information associated with that). func checkCreds(st *state.State, req params.LoginRequest, lookForModelUser bool, authenticator authentication.EntityAuthenticator) (state.Entity, *time.Time, error) { var tag names.Tag if req.AuthTag != "" { var err error tag, err = names.ParseTag(req.AuthTag) if err != nil { return nil, nil, errors.Trace(err) } } var entityFinder authentication.EntityFinder = st if lookForModelUser { // When looking up model users, use a custom // entity finder that looks up both the local user (if the user // tag is in the local domain) and the model user. entityFinder = modelUserEntityFinder{st} } entity, err := authenticator.Authenticate(entityFinder, tag, req) if err != nil { return nil, nil, errors.Trace(err) } // For user logins, update the last login time. var lastLogin *time.Time if entity, ok := entity.(loginEntity); ok { userLastLogin, err := entity.LastLogin() if err != nil && !state.IsNeverLoggedInError(err) { return nil, nil, errors.Trace(err) } entity.UpdateLastLogin() lastLogin = &userLastLogin } return entity, lastLogin, nil }
// Remove removes volumes and filesystems from state. func (s *StorageProvisionerAPI) Remove(args params.Entities) (params.ErrorResults, error) { canAccess, err := s.getStorageEntityAuthFunc() if err != nil { return params.ErrorResults{}, err } results := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } one := func(arg params.Entity) error { tag, err := names.ParseTag(arg.Tag) if err != nil { return errors.Trace(err) } if !canAccess(tag) { return common.ErrPerm } switch tag := tag.(type) { case names.FilesystemTag: return s.st.RemoveFilesystem(tag) case names.VolumeTag: return s.st.RemoveVolume(tag) default: // should have been picked up by canAccess logger.Debugf("unexpected %v tag", tag.Kind()) return common.ErrPerm } } for i, arg := range args.Entities { err := one(arg) results.Results[i].Error = common.ServerError(err) } return results, nil }
// Name returns the unit's name. func (u *Unit) Name() string { tag, err := names.ParseTag(u.tag, names.UnitTagKind) if err != nil { panic(err) } return tag.Id() }
func (s *machineSuite) TestEntitySetPassword(c *gc.C) { entity, err := apiagent.NewState(s.st).Entity(s.machine.Tag()) c.Assert(err, jc.ErrorIsNil) err = entity.SetPassword("foo") c.Assert(err, gc.ErrorMatches, "password is only 3 bytes long, and is not a valid Agent password") err = entity.SetPassword("foo-12345678901234567890") c.Assert(err, jc.ErrorIsNil) err = entity.ClearReboot() c.Assert(err, jc.ErrorIsNil) err = s.machine.Refresh() c.Assert(err, jc.ErrorIsNil) c.Assert(s.machine.PasswordValid("bar"), jc.IsFalse) c.Assert(s.machine.PasswordValid("foo-12345678901234567890"), jc.IsTrue) // Check that we cannot log in to mongo with the correct password. // This is because there's no mongo password set for s.machine, // which has JobHostUnits info := s.MongoInfo(c) // TODO(dfc) this entity.Tag should return a Tag tag, err := names.ParseTag(entity.Tag()) c.Assert(err, jc.ErrorIsNil) info.Tag = tag info.Password = "******" err = tryOpenState(s.State.ModelTag(), info) c.Assert(errors.Cause(err), jc.Satisfies, errors.IsUnauthorized) }
// Remove removes every given entity from state, calling EnsureDead // first, then Remove. It will fail if the entity is not present. func (r *Remover) Remove(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canModify, err := r.getCanModify() if err != nil { return params.ErrorResults{}, errors.Trace(err) } for i, entity := range args.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { result.Results[i].Error = ServerError(ErrPerm) continue } err = ErrPerm if canModify(tag) { err = r.removeEntity(tag) } result.Results[i].Error = ServerError(err) } return result, nil }
// WatchInterfaces returns a NotifyWatcher for observing changes // to each unit's service configuration settings. func (n *NetworkerAPI) WatchInterfaces(args params.Entities) (params.NotifyWatchResults, error) { result := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } canAccess, err := n.getAuthFunc() if err != nil { return result, err } for i, entity := range args.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } if !canAccess(tag) { err = common.ErrPerm } else { tag, ok := tag.(names.MachineTag) if ok { id := tag.Id() result.Results[i].NotifyWatcherId, err = n.watchOneMachineInterfaces(id) } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// WatchRetryStrategy watches for changes to the environment. Currently we only allow // changes to the boolean that determines whether retries should be attempted or not. func (h *RetryStrategyAPI) WatchRetryStrategy(args params.Entities) (params.NotifyWatchResults, error) { results := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } canAccess, err := h.accessUnit() if err != nil { return params.NotifyWatchResults{}, errors.Trace(err) } for i, entity := range args.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { results.Results[i].Error = common.ServerError(err) continue } err = common.ErrPerm if canAccess(tag) { watch := h.st.WatchForModelConfigChanges() // Consume the initial event. Technically, API calls to Watch // 'transmit' the initial event in the Watch response. But // NotifyWatchers have no state to transmit. if _, ok := <-watch.Changes(); ok { results.Results[i].NotifyWatcherId = h.resources.Register(watch) err = nil } else { err = watcher.EnsureErr(watch) } } results.Results[i].Error = common.ServerError(err) } return results, nil }
// checkCreds validates the entities credentials in the current environment. // If the entity is a user, and lookForEnvUser is true, an env user must exist // for the environment. In the case of a user logging in to the server, but // not an environment, there is no env user needed. While we have the env // user, if we do have it, update the last login time. func checkCreds(st *state.State, req params.LoginRequest, lookForEnvUser bool) (state.Entity, *time.Time, error) { tag, err := names.ParseTag(req.AuthTag) if err != nil { return nil, nil, err } entity, err := st.FindEntity(tag) if errors.IsNotFound(err) { // We return the same error when an entity does not exist as for a bad // password, so that we don't allow unauthenticated users to find // information about existing entities. logger.Debugf("entity %q not found", tag) return nil, nil, common.ErrBadCreds } if err != nil { return nil, nil, errors.Trace(err) } authenticator, err := authentication.FindEntityAuthenticator(entity) if err != nil { return nil, nil, err } if err = authenticator.Authenticate(entity, req.Credentials, req.Nonce); err != nil { logger.Debugf("bad credentials") return nil, nil, err } // For user logins, update the last login time. // NOTE: this code path is only for local users. When we support remote // user logins with bearer tokens, we will need to make sure that we also // update the last connection times for the environment users there. var lastLogin *time.Time if user, ok := entity.(*state.User); ok { userLastLogin, err := user.LastLogin() if err != nil && !state.IsNeverLoggedInError(err) { return nil, nil, errors.Trace(err) } if lookForEnvUser { envUser, err := st.EnvironmentUser(user.UserTag()) if err != nil { return nil, nil, errors.Wrap(err, common.ErrBadCreds) } // The last connection for the environment takes precedence over // the local user last login time. userLastLogin, err = envUser.LastConnection() if err != nil && !state.IsNeverConnectedError(err) { return nil, nil, errors.Trace(err) } envUser.UpdateLastConnection() } // Only update the user's last login time if it is a successful // login, meaning that if we are logging into an environment, make // sure that there is an environment user in that environment for // this user. user.UpdateLastLogin() lastLogin = &userLastLogin } return entity, lastLogin, nil }
// WatchLoggingConfig starts a watcher to track changes to the logging config // for the agents specified.. Unfortunately the current infrastruture makes // watching parts of the config non-trivial, so currently any change to the // config will cause the watcher to notify the client. func (api *LoggerAPI) WatchLoggingConfig(arg params.Entities) params.NotifyWatchResults { result := make([]params.NotifyWatchResult, len(arg.Entities)) for i, entity := range arg.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { result[i].Error = common.ServerError(err) continue } err = common.ErrPerm if api.authorizer.AuthOwner(tag) { watch := api.state.WatchForEnvironConfigChanges() // Consume the initial event. Technically, API calls to Watch // 'transmit' the initial event in the Watch response. But // NotifyWatchers have no state to transmit. if _, ok := <-watch.Changes(); ok { result[i].NotifyWatcherId = api.resources.Register(watch) err = nil } else { err = watcher.MustErr(watch) } } result[i].Error = common.ServerError(err) } return params.NotifyWatchResults{Results: result} }
// LoggingConfig reports the logging configuration for the agents specified. func (api *LoggerAPI) LoggingConfig(arg params.Entities) params.StringResults { if len(arg.Entities) == 0 { return params.StringResults{} } results := make([]params.StringResult, len(arg.Entities)) config, configErr := api.state.EnvironConfig() for i, entity := range arg.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { results[i].Error = common.ServerError(err) continue } err = common.ErrPerm if api.authorizer.AuthOwner(tag) { if configErr == nil { results[i].Result = config.LoggingConfig() err = nil } else { err = configErr } } results[i].Error = common.ServerError(err) } return params.StringResults{Results: results} }
// SetPasswords sets the given password for each supplied entity, if possible. func (pc *PasswordChanger) SetPasswords(args params.EntityPasswords) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } canChange, err := pc.getCanChange() if err != nil { return params.ErrorResults{}, errors.Trace(err) } for i, param := range args.Changes { tag, err := names.ParseTag(param.Tag) if err != nil { result.Results[i].Error = ServerError(ErrPerm) continue } if !canChange(tag) { result.Results[i].Error = ServerError(ErrPerm) continue } if err := pc.setPassword(tag, param.Password); err != nil { result.Results[i].Error = ServerError(err) } } return result, nil }
// loginRequest forms a LoginRequest from the information // in the given HTTP request. func (ctxt *httpContext) loginRequest(r *http.Request) (params.LoginRequest, error) { authHeader := r.Header.Get("Authorization") if authHeader == "" { // No authorization header implies an attempt // to login with macaroon authentication. return params.LoginRequest{ Macaroons: httpbakery.RequestMacaroons(r), }, nil } parts := strings.Fields(authHeader) if len(parts) != 2 || parts[0] != "Basic" { // Invalid header format or no header provided. return params.LoginRequest{}, errors.New("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 params.LoginRequest{}, errors.New("invalid request format") } tagPass := strings.SplitN(string(challenge), ":", 2) if len(tagPass) != 2 { return params.LoginRequest{}, errors.New("invalid request format") } // Ensure that a sensible tag was passed. _, err = names.ParseTag(tagPass[0]) if err != nil { return params.LoginRequest{}, errors.Trace(common.ErrBadCreds) } return params.LoginRequest{ AuthTag: tagPass[0], Credentials: tagPass[1], Nonce: r.Header.Get(params.MachineNonceHeader), }, nil }
// Name returns the service name. func (s *Service) Name() string { tag, err := names.ParseTag(s.tag, names.ServiceTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid service tag", s.tag)) } return tag.Id() }
// Actions takes a list of ActionTags, and returns the full Action for // each ID. func (a *ActionAPI) Actions(arg params.Entities) (params.ActionResults, error) { response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))} for i, entity := range arg.Entities { currentResult := &response.Results[i] tag, err := names.ParseTag(entity.Tag) if err != nil { currentResult.Error = common.ServerError(common.ErrBadId) continue } actionTag, ok := tag.(names.ActionTag) if !ok { currentResult.Error = common.ServerError(common.ErrBadId) continue } action, err := a.state.ActionByTag(actionTag) if err != nil { currentResult.Error = common.ServerError(common.ErrBadId) continue } receiverTag, err := names.ActionReceiverTag(action.Receiver()) if err != nil { currentResult.Error = common.ServerError(err) continue } response.Results[i] = common.MakeActionResult(receiverTag, action) } return response, nil }
// Tools finds the tools necessary for the given agents. func (t *ToolsGetter) Tools(args params.Entities) (params.ToolsResults, error) { result := params.ToolsResults{ Results: make([]params.ToolsResult, len(args.Entities)), } canRead, err := t.getCanRead() if err != nil { return result, err } agentVersion, err := t.getGlobalAgentVersion() if err != nil { return result, err } toolsStorage, err := t.toolsStorageGetter.ToolsStorage() if err != nil { return result, err } defer toolsStorage.Close() for i, entity := range args.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { result.Results[i].Error = ServerError(ErrPerm) continue } agentTools, err := t.oneAgentTools(canRead, tag, agentVersion, toolsStorage) if err == nil { result.Results[i].Tools = agentTools // TODO(axw) Get rid of this in 1.22, when all upgraders // are known to ignore the flag. result.Results[i].DisableSSLHostnameVerification = true } result.Results[i].Error = 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 { tag, err := names.ParseTag(entity.Tag) if err != nil { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = common.ErrPerm if canAccess(tag) { var unitOrService state.Entity unitOrService, err = u.st.FindEntity(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 }
// 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.ParseTag(tagPass[0], names.UserTagKind); err != nil { return common.ErrBadCreds } // Ensure the credentials are correct. _, err = checkCreds(h.state, params.Creds{ AuthTag: tagPass[0], Password: tagPass[1], }) return err }
// InstanceId returns the provider specific instance id for each given // machine or an CodeNotProvisioned error, if not set. func (ig *InstanceIdGetter) InstanceId(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canRead, err := ig.getCanRead() if err != nil { return result, err } for i, entity := range args.Entities { tag, err := names.ParseTag(entity.Tag) if err != nil { result.Results[i].Error = ServerError(ErrPerm) continue } err = ErrPerm if canRead(tag) { var instanceId instance.Id instanceId, err = ig.getInstanceId(tag) if err == nil { result.Results[i].Result = string(instanceId) } } result.Results[i].Error = ServerError(err) } return result, nil }
// limitLoginsDuringRestore will only allow logins for restore related purposes // while the different steps of restore are running. func (a *MachineAgent) limitLoginsDuringRestore(req params.LoginRequest) error { var err error switch { case a.IsRestoreRunning(): err = apiserver.RestoreInProgressError case a.IsRestorePreparing(): err = apiserver.AboutToRestoreError } if err != nil { authTag, parseErr := names.ParseTag(req.AuthTag) if parseErr != nil { return errors.Annotate(err, "could not parse auth tag") } switch authTag := authTag.(type) { case names.UserTag: // use a restricted API mode return err case names.MachineTag: if authTag == a.Tag() { // allow logins from the local machine return nil } } return errors.Errorf("login for %q blocked because restore is in progress", authTag) } return nil }
// Login authenticates as the entity with the given name and password. // Subsequent requests on the state will act as that entity. This // method is usually called automatically by Open. The machine nonce // should be empty unless logging in as a machine agent. func (st *State) Login(tag, password, nonce string) error { var result params.LoginResult err := st.APICall("Admin", 0, "", "Login", ¶ms.Creds{ AuthTag: tag, Password: password, Nonce: nonce, }, &result) if err == nil { authtag, err := names.ParseTag(tag) if err != nil { return err } st.authTag = authtag hostPorts, err := addAddress(result.Servers, st.addr) if err != nil { st.Close() return err } st.hostPorts = hostPorts st.environTag = result.EnvironTag st.facadeVersions = make(map[string][]int, len(result.Facades)) for _, facade := range result.Facades { st.facadeVersions[facade.Name] = facade.Versions } } return err }
func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) { nothing := params.StringsWatchResult{} canAccess, err := p.getAuthFunc() if err != nil { return nothing, err } if !canAccess(arg.MachineTag) { return nothing, common.ErrPerm } tag, err := names.ParseTag(arg.MachineTag, names.MachineTagKind) if err != nil { return nothing, err } machine, err := p.st.Machine(tag.Id()) if err != nil { return nothing, err } var watch state.StringsWatcher if arg.ContainerType != "" { watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType)) } else { watch = machine.WatchAllContainers() } // Consume the initial event and forward it to the result. if changes, ok := <-watch.Changes(); ok { return params.StringsWatchResult{ StringsWatcherId: p.resources.Register(watch), Changes: changes, }, nil } return nothing, watcher.MustErr(watch) }
// WatchAPIVersion starts a watcher to track if there is a new version // of the API that we want to upgrade to func (u *UpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) { result := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } for i, agent := range args.Entities { tag, err := names.ParseTag(agent.Tag) if err != nil { return params.NotifyWatchResults{}, errors.Trace(err) } err = common.ErrPerm if u.authorizer.AuthOwner(tag) { watch := u.st.WatchForEnvironConfigChanges() // Consume the initial event. Technically, API // calls to Watch 'transmit' the initial event // in the Watch response. But NotifyWatchers // have no state to transmit. if _, ok := <-watch.Changes(); ok { result.Results[i].NotifyWatcherId = u.resources.Register(watch) err = nil } else { err = watcher.MustErr(watch) } } result.Results[i].Error = common.ServerError(err) } return result, nil }
// Id returns the machine id. func (m *Machine) Id() string { tag, err := names.ParseTag(m.tag, names.MachineTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid machine tag", m.tag)) } return tag.Id() }
// Tag is part of the state.Block interface. func (b *block) Tag() (names.Tag, error) { tag, err := names.ParseTag(b.doc.Tag) if err != nil { return nil, errors.Annotatef(err, "getting block information") } return tag, nil }
// parseTag, given an entity tag, returns the collection name and id // of the entity document. func (st *State) parseTag(tag string) (coll string, id string, err error) { t, err := names.ParseTag(tag, "") if err != nil { return "", "", err } switch t.(type) { case names.MachineTag: coll = st.machines.Name case names.ServiceTag: coll = st.services.Name case names.UnitTag: coll = st.units.Name case names.UserTag: coll = st.users.Name case names.RelationTag: coll = st.relations.Name case names.EnvironTag: coll = st.environments.Name case names.NetworkTag: coll = st.networks.Name default: return "", "", fmt.Errorf("%q is not a valid collection tag", tag) } return coll, t.Id(), nil }
func (s *initSystemSuite) SetUpTest(c *gc.C) { s.BaseSuite.SetUpTest(c) dataDir, err := paths.DataDir("vivid") c.Assert(err, jc.ErrorIsNil) s.dataDir = dataDir // Patch things out. s.ch = systemd.PatchNewChan(s) s.stub = &testing.Stub{} s.conn = systemd.PatchNewConn(s, s.stub) s.fops = systemd.PatchFileOps(s, s.stub) s.exec = systemd.PatchExec(s, s.stub) // Set up the service. tagStr := "machine-0" tag, err := names.ParseTag(tagStr) c.Assert(err, jc.ErrorIsNil) s.tag = tag s.name = "jujud-" + tagStr s.conf = common.Conf{ Desc: "juju agent for " + tagStr, ExecStart: jujud + " " + tagStr, } s.service = s.newService(c) // Reset any incidental calls. s.stub.ResetCalls() }
func (u *UniterAPIV3) charmModifiedVersion(tagStr string, canAccess func(names.Tag) bool) (int, error) { tag, err := names.ParseTag(tagStr) if err != nil { return -1, common.ErrPerm } if !canAccess(tag) { return -1, common.ErrPerm } unitOrService, err := u.st.FindEntity(tag) if err != nil { return -1, err } var service *state.Service switch entity := unitOrService.(type) { case *state.Service: service = entity case *state.Unit: service, err = entity.Service() if err != nil { return -1, err } default: return -1, errors.BadRequestf("type %t does not have a CharmModifiedVersion", entity) } return service.CharmModifiedVersion(), nil }