// startFunc returns a StartFunc that creates a worker based on the manifolds // named in the supplied config. func startFunc(config ManifoldConfig) dependency.StartFunc { return func(getResource dependency.GetResourceFunc) (worker.Worker, error) { // Get dependencies and open a connection. var a agent.Agent if err := getResource(config.AgentName, &a); err != nil { return nil, err } conn, err := openConnection(a) if err != nil { return nil, errors.Annotate(err, "cannot open api") } // Add the environment uuid to agent config if not present. currentConfig := a.CurrentConfig() if currentConfig.Environment().Id() == "" { err := a.ChangeConfig(func(setter agent.ConfigSetter) error { environTag, err := conn.EnvironTag() if err != nil { return errors.Annotate(err, "no environment uuid set on api") } return setter.Migrate(agent.MigrateParams{ Environment: environTag, }) }) if err != nil { logger.Warningf("unable to save environment uuid: %v", err) // Not really fatal, just annoying. } } // Return the worker. return newApiConnWorker(conn) } }
// NewLock creates a gate.Lock to be used to synchronise workers which // need to start after upgrades have completed. If no upgrade steps // are required the Lock is unlocked and the version in agent's // configuration is updated to the currently running version. // // The returned Lock should be passed to NewWorker. func NewLock(a agent.Agent) (gate.Lock, error) { lock := gate.NewLock() if wrench.IsActive("machine-agent", "always-try-upgrade") { // Always enter upgrade mode. This allows test of upgrades // even when there's actually no upgrade steps to run. return lock, nil } err := a.ChangeConfig(func(agentConfig agent.ConfigSetter) error { if !upgrades.AreUpgradesDefined(agentConfig.UpgradedToVersion()) { logger.Infof("no upgrade steps required or upgrade steps for %v "+ "have already been run.", jujuversion.Current) lock.Unlock() // Even if no upgrade is required the version number in // the agent's config still needs to be bumped. agentConfig.SetUpgradedToVersion(jujuversion.Current) } return nil }) if err != nil { return nil, err } return lock, 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 } 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 }, } }
func setAgentPassword(newPw, oldPw string, a agent.Agent, entity *apiagent.Entity) error { // Change the configuration *before* setting the entity // password, so that we avoid the possibility that // we might successfully change the entity's // password but fail to write the configuration, // thus locking us out completely. if err := a.ChangeConfig(func(c agent.ConfigSetter) error { c.SetPassword(newPw) c.SetOldPassword(oldPw) return nil }); err != nil { return err } return entity.SetPassword(newPw) }
// changePassword generates a new random password and records it in // local agent configuration and on the remote state server. The supplied // oldPassword -- which must be the current valid password -- is set as a // fallback in local config, in case we fail to update the remote password. func changePassword(oldPassword string, a agent.Agent, facade apiagent.ConnFacade) error { newPassword, err := utils.RandomPassword() if err != nil { return errors.Trace(err) } if err := a.ChangeConfig(func(c agent.ConfigSetter) error { c.SetPassword(newPassword) c.SetOldPassword(oldPassword) return nil }); err != nil { return err } // This has to happen *after* we record the old/new passwords // locally, lest we change it remotely, crash suddenly, and // end up locked out forever. return facade.SetPassword(a.CurrentConfig().Tag(), newPassword) }
// maybeSetAgentModelTag tries to update the agent configuration if // it's missing a model tag. It doesn't *really* matter if it fails, // because we can demonstrably connect without it, so we log any // errors encountered and never return any to the client. func maybeSetAgentModelTag(a agent.Agent, conn api.Connection) { if a.CurrentConfig().Model().Id() == "" { err := a.ChangeConfig(func(setter agent.ConfigSetter) error { modelTag, err := conn.ModelTag() if err != nil { return errors.Annotate(err, "no model uuid set on api") } return setter.Migrate(agent.MigrateParams{ Model: modelTag, }) }) if err != nil { logger.Warningf("unable to save model uuid: %v", err) // Not really fatal, just annoying. } } }
// startFunc returns a StartFunc that creates a worker based on the manifolds // named in the supplied config. func startFunc(config ManifoldConfig) dependency.StartFunc { return func(getResource dependency.GetResourceFunc) (worker.Worker, error) { // Get dependencies and open a connection. var gate gate.Unlocker if err := getResource(config.APIInfoGateName, &gate); err != nil { return nil, err } var a agent.Agent if err := getResource(config.AgentName, &a); err != nil { return nil, err } conn, err := openConnection(a) if err != nil { return nil, errors.Annotate(err, "cannot open api") } // Add the environment uuid to agent config if not present. currentConfig := a.CurrentConfig() if currentConfig.Environment().Id() == "" { err := a.ChangeConfig(func(setter agent.ConfigSetter) error { environTag, err := conn.EnvironTag() if err != nil { return errors.Annotate(err, "no environment uuid set on api") } return setter.Migrate(agent.MigrateParams{ Environment: environTag, }) }) if err != nil { logger.Warningf("unable to save environment uuid: %v", err) // Not really fatal, just annoying. } } // Now we know the agent config has been fixed up, notify everyone // else who might depend upon its stability/correctness. gate.Unlock() // Return the worker. return newApiConnWorker(conn) } }
// 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 }, } }
// 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 }, } }