func newWorker(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { cfg := a.CurrentConfig() // Grab the tag and ensure that it's for a machine. tag, ok := cfg.Tag().(names.MachineTag) if !ok { return nil, errors.New("this manifold may only be used inside a machine agent") } // Get the machine agent's jobs. // TODO(fwereade): this functionality should be on the // deployer facade instead. agentFacade := apiagent.NewState(apiCaller) entity, err := agentFacade.Entity(tag) if err != nil { return nil, err } var isModelManager bool for _, job := range entity.Jobs() { if job == multiwatcher.JobManageModel { isModelManager = true break } } if !isModelManager { return nil, dependency.ErrMissing } return NewResumer(apiresumer.NewAPI(apiCaller)), nil }
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) }
// newWorker trivially wraps NewWorker for use in a engine.AgentAPIManifold. func newWorker(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { cfg := a.CurrentConfig() // Grab the tag and ensure that it's for a machine. tag, ok := cfg.Tag().(names.MachineTag) if !ok { return nil, errors.New("this manifold may only be used inside a machine agent") } // Get the machine agent's jobs. entity, err := apiagent.NewState(apiCaller).Entity(tag) if err != nil { return nil, err } var isModelManager bool for _, job := range entity.Jobs() { if job == multiwatcher.JobManageModel { isModelManager = true break } } if !isModelManager { return nil, dependency.ErrMissing } return NewWorker(cfg) }
func (s *servingInfoSuite) TestIsMasterPermission(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c) _, err := apiagent.NewState(st).IsMaster() c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{ Message: "permission denied", Code: "unauthorized access", }) }
// manifoldStart creates a *Tracker given a base.APICaller. func manifoldStart(apiCaller base.APICaller) (worker.Worker, error) { w, err := NewTracker(Config{ Observer: agent.NewState(apiCaller), }) if err != nil { return nil, errors.Trace(err) } return w, nil }
func (s *modelSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) conn, _ := s.OpenAPIAsNewMachine(c) agentAPI := apiagent.NewState(conn) c.Assert(agentAPI, gc.NotNil) s.ModelWatcherTests = apitesting.NewModelWatcherTests( agentAPI, s.BackingState, apitesting.NoSecrets) }
// ServingInfoSetterManifold defines a simple start function which // runs after the API connection has come up. If the machine agent is // a controller, it grabs the state serving info over the API and // records it to agent configuration, and then stops. func ServingInfoSetterManifold(config ServingInfoSetterConfig) dependency.Manifold { return dependency.Manifold{ Inputs: []string{ config.AgentName, config.APICallerName, }, Start: func(context dependency.Context) (worker.Worker, error) { // Get the agent. var agent coreagent.Agent if err := context.Get(config.AgentName, &agent); err != nil { return nil, err } // Grab the tag and ensure that it's for a machine. tag, ok := agent.CurrentConfig().Tag().(names.MachineTag) if !ok { return nil, errors.New("agent's tag is not a machine tag") } // Get API connection. var apiCaller base.APICaller if err := context.Get(config.APICallerName, &apiCaller); err != nil { return nil, err } apiState := apiagent.NewState(apiCaller) // If the machine needs State, grab the state serving info // over the API and write it to the agent configuration. machine, err := apiState.Entity(tag) if err != nil { return nil, err } for _, job := range machine.Jobs() { if job.NeedsState() { info, err := apiState.StateServingInfo() if err != nil { return nil, errors.Errorf("cannot get state serving info: %v", err) } err = agent.ChangeConfig(func(config coreagent.ConfigSetter) error { config.SetStateServingInfo(info) return nil }) if err != nil { return nil, err } } } // All is well - we're done (no actual worker is actually returned). return nil, dependency.ErrUninstall }, } }
// isModelManager returns whether the agent has JobManageModel, // or an error. func isModelManager(a agent.Agent, apiCaller base.APICaller) (bool, error) { agentFacade := apiagent.NewState(apiCaller) entity, err := agentFacade.Entity(a.CurrentConfig().Tag()) if err != nil { return false, errors.Trace(err) } for _, job := range entity.Jobs() { if job == multiwatcher.JobManageModel { return true, nil } } return false, nil }
func (s *machineSuite) TestMachineEntity(c *gc.C) { tag := names.NewMachineTag("42") m, err := apiagent.NewState(s.st).Entity(tag) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(m, gc.IsNil) m, err = apiagent.NewState(s.st).Entity(s.machine.Tag()) c.Assert(err, jc.ErrorIsNil) c.Assert(m.Tag(), gc.Equals, s.machine.Tag().String()) c.Assert(m.Life(), gc.Equals, params.Alive) c.Assert(m.Jobs(), gc.DeepEquals, []multiwatcher.MachineJob{multiwatcher.JobHostUnits}) err = s.machine.EnsureDead() c.Assert(err, jc.ErrorIsNil) err = s.machine.Remove() c.Assert(err, jc.ErrorIsNil) m, err = apiagent.NewState(s.st).Entity(s.machine.Tag()) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("machine %s not found", s.machine.Id())) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(m, gc.IsNil) }
func (s *unitSuite) TestUnitEntity(c *gc.C) { tag := names.NewUnitTag("wordpress/1") m, err := apiagent.NewState(s.st).Entity(tag) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(m, gc.IsNil) m, err = apiagent.NewState(s.st).Entity(s.unit.Tag()) c.Assert(err, jc.ErrorIsNil) c.Assert(m.Tag(), gc.Equals, s.unit.Tag().String()) c.Assert(m.Life(), gc.Equals, params.Alive) c.Assert(m.Jobs(), gc.HasLen, 0) err = s.unit.EnsureDead() c.Assert(err, jc.ErrorIsNil) err = s.unit.Remove() c.Assert(err, jc.ErrorIsNil) m, err = apiagent.NewState(s.st).Entity(s.unit.Tag()) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("unit %q not found", s.unit.Name())) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(m, gc.IsNil) }
func newWorker(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { st := apiagent.NewState(apiCaller) isMM, err := isModelManager(a, st) if err != nil { return nil, errors.Trace(err) } if !isMM { return nil, dependency.ErrMissing } // 4 times a day seems a decent enough amount of checks. checkerParams := VersionCheckerParams{ CheckInterval: time.Hour * 6, } return New(agenttools.NewFacade(apiCaller), &checkerParams), nil }
func (s *servingInfoSuite) TestIsMaster(c *gc.C) { calledIsMaster := false var fakeMongoIsMaster = func(session *mgo.Session, m mongo.WithAddresses) (bool, error) { calledIsMaster = true return true, nil } s.PatchValue(&apiserveragent.MongoIsMaster, fakeMongoIsMaster) st, _ := s.OpenAPIAsNewMachine(c, state.JobManageModel) expected := true result, err := apiagent.NewState(st).IsMaster() c.Assert(err, jc.ErrorIsNil) c.Assert(result, gc.Equals, expected) c.Assert(calledIsMaster, jc.IsTrue) }
// Manifold returns a dependency manifold that runs a deployer worker, // using the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { // newWorker trivially wraps NewDeployer for use in a util.PostUpgradeManifold. // // It's not tested at the moment, because the scaffolding // necessary is too unwieldy/distracting to introduce at this point. newWorker := func(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { cfg := a.CurrentConfig() // Grab the tag and ensure that it's for a machine. tag, ok := cfg.Tag().(names.MachineTag) if !ok { return nil, errors.New("agent's tag is not a machine tag") } // Get the machine agent's jobs. // TODO(fwereade): this functionality should be on the // deployer facade instead. agentFacade := apiagent.NewState(apiCaller) entity, err := agentFacade.Entity(tag) if err != nil { return nil, err } var isUnitHoster bool for _, job := range entity.Jobs() { if job == multiwatcher.JobHostUnits { isUnitHoster = true break } } if !isUnitHoster { return nil, dependency.ErrUninstall } deployerFacade := apideployer.NewState(apiCaller) context := config.NewDeployContext(deployerFacade, cfg) w, err := NewDeployer(deployerFacade, context) if err != nil { return nil, errors.Annotate(err, "cannot start unit agent deployer worker") } return w, nil } return util.PostUpgradeManifold(config.PostUpgradeManifoldConfig, newWorker) }
func (s *machineSuite) TestClearReboot(c *gc.C) { err := s.machine.SetRebootFlag(true) c.Assert(err, jc.ErrorIsNil) rFlag, err := s.machine.GetRebootFlag() c.Assert(err, jc.ErrorIsNil) c.Assert(rFlag, jc.IsTrue) entity, err := apiagent.NewState(s.st).Entity(s.machine.Tag()) c.Assert(err, jc.ErrorIsNil) err = entity.ClearReboot() c.Assert(err, jc.ErrorIsNil) rFlag, err = s.machine.GetRebootFlag() c.Assert(err, jc.ErrorIsNil) c.Assert(rFlag, jc.IsFalse) }
// Manifold returns a dependency manifold that runs a machiner worker, using // the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { // TODO(waigani) This function is currently covered by functional tests // under the machine agent. Add unit tests once infrastructure to do so is // in place. // newWorker non-trivially wraps NewMachiner to specialise a PostUpgradeManifold. var newWorker = func(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { currentConfig := a.CurrentConfig() // TODO(fwereade): this functionality should be on the // deployer facade instead. agentFacade := apiagent.NewState(apiCaller) envConfig, err := agentFacade.ModelConfig() if err != nil { return nil, errors.Errorf("cannot read environment config: %v", err) } ignoreMachineAddresses, _ := envConfig.IgnoreMachineAddresses() // Containers only have machine addresses, so we can't ignore them. tag := currentConfig.Tag() if names.IsContainerMachine(tag.Id()) { ignoreMachineAddresses = false } if ignoreMachineAddresses { logger.Infof("machine addresses not used, only addresses from provider") } accessor := APIMachineAccessor{apimachiner.NewState(apiCaller)} w, err := NewMachiner(Config{ MachineAccessor: accessor, Tag: tag.(names.MachineTag), ClearMachineAddressesOnStart: ignoreMachineAddresses, NotifyMachineDead: func() error { return agent.SetCanUninstall(a) }, }) if err != nil { return nil, errors.Annotate(err, "cannot start machiner worker") } return w, err } return util.PostUpgradeManifold(config.PostUpgradeManifoldConfig, newWorker) }
// Manifold returns a dependency manifold that runs a log forwarding // worker, using the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { openLogStream := config.OpenLogStream if openLogStream == nil { openLogStream = func(caller base.APICaller, cfg params.LogStreamConfig, controllerUUID string) (LogStream, error) { return logstream.Open(caller, cfg, controllerUUID) } } openForwarder := config.OpenLogForwarder if openForwarder == nil { openForwarder = NewLogForwarder } return dependency.Manifold{ Inputs: []string{ config.StateName, // ...just to force it to run only on the controller. config.APICallerName, }, Start: func(context dependency.Context) (worker.Worker, error) { var apiCaller base.APICaller if err := context.Get(config.APICallerName, &apiCaller); err != nil { return nil, errors.Trace(err) } agentFacade := apiagent.NewState(apiCaller) controllerCfg, err := agentFacade.ControllerConfig() if err != nil { return nil, errors.Annotate(err, "cannot read controller config") } orchestrator, err := newOrchestratorForController(OrchestratorArgs{ ControllerUUID: controllerCfg.ControllerUUID(), LogForwardConfig: agentFacade, Caller: apiCaller, Sinks: config.Sinks, OpenLogStream: openLogStream, OpenLogForwarder: openForwarder, }) return orchestrator, errors.Annotate(err, "creating log forwarding orchestrator") }, } }
func (s *servingInfoSuite) TestStateServingInfo(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c, state.JobManageModel) ssi := state.StateServingInfo{ PrivateKey: "some key", Cert: "Some cert", SharedSecret: "really, really secret", APIPort: 33, StatePort: 44, } expected := params.StateServingInfo{ PrivateKey: ssi.PrivateKey, Cert: ssi.Cert, SharedSecret: ssi.SharedSecret, APIPort: ssi.APIPort, StatePort: ssi.StatePort, } s.State.SetStateServingInfo(ssi) info, err := apiagent.NewState(st).StateServingInfo() c.Assert(err, jc.ErrorIsNil) c.Assert(info, jc.DeepEquals, expected) }
// newWorker non-trivially wraps NewMachiner to specialise a util.AgentApiManifold. // // TODO(waigani) This function is currently covered by functional tests // under the machine agent. Add unit tests once infrastructure to do so is // in place. func newWorker(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { currentConfig := a.CurrentConfig() // TODO(fwereade): this functionality should be on the // machiner facade instead -- or, better yet, separate // the networking concerns from the lifecycle ones and // have completey separate workers. // // (With their own facades.) agentFacade := apiagent.NewState(apiCaller) envConfig, err := agentFacade.ModelConfig() if err != nil { return nil, errors.Errorf("cannot read environment config: %v", err) } ignoreMachineAddresses, _ := envConfig.IgnoreMachineAddresses() // Containers only have machine addresses, so we can't ignore them. tag := currentConfig.Tag() if names.IsContainerMachine(tag.Id()) { ignoreMachineAddresses = false } if ignoreMachineAddresses { logger.Infof("machine addresses not used, only addresses from provider") } accessor := APIMachineAccessor{apimachiner.NewState(apiCaller)} w, err := NewMachiner(Config{ MachineAccessor: accessor, Tag: tag.(names.MachineTag), ClearMachineAddressesOnStart: ignoreMachineAddresses, NotifyMachineDead: func() error { return agent.SetCanUninstall(a) }, }) if err != nil { return nil, errors.Annotate(err, "cannot start machiner worker") } return w, err }
// Manifold returns a Manifold that encapsulates a *Tracker and exposes it as // an environs.Environ resource. func Manifold(config ManifoldConfig) dependency.Manifold { manifold := dependency.Manifold{ Inputs: []string{ config.APICallerName, }, Output: manifoldOutput, Start: func(getResource dependency.GetResourceFunc) (worker.Worker, error) { var apiCaller base.APICaller if err := getResource(config.APICallerName, &apiCaller); err != nil { return nil, errors.Trace(err) } w, err := NewTracker(Config{ Observer: agent.NewState(apiCaller), NewEnvironFunc: config.NewEnvironFunc, }) if err != nil { return nil, errors.Trace(err) } return w, nil }, } return manifold }
// newWorker trivially wraps NewDeployer for use in a engine.AgentApiManifold. // // It's not tested at the moment, because the scaffolding // necessary is too unwieldy/distracting to introduce at this point. func (config ManifoldConfig) newWorker(a agent.Agent, apiCaller base.APICaller) (worker.Worker, error) { cfg := a.CurrentConfig() // Grab the tag and ensure that it's for a machine. tag, ok := cfg.Tag().(names.MachineTag) if !ok { return nil, errors.New("agent's tag is not a machine tag") } // Get the machine agent's jobs. // TODO(fwereade): this functionality should be on the // deployer facade instead. agentFacade := apiagent.NewState(apiCaller) entity, err := agentFacade.Entity(tag) if err != nil { return nil, err } var isUnitHoster bool for _, job := range entity.Jobs() { if job == multiwatcher.JobHostUnits { isUnitHoster = true break } } if !isUnitHoster { return nil, dependency.ErrUninstall } deployerFacade := apideployer.NewState(apiCaller) context := config.NewDeployContext(deployerFacade, cfg) w, err := NewDeployer(deployerFacade, context) if err != nil { return nil, errors.Annotate(err, "cannot start unit agent deployer worker") } return w, nil }
// Agent returns a version of the state that provides // functionality required by the agent code. func (st *State) Agent() *agent.State { return agent.NewState(st) }
// ServingInfoSetterManifold defines a simple start function which // runs after the API connection has come up. If the machine agent is // a controller, it grabs the state serving info over the API and // records it to agent configuration, and then stops. func ServingInfoSetterManifold(config ServingInfoSetterConfig) dependency.Manifold { return dependency.Manifold{ Inputs: []string{ config.AgentName, config.APICallerName, }, Start: func(context dependency.Context) (worker.Worker, error) { // Get the agent. var agent coreagent.Agent if err := context.Get(config.AgentName, &agent); err != nil { return nil, err } // Grab the tag and ensure that it's for a machine. tag, ok := agent.CurrentConfig().Tag().(names.MachineTag) if !ok { return nil, errors.New("agent's tag is not a machine tag") } // Get API connection. var apiCaller base.APICaller if err := context.Get(config.APICallerName, &apiCaller); err != nil { return nil, err } apiState := apiagent.NewState(apiCaller) // If the machine needs State, grab the state serving info // over the API and write it to the agent configuration. if controller, err := isController(apiState, tag); err != nil { return nil, errors.Annotate(err, "checking controller status") } else if !controller { // Not a controller, nothing to do. return nil, dependency.ErrUninstall } info, err := apiState.StateServingInfo() if err != nil { return nil, errors.Annotate(err, "getting state serving info") } err = agent.ChangeConfig(func(config coreagent.ConfigSetter) error { existing, hasInfo := config.StateServingInfo() if hasInfo { // Use the existing cert and key as they appear to // have been already updated by the cert updater // worker to have this machine's IP address as // part of the cert. This changed cert is never // put back into the database, so it isn't // reflected in the copy we have got from // apiState. info.Cert = existing.Cert info.PrivateKey = existing.PrivateKey } config.SetStateServingInfo(info) return nil }) if err != nil { return nil, errors.Trace(err) } // All is well - we're done (no actual worker is actually returned). return nil, dependency.ErrUninstall }, } }
func (f *fakeAPIConn) Agent() *apiagent.State { return apiagent.NewState(f) }
// Manifold returns a dependency manifold that runs an upgrader // worker, using the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { return dependency.Manifold{ Inputs: []string{ config.AgentName, config.APICallerName, config.UpgradeStepsGateName, }, Start: func(getResource dependency.GetResourceFunc) (worker.Worker, error) { // Sanity checks if config.OpenStateForUpgrade == nil { return nil, errors.New("missing OpenStateForUpgrade in config") } if config.PreUpgradeSteps == nil { return nil, errors.New("missing PreUpgradeSteps in config") } // Get machine agent. var agent agent.Agent if err := getResource(config.AgentName, &agent); err != nil { return nil, err } // Grab the tag and ensure that it's for a machine. tag, ok := agent.CurrentConfig().Tag().(names.MachineTag) if !ok { return nil, errors.New("agent's tag is not a machine tag") } // Get API connection. // TODO(fwereade): can we make the worker use an // APICaller instead? should be able to depend on // the Engine to abort us when conn is closed... var apiConn api.Connection if err := getResource(config.APICallerName, &apiConn); err != nil { return nil, err } // Get the machine agent's jobs. // TODO(fwereade): use appropriate facade! agentFacade := apiagent.NewState(apiConn) entity, err := agentFacade.Entity(tag) if err != nil { return nil, err } jobs := entity.Jobs() // Get machine instance for setting status on. // TODO(fwereade): use appropriate facade! machinerFacade := apimachiner.NewState(apiConn) machine, err := machinerFacade.Machine(tag) if err != nil { return nil, err } // Get upgradesteps completed lock. var upgradeStepsLock gate.Lock if err := getResource(config.UpgradeStepsGateName, &upgradeStepsLock); err != nil { return nil, err } return NewWorker( upgradeStepsLock, agent, apiConn, jobs, config.OpenStateForUpgrade, config.PreUpgradeSteps, machine, ) }, } }
// startAPIWorkers is called to start workers which rely on the // machine agent's API connection (via the apiworkers manifold). It // returns a Runner with a number of workers attached to it. // // The workers started here need to be converted to run under the // dependency engine. Once they have all been converted, this method - // and the apiworkers manifold - can be removed. func (a *MachineAgent) startAPIWorkers(apiConn api.Connection) (_ worker.Worker, outErr error) { agentConfig := a.CurrentConfig() entity, err := apiagent.NewState(apiConn).Entity(a.Tag()) if err != nil { return nil, errors.Trace(err) } var isModelManager bool for _, job := range entity.Jobs() { switch job { case multiwatcher.JobManageModel: isModelManager = true default: // TODO(dimitern): Once all workers moved over to using // the API, report "unknown job type" here. } } runner := newConnRunner(apiConn) defer func() { // If startAPIWorkers exits early with an error, stop the // runner so that any already started runners aren't leaked. if outErr != nil { worker.Stop(runner) } }() modelConfig, err := apiagent.NewState(apiConn).ModelConfig() if err != nil { return nil, fmt.Errorf("cannot read model config: %v", err) } // Perform the operations needed to set up hosting for containers. if err := a.setupContainerSupport(runner, apiConn, agentConfig); err != nil { cause := errors.Cause(err) if params.IsCodeDead(cause) || cause == worker.ErrTerminateAgent { return nil, worker.ErrTerminateAgent } return nil, fmt.Errorf("setting up container support: %v", err) } if isModelManager { // Published image metadata for some providers are in simple streams. // Providers that do not depend on simple streams do not need this worker. env, err := newEnvirons(modelConfig) if err != nil { return nil, errors.Annotate(err, "getting environ") } if _, ok := env.(simplestreams.HasRegion); ok { // Start worker that stores published image metadata in state. runner.StartWorker("imagemetadata", func() (worker.Worker, error) { return newMetadataUpdater(apiConn.MetadataUpdater()), nil }) } // We don't have instance info set and the network config for the // bootstrap machine only, so update it now. All the other machines will // have instance info including network config set at provisioning time. if err := a.setControllerNetworkConfig(apiConn); err != nil { return nil, errors.Annotate(err, "setting controller network config") } } else { runner.StartWorker("stateconverter", func() (worker.Worker, error) { // TODO(fwereade): this worker needs its own facade. facade := apimachiner.NewState(apiConn) handler := conv2state.New(facade, a) w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{ Handler: handler, }) if err != nil { return nil, errors.Annotate(err, "cannot start controller promoter worker") } return w, nil }) } return runner, nil }
// ServingInfoSetterManifold defines a simple start function which // runs after the API connection has come up. If the machine agent is // a controller, it grabs the state serving info over the API and // records it to agent configuration, and then stops. func ServingInfoSetterManifold(config ServingInfoSetterConfig) dependency.Manifold { return dependency.Manifold{ Inputs: []string{ config.AgentName, config.APICallerName, }, Start: func(context dependency.Context) (worker.Worker, error) { // Get the agent. var agent coreagent.Agent if err := context.Get(config.AgentName, &agent); err != nil { return nil, err } // Grab the tag and ensure that it's for a machine. tag, ok := agent.CurrentConfig().Tag().(names.MachineTag) if !ok { return nil, errors.New("agent's tag is not a machine tag") } // Get API connection. var apiCaller base.APICaller if err := context.Get(config.APICallerName, &apiCaller); err != nil { return nil, err } apiState := apiagent.NewState(apiCaller) // If the machine needs State, grab the state serving info // over the API and write it to the agent configuration. machine, err := apiState.Entity(tag) if err != nil { return nil, err } existing, hasInfo := agent.CurrentConfig().StateServingInfo() for _, job := range machine.Jobs() { if job.NeedsState() { info, err := apiState.StateServingInfo() if err != nil { return nil, errors.Errorf("cannot get state serving info: %v", err) } if hasInfo { // Use the existing Cert as it may have been updated already // by the cert updater worker to have this machine's IP address // as part of the cert. This changed cert is never put back into // the database, so it isn't reflected in the copy we have got // from apiState. info.Cert = existing.Cert } err = agent.ChangeConfig(func(config coreagent.ConfigSetter) error { config.SetStateServingInfo(info) return nil }) if err != nil { return nil, err } } } // All is well - we're done (no actual worker is actually returned). return nil, dependency.ErrUninstall }, } }