github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/unit/manifolds.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package unit 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/utils/voyeur" 13 "github.com/juju/version" 14 "github.com/prometheus/client_golang/prometheus" 15 "gopkg.in/juju/worker.v1/dependency" 16 17 coreagent "github.com/juju/juju/agent" 18 "github.com/juju/juju/api" 19 "github.com/juju/juju/api/base" 20 msapi "github.com/juju/juju/api/meterstatus" 21 "github.com/juju/juju/cmd/jujud/agent/engine" 22 "github.com/juju/juju/core/machinelock" 23 "github.com/juju/juju/core/status" 24 "github.com/juju/juju/state" 25 "github.com/juju/juju/utils/proxy" 26 "github.com/juju/juju/worker" 27 "github.com/juju/juju/worker/agent" 28 "github.com/juju/juju/worker/apiaddressupdater" 29 "github.com/juju/juju/worker/apicaller" 30 "github.com/juju/juju/worker/apiconfigwatcher" 31 "github.com/juju/juju/worker/fortress" 32 "github.com/juju/juju/worker/gate" 33 "github.com/juju/juju/worker/leadership" 34 "github.com/juju/juju/worker/logger" 35 "github.com/juju/juju/worker/logsender" 36 "github.com/juju/juju/worker/meterstatus" 37 "github.com/juju/juju/worker/metrics/collect" 38 "github.com/juju/juju/worker/metrics/sender" 39 "github.com/juju/juju/worker/metrics/spool" 40 "github.com/juju/juju/worker/migrationflag" 41 "github.com/juju/juju/worker/migrationminion" 42 "github.com/juju/juju/worker/proxyupdater" 43 "github.com/juju/juju/worker/retrystrategy" 44 "github.com/juju/juju/worker/uniter" 45 "github.com/juju/juju/worker/upgrader" 46 "github.com/juju/juju/worker/upgradesteps" 47 ) 48 49 // ManifoldsConfig allows specialisation of the result of Manifolds. 50 type ManifoldsConfig struct { 51 52 // Agent contains the agent that will be wrapped and made available to 53 // its dependencies via a dependency.Engine. 54 Agent coreagent.Agent 55 56 // LogSource will be read from by the logsender component. 57 LogSource logsender.LogRecordCh 58 59 // LeadershipGuarantee controls the behaviour of the leadership tracker. 60 LeadershipGuarantee time.Duration 61 62 // AgentConfigChanged is set whenever the unit agent's config 63 // is updated. 64 AgentConfigChanged *voyeur.Value 65 66 // ValidateMigration is called by the migrationminion during the 67 // migration process to check that the agent will be ok when 68 // connected to the new target controller. 69 ValidateMigration func(base.APICaller) error 70 71 // PrometheusRegisterer is a prometheus.Registerer that may be used 72 // by workers to register Prometheus metric collectors. 73 PrometheusRegisterer prometheus.Registerer 74 75 // UpdateLoggerConfig is a function that will save the specified 76 // config value as the logging config in the agent.conf file. 77 UpdateLoggerConfig func(string) error 78 79 // PreviousAgentVersion passes through the version the unit 80 // agent was running before the current restart. 81 PreviousAgentVersion version.Number 82 83 // UpgradeStepsLock is passed to the upgrade steps gate to 84 // coordinate workers that shouldn't do anything until the 85 // upgrade-steps worker is done. 86 UpgradeStepsLock gate.Lock 87 88 // UpgradeCheckLock is passed to the upgrade check gate to 89 // coordinate workers that shouldn't do anything until the 90 // upgrader worker completes it's first check. 91 UpgradeCheckLock gate.Lock 92 93 // PreUpgradeSteps is a function that is used by the upgradesteps 94 // worker to ensure that conditions are OK for an upgrade to 95 // proceed. 96 PreUpgradeSteps func(*state.StatePool, coreagent.Config, bool, bool) error 97 98 // MachineLock is a central source for acquiring the machine lock. 99 // This is used by a number of workers to ensure serialisation of actions 100 // across the machine. 101 MachineLock machinelock.Lock 102 } 103 104 // Manifolds returns a set of co-configured manifolds covering the various 105 // responsibilities of a standalone unit agent. It also accepts the logSource 106 // argument because we haven't figured out how to thread all the logging bits 107 // through a dependency engine yet. 108 // 109 // Thou Shalt Not Use String Literals In This Function. Or Else. 110 func Manifolds(config ManifoldsConfig) dependency.Manifolds { 111 112 // connectFilter exists to let us retry api connections immediately 113 // on password change, rather than causing the dependency engine to 114 // wait for a while. 115 connectFilter := func(err error) error { 116 cause := errors.Cause(err) 117 if cause == apicaller.ErrChangedPassword { 118 return dependency.ErrBounce 119 } else if cause == apicaller.ErrConnectImpossible { 120 return worker.ErrTerminateAgent 121 } 122 return err 123 } 124 125 return dependency.Manifolds{ 126 127 // The agent manifold references the enclosing agent, and is the 128 // foundation stone on which most other manifolds ultimately depend. 129 // (Currently, that is "all manifolds", but consider a shared clock.) 130 agentName: agent.Manifold(config.Agent), 131 132 // The api-config-watcher manifold monitors the API server 133 // addresses in the agent config and bounces when they 134 // change. It's required as part of model migrations. 135 apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{ 136 AgentName: agentName, 137 AgentConfigChanged: config.AgentConfigChanged, 138 }), 139 140 // The api caller is a thin concurrent wrapper around a connection 141 // to some API server. It's used by many other manifolds, which all 142 // select their own desired facades. It will be interesting to see 143 // how this works when we consolidate the agents; might be best to 144 // handle the auth changes server-side..? 145 apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ 146 AgentName: agentName, 147 APIConfigWatcherName: apiConfigWatcherName, 148 APIOpen: api.Open, 149 NewConnection: apicaller.ScaryConnect, 150 Filter: connectFilter, 151 }), 152 153 // The log sender is a leaf worker that sends log messages to some 154 // API server, when configured so to do. We should only need one of 155 // these in a consolidated agent. 156 logSenderName: logsender.Manifold(logsender.ManifoldConfig{ 157 APICallerName: apiCallerName, 158 LogSource: config.LogSource, 159 }), 160 161 // The upgrade steps gate is used to coordinate workers which 162 // shouldn't do anything until the upgrade-steps worker has 163 // finished running any required upgrade steps. The flag of 164 // similar name is used to implement the isFullyUpgraded func 165 // that keeps upgrade concerns out of unrelated manifolds. 166 upgradeStepsGateName: gate.ManifoldEx(config.UpgradeStepsLock), 167 upgradeStepsFlagName: gate.FlagManifold(gate.FlagManifoldConfig{ 168 GateName: upgradeStepsGateName, 169 NewWorker: gate.NewFlagWorker, 170 }), 171 172 // The upgrade check gate is used to coordinate workers which 173 // shouldn't do anything until the upgrader worker has 174 // completed its first check for a new tools version to 175 // upgrade to. The flag of similar name is used to implement 176 // the isFullyUpgraded func that keeps upgrade concerns out of 177 // unrelated manifolds. 178 upgradeCheckGateName: gate.ManifoldEx(config.UpgradeCheckLock), 179 upgradeCheckFlagName: gate.FlagManifold(gate.FlagManifoldConfig{ 180 GateName: upgradeCheckGateName, 181 NewWorker: gate.NewFlagWorker, 182 }), 183 184 // The upgrader is a leaf worker that returns a specific error type 185 // recognised by the unit agent, causing other workers to be stopped 186 // and the agent to be restarted running the new tools. We should only 187 // need one of these in a consolidated agent, but we'll need to be 188 // careful about behavioural differences, and interactions with the 189 // upgradesteps worker. 190 upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{ 191 AgentName: agentName, 192 APICallerName: apiCallerName, 193 UpgradeStepsGateName: upgradeStepsGateName, 194 UpgradeCheckGateName: upgradeCheckGateName, 195 PreviousAgentVersion: config.PreviousAgentVersion, 196 }), 197 198 // The upgradesteps worker runs soon after the unit agent 199 // starts and runs any steps required to upgrade to the 200 // running jujud version. Once upgrade steps have run, the 201 // upgradesteps gate is unlocked and the worker exits. 202 upgradeStepsName: upgradesteps.Manifold(upgradesteps.ManifoldConfig{ 203 AgentName: agentName, 204 APICallerName: apiCallerName, 205 UpgradeStepsGateName: upgradeStepsGateName, 206 // Realistically, units should not open state for any reason. 207 OpenStateForUpgrade: func() (*state.StatePool, error) { 208 return nil, errors.New("unit agent cannot open state") 209 }, 210 PreUpgradeSteps: config.PreUpgradeSteps, 211 NewAgentStatusSetter: func(apiConn api.Connection) (upgradesteps.StatusSetter, error) { 212 return &noopStatusSetter{}, nil 213 }, 214 }), 215 216 // The migration workers collaborate to run migrations; 217 // and to create a mechanism for running other workers 218 // so they can't accidentally interfere with a migration 219 // in progress. Such a manifold should (1) depend on the 220 // migration-inactive flag, to know when to start or die; 221 // and (2) occupy the migration-fortress, so as to avoid 222 // possible interference with the minion (which will not 223 // take action until it's gained sole control of the 224 // fortress). 225 migrationFortressName: ifFullyUpgraded(fortress.Manifold()), 226 migrationInactiveFlagName: migrationflag.Manifold(migrationflag.ManifoldConfig{ 227 APICallerName: apiCallerName, 228 Check: migrationflag.IsTerminal, 229 NewFacade: migrationflag.NewFacade, 230 NewWorker: migrationflag.NewWorker, 231 }), 232 migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{ 233 AgentName: agentName, 234 APICallerName: apiCallerName, 235 FortressName: migrationFortressName, 236 APIOpen: api.Open, 237 ValidateMigration: config.ValidateMigration, 238 NewFacade: migrationminion.NewFacade, 239 NewWorker: migrationminion.NewWorker, 240 }), 241 242 // The logging config updater is a leaf worker that indirectly 243 // controls the messages sent via the log sender according to 244 // changes in environment config. We should only need one of 245 // these in a consolidated agent. 246 loggingConfigUpdaterName: ifNotMigrating(logger.Manifold(logger.ManifoldConfig{ 247 AgentName: agentName, 248 APICallerName: apiCallerName, 249 UpdateAgentFunc: config.UpdateLoggerConfig, 250 })), 251 252 // The api address updater is a leaf worker that rewrites agent config 253 // as the controller addresses change. We should only need one of 254 // these in a consolidated agent. 255 apiAddressUpdaterName: ifNotMigrating(apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{ 256 AgentName: agentName, 257 APICallerName: apiCallerName, 258 })), 259 260 // The proxy config updater is a leaf worker that sets http/https/apt/etc 261 // proxy settings. 262 // TODO(fwereade): timing of this is suspicious. There was superstitious 263 // code trying to run this early; if that ever helped, it was only by 264 // coincidence. Probably we ought to be making components that might 265 // need proxy config into explicit dependencies of the proxy updater... 266 proxyConfigUpdaterName: ifNotMigrating(proxyupdater.Manifold(proxyupdater.ManifoldConfig{ 267 AgentName: agentName, 268 APICallerName: apiCallerName, 269 Logger: loggo.GetLogger("juju.worker.proxyupdater"), 270 WorkerFunc: proxyupdater.NewWorker, 271 InProcessUpdate: proxy.DefaultConfig.Set, 272 })), 273 274 // The charmdir resource coordinates whether the charm directory is 275 // available or not; after 'start' hook and before 'stop' hook 276 // executes, and not during upgrades. 277 charmDirName: ifNotMigrating(fortress.Manifold()), 278 279 // The leadership tracker attempts to secure and retain leadership of 280 // the unit's service, and is consulted on such matters by the 281 // uniter. As it stands today, we'll need one per unit in a 282 // consolidated agent. 283 leadershipTrackerName: ifNotMigrating(leadership.Manifold(leadership.ManifoldConfig{ 284 AgentName: agentName, 285 APICallerName: apiCallerName, 286 Clock: clock.WallClock, 287 LeadershipGuarantee: config.LeadershipGuarantee, 288 })), 289 290 // HookRetryStrategy uses a retrystrategy worker to get a 291 // retry strategy that will be used by the uniter to run its hooks. 292 hookRetryStrategyName: ifNotMigrating(retrystrategy.Manifold(retrystrategy.ManifoldConfig{ 293 AgentName: agentName, 294 APICallerName: apiCallerName, 295 NewFacade: retrystrategy.NewFacade, 296 NewWorker: retrystrategy.NewRetryStrategyWorker, 297 })), 298 299 // The uniter installs charms; manages the unit's presence in its 300 // relations; creates suboordinate units; runs all the hooks; sends 301 // metrics; etc etc etc. We expect to break it up further in the 302 // coming weeks, and to need one per unit in a consolidated agent 303 // (and probably one for each component broken out). 304 uniterName: ifNotMigrating(uniter.Manifold(uniter.ManifoldConfig{ 305 AgentName: agentName, 306 APICallerName: apiCallerName, 307 MachineLock: config.MachineLock, 308 Clock: clock.WallClock, 309 LeadershipTrackerName: leadershipTrackerName, 310 CharmDirName: charmDirName, 311 HookRetryStrategyName: hookRetryStrategyName, 312 TranslateResolverErr: uniter.TranslateFortressErrors, 313 })), 314 315 // TODO (mattyw) should be added to machine agent. 316 metricSpoolName: ifNotMigrating(spool.Manifold(spool.ManifoldConfig{ 317 AgentName: agentName, 318 })), 319 320 // The metric collect worker executes the collect-metrics hook in a 321 // restricted context that can safely run concurrently with other hooks. 322 metricCollectName: ifNotMigrating(collect.Manifold(collect.ManifoldConfig{ 323 AgentName: agentName, 324 MetricSpoolName: metricSpoolName, 325 CharmDirName: charmDirName, 326 })), 327 328 // The meter status worker executes the meter-status-changed hook when it detects 329 // that the meter status has changed. 330 meterStatusName: ifNotMigrating(meterstatus.Manifold(meterstatus.ManifoldConfig{ 331 AgentName: agentName, 332 APICallerName: apiCallerName, 333 MachineLock: config.MachineLock, 334 Clock: clock.WallClock, 335 NewHookRunner: meterstatus.NewHookRunner, 336 NewMeterStatusAPIClient: msapi.NewClient, 337 NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker, 338 NewIsolatedStatusWorker: meterstatus.NewIsolatedStatusWorker, 339 })), 340 341 // The metric sender worker periodically sends accumulated metrics to the controller. 342 metricSenderName: ifNotMigrating(sender.Manifold(sender.ManifoldConfig{ 343 AgentName: agentName, 344 APICallerName: apiCallerName, 345 MetricSpoolName: metricSpoolName, 346 })), 347 } 348 } 349 350 var ifFullyUpgraded = engine.Housing{ 351 Flags: []string{ 352 upgradeStepsFlagName, 353 upgradeCheckFlagName, 354 }, 355 }.Decorate 356 357 var ifNotMigrating = engine.Housing{ 358 Flags: []string{ 359 migrationInactiveFlagName, 360 }, 361 Occupy: migrationFortressName, 362 }.Decorate 363 364 const ( 365 agentName = "agent" 366 apiConfigWatcherName = "api-config-watcher" 367 apiCallerName = "api-caller" 368 logSenderName = "log-sender" 369 370 upgraderName = "upgrader" 371 upgradeStepsName = "upgrade-steps-runner" 372 upgradeStepsGateName = "upgrade-steps-gate" 373 upgradeStepsFlagName = "upgrade-steps-flag" 374 upgradeCheckGateName = "upgrade-check-gate" 375 upgradeCheckFlagName = "upgrade-check-flag" 376 377 migrationFortressName = "migration-fortress" 378 migrationInactiveFlagName = "migration-inactive-flag" 379 migrationMinionName = "migration-minion" 380 381 loggingConfigUpdaterName = "logging-config-updater" 382 proxyConfigUpdaterName = "proxy-config-updater" 383 apiAddressUpdaterName = "api-address-updater" 384 385 charmDirName = "charm-dir" 386 leadershipTrackerName = "leadership-tracker" 387 hookRetryStrategyName = "hook-retry-strategy" 388 uniterName = "uniter" 389 390 metricSpoolName = "metric-spool" 391 meterStatusName = "meter-status" 392 metricCollectName = "metric-collect" 393 metricSenderName = "metric-sender" 394 ) 395 396 type noopStatusSetter struct{} 397 398 // SetStatus implements upgradesteps.StatusSetter 399 func (a *noopStatusSetter) SetStatus(setableStatus status.Status, info string, data map[string]interface{}) error { 400 return nil 401 }