github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/agentconfigupdater/manifold.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agentconfigupdater 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/pubsub/v2" 9 "github.com/juju/worker/v3" 10 "github.com/juju/worker/v3/dependency" 11 12 coreagent "github.com/juju/juju/agent" 13 apiagent "github.com/juju/juju/api/agent/agent" 14 "github.com/juju/juju/api/base" 15 "github.com/juju/juju/mongo" 16 jworker "github.com/juju/juju/worker" 17 ) 18 19 // Logger defines the logging methods used by the worker. 20 type Logger interface { 21 Criticalf(string, ...interface{}) 22 Warningf(string, ...interface{}) 23 Infof(string, ...interface{}) 24 Debugf(string, ...interface{}) 25 Tracef(string, ...interface{}) 26 } 27 28 // ManifoldConfig provides the dependencies for the 29 // agent config updater manifold. 30 type ManifoldConfig struct { 31 AgentName string 32 APICallerName string 33 CentralHubName string 34 Logger Logger 35 } 36 37 // Manifold defines a simple start function which 38 // runs after the API connection has come up. If the machine agent is 39 // a controller, it grabs the state serving info over the API and 40 // records it to agent configuration, and then stops. 41 func Manifold(config ManifoldConfig) dependency.Manifold { 42 return dependency.Manifold{ 43 Inputs: []string{ 44 config.AgentName, 45 config.APICallerName, 46 config.CentralHubName, 47 }, 48 Start: func(context dependency.Context) (worker.Worker, error) { 49 // Get the agent. 50 var agent coreagent.Agent 51 if err := context.Get(config.AgentName, &agent); err != nil { 52 return nil, err 53 } 54 55 // Grab the tag and ensure that it's for a controller. 56 tag := agent.CurrentConfig().Tag() 57 if !apiagent.IsAllowedControllerTag(tag.Kind()) { 58 return nil, errors.New("agent's tag is not a machine or controller agent tag") 59 } 60 61 // Get API connection. 62 var apiCaller base.APICaller 63 if err := context.Get(config.APICallerName, &apiCaller); err != nil { 64 return nil, err 65 } 66 // If the machine needs State, grab the state serving info 67 // over the API and write it to the agent configuration. 68 if controller, err := apiagent.IsController(apiCaller, tag); err != nil { 69 return nil, errors.Annotate(err, "checking controller status") 70 } else if !controller { 71 // Not a controller, nothing to do. 72 return nil, dependency.ErrUninstall 73 } 74 75 // Do the initial state serving info and mongo profile checks 76 // before attempting to get the central hub. The central hub is only 77 // running when the agent is a controller. If the agent isn't a controller 78 // but should be, the agent config will not have any state serving info 79 // but the database will think that we should be. In those situations 80 // we need to update the local config and restart. 81 apiState, err := apiagent.NewState(apiCaller) 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 controllerConfig, err := apiState.ControllerConfig() 86 if err != nil { 87 return nil, errors.Annotate(err, "getting controller config") 88 } 89 90 logger := config.Logger 91 92 // If the mongo memory profile from the controller config 93 // is different from the one in the agent config we need to 94 // restart the agent to apply the memory profile to the mongo 95 // service. 96 agentsMongoMemoryProfile := agent.CurrentConfig().MongoMemoryProfile() 97 configMongoMemoryProfile := mongo.MemoryProfile(controllerConfig.MongoMemoryProfile()) 98 mongoProfileChanged := agentsMongoMemoryProfile != configMongoMemoryProfile 99 100 agentsJujuDBSnapChannel := agent.CurrentConfig().JujuDBSnapChannel() 101 configJujuDBSnapChannel := controllerConfig.JujuDBSnapChannel() 102 jujuDBSnapChannelChanged := agentsJujuDBSnapChannel != configJujuDBSnapChannel 103 104 agentsQueryTracingEnabled := agent.CurrentConfig().QueryTracingEnabled() 105 configQueryTracingEnabled := controllerConfig.QueryTracingEnabled() 106 queryTracingEnabledChanged := agentsQueryTracingEnabled != configQueryTracingEnabled 107 108 agentsQueryTracingThreshold := agent.CurrentConfig().QueryTracingThreshold() 109 configQueryTracingThreshold := controllerConfig.QueryTracingThreshold() 110 queryTracingThresholdChanged := agentsQueryTracingThreshold != configQueryTracingThreshold 111 112 info, err := apiState.StateServingInfo() 113 if err != nil { 114 return nil, errors.Annotate(err, "getting state serving info") 115 } 116 err = agent.ChangeConfig(func(config coreagent.ConfigSetter) error { 117 existing, hasInfo := config.StateServingInfo() 118 if hasInfo { 119 // Use the existing cert and key as they appear to 120 // have been already updated by the cert updater 121 // worker to have this machine's IP address as 122 // part of the cert. This changed cert is never 123 // put back into the database, so it isn't 124 // reflected in the copy we have got from 125 // apiState. 126 info.Cert = existing.Cert 127 info.PrivateKey = existing.PrivateKey 128 } 129 config.SetStateServingInfo(info) 130 if mongoProfileChanged { 131 logger.Debugf("setting agent config mongo memory profile: %q => %q", agentsMongoMemoryProfile, configMongoMemoryProfile) 132 config.SetMongoMemoryProfile(configMongoMemoryProfile) 133 } 134 if jujuDBSnapChannelChanged { 135 logger.Debugf("setting agent config mongo snap channel: %q => %q", agentsJujuDBSnapChannel, configJujuDBSnapChannel) 136 config.SetJujuDBSnapChannel(configJujuDBSnapChannel) 137 } 138 if queryTracingEnabledChanged { 139 logger.Debugf("setting agent config query tracing enabled: %t => %t", agentsQueryTracingEnabled, configQueryTracingEnabled) 140 config.SetQueryTracingEnabled(configQueryTracingEnabled) 141 } 142 if queryTracingThresholdChanged { 143 logger.Debugf("setting agent config query tracing threshold: %d => %d", agentsQueryTracingThreshold, configQueryTracingThreshold) 144 config.SetQueryTracingThreshold(configQueryTracingThreshold) 145 } 146 147 return nil 148 }) 149 if err != nil { 150 return nil, errors.Trace(err) 151 } 152 153 // If we need a restart, return the fatal error. 154 if mongoProfileChanged { 155 logger.Infof("restarting agent for new mongo memory profile") 156 return nil, jworker.ErrRestartAgent 157 } else if jujuDBSnapChannelChanged { 158 logger.Infof("restarting agent for new mongo snap channel") 159 return nil, jworker.ErrRestartAgent 160 } else if queryTracingEnabledChanged { 161 logger.Infof("restarting agent for new query tracing enabled") 162 return nil, jworker.ErrRestartAgent 163 } else if queryTracingThresholdChanged { 164 logger.Infof("restarting agent for new query tracing threshold") 165 return nil, jworker.ErrRestartAgent 166 } 167 168 // Only get the hub if we are a controller and we haven't updated 169 // the memory profile. 170 var hub *pubsub.StructuredHub 171 if err := context.Get(config.CentralHubName, &hub); err != nil { 172 logger.Tracef("hub dependency not available") 173 return nil, err 174 } 175 176 return NewWorker(WorkerConfig{ 177 Agent: agent, 178 Hub: hub, 179 MongoProfile: configMongoMemoryProfile, 180 JujuDBSnapChannel: configJujuDBSnapChannel, 181 QueryTracingEnabled: configQueryTracingEnabled, 182 QueryTracingThreshold: configQueryTracingThreshold, 183 Logger: config.Logger, 184 }) 185 }, 186 } 187 }