github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/machine/manifolds.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package machine 5 6 import ( 7 "runtime" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/utils/proxy" 12 "github.com/juju/utils/voyeur" 13 14 coreagent "github.com/juju/juju/agent" 15 "github.com/juju/juju/api" 16 "github.com/juju/juju/api/base" 17 apideployer "github.com/juju/juju/api/deployer" 18 "github.com/juju/juju/cmd/jujud/agent/engine" 19 "github.com/juju/juju/container/lxd" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/worker" 22 "github.com/juju/juju/worker/agent" 23 "github.com/juju/juju/worker/apiaddressupdater" 24 "github.com/juju/juju/worker/apicaller" 25 "github.com/juju/juju/worker/apiconfigwatcher" 26 "github.com/juju/juju/worker/authenticationworker" 27 "github.com/juju/juju/worker/dependency" 28 "github.com/juju/juju/worker/deployer" 29 "github.com/juju/juju/worker/diskmanager" 30 "github.com/juju/juju/worker/fortress" 31 "github.com/juju/juju/worker/gate" 32 "github.com/juju/juju/worker/hostkeyreporter" 33 "github.com/juju/juju/worker/identityfilewriter" 34 "github.com/juju/juju/worker/logforwarder" 35 "github.com/juju/juju/worker/logforwarder/sinks" 36 "github.com/juju/juju/worker/logger" 37 "github.com/juju/juju/worker/logsender" 38 "github.com/juju/juju/worker/machineactions" 39 "github.com/juju/juju/worker/machiner" 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/reboot" 44 "github.com/juju/juju/worker/resumer" 45 workerstate "github.com/juju/juju/worker/state" 46 "github.com/juju/juju/worker/stateconfigwatcher" 47 "github.com/juju/juju/worker/storageprovisioner" 48 "github.com/juju/juju/worker/terminationworker" 49 "github.com/juju/juju/worker/toolsversionchecker" 50 "github.com/juju/juju/worker/upgrader" 51 "github.com/juju/juju/worker/upgradesteps" 52 "github.com/juju/utils/clock" 53 "github.com/juju/version" 54 ) 55 56 // ManifoldsConfig allows specialisation of the result of Manifolds. 57 type ManifoldsConfig struct { 58 59 // Agent contains the agent that will be wrapped and made available to 60 // its dependencies via a dependency.Engine. 61 Agent coreagent.Agent 62 63 // AgentConfigChanged is set whenever the machine agent's config 64 // is updated. 65 AgentConfigChanged *voyeur.Value 66 67 // RootDir is the root directory that any worker that needs to 68 // access local filesystems should use as a base. In actual use it 69 // will be "" but it may be overriden in tests. 70 RootDir string 71 72 // PreviousAgentVersion passes through the version the machine 73 // agent was running before the current restart. 74 PreviousAgentVersion version.Number 75 76 // UpgradeStepsLock is passed to the upgrade steps gate to 77 // coordinate workers that shouldn't do anything until the 78 // upgrade-steps worker is done. 79 UpgradeStepsLock gate.Lock 80 81 // UpgradeCheckLock is passed to the upgrade check gate to 82 // coordinate workers that shouldn't do anything until the 83 // upgrader worker completes it's first check. 84 UpgradeCheckLock gate.Lock 85 86 // OpenState is function used by the state manifold to create a 87 // *state.State. 88 OpenState func(coreagent.Config) (*state.State, error) 89 90 // OpenStateForUpgrade is a function the upgradesteps worker can 91 // use to establish a connection to state. 92 OpenStateForUpgrade func() (*state.State, error) 93 94 // StartStateWorkers is function called by the stateworkers 95 // manifold to start workers which rely on a *state.State but 96 // which haven't been converted to run directly under the 97 // dependency engine yet. This will go once these workers have 98 // been converted. 99 StartStateWorkers func(*state.State) (worker.Worker, error) 100 101 // StartAPIWorkers is passed to the apiworkers manifold. It starts 102 // workers which rely on an API connection (which have not yet 103 // been converted to work directly with the dependency engine). 104 StartAPIWorkers func(api.Connection) (worker.Worker, error) 105 106 // PreUpgradeSteps is a function that is used by the upgradesteps 107 // worker to ensure that conditions are OK for an upgrade to 108 // proceed. 109 PreUpgradeSteps func(*state.State, coreagent.Config, bool, bool) error 110 111 // LogSource defines the channel type used to send log message 112 // structs within the machine agent. 113 LogSource logsender.LogRecordCh 114 115 // newDeployContext gives the tests the opportunity to create a deployer.Context 116 // that can be used for testing so as to avoid (1) deploying units to the system 117 // running the tests and (2) get access to the *State used internally, so that 118 // tests can be run without waiting for the 5s watcher refresh time to which we would 119 // otherwise be restricted. 120 NewDeployContext func(st *apideployer.State, agentConfig coreagent.Config) deployer.Context 121 122 // Clock supplies timekeeping services to various workers. 123 Clock clock.Clock 124 125 // ValidateMigration is called by the migrationminion during the 126 // migration process to check that the agent will be ok when 127 // connected to the new target controller. 128 ValidateMigration func(base.APICaller) error 129 } 130 131 // Manifolds returns a set of co-configured manifolds covering the 132 // various responsibilities of a machine agent. 133 // 134 // Thou Shalt Not Use String Literals In This Function. Or Else. 135 func Manifolds(config ManifoldsConfig) dependency.Manifolds { 136 137 // connectFilter exists: 138 // 1) to let us retry api connections immediately on password change, 139 // rather than causing the dependency engine to wait for a while; 140 // 2) to ensure that certain connection failures correctly trigger 141 // complete agent removal. (It's not safe to let any agent other 142 // than the machine mess around with SetCanUninstall). 143 connectFilter := func(err error) error { 144 cause := errors.Cause(err) 145 if cause == apicaller.ErrConnectImpossible { 146 err2 := coreagent.SetCanUninstall(config.Agent) 147 if err2 != nil { 148 return errors.Trace(err2) 149 } 150 return worker.ErrTerminateAgent 151 } else if cause == apicaller.ErrChangedPassword { 152 return dependency.ErrBounce 153 } 154 return err 155 } 156 var externalUpdateProxyFunc func(proxy.Settings) error 157 if runtime.GOOS == "linux" { 158 externalUpdateProxyFunc = lxd.ConfigureLXDProxies 159 } 160 161 return dependency.Manifolds{ 162 // The agent manifold references the enclosing agent, and is the 163 // foundation stone on which most other manifolds ultimately depend. 164 agentName: agent.Manifold(config.Agent), 165 166 // The termination worker returns ErrTerminateAgent if a 167 // termination signal is received by the process it's running 168 // in. It has no inputs and its only output is the error it 169 // returns. It depends on the uninstall file having been 170 // written *by the manual provider* at install time; it would 171 // be Very Wrong Indeed to use SetCanUninstall in conjunction 172 // with this code. 173 terminationName: terminationworker.Manifold(), 174 175 // The stateconfigwatcher manifold watches the machine agent's 176 // configuration and reports if state serving info is 177 // present. It will bounce itself if state serving info is 178 // added or removed. It is intended as a dependency just for 179 // the state manifold. 180 stateConfigWatcherName: stateconfigwatcher.Manifold(stateconfigwatcher.ManifoldConfig{ 181 AgentName: agentName, 182 AgentConfigChanged: config.AgentConfigChanged, 183 }), 184 185 // The state manifold creates a *state.State and makes it 186 // available to other manifolds. It pings the mongodb session 187 // regularly and will die if pings fail. 188 stateName: workerstate.Manifold(workerstate.ManifoldConfig{ 189 AgentName: agentName, 190 StateConfigWatcherName: stateConfigWatcherName, 191 OpenState: config.OpenState, 192 }), 193 194 // The stateworkers manifold starts workers which rely on a 195 // *state.State but which haven't been converted to run 196 // directly under the dependency engine yet. This manifold 197 // will be removed once all such workers have been converted; 198 // until then, the workers are expected to handle their own 199 // checks for upgrades etc, rather than blocking this whole 200 // worker on upgrade completion. 201 stateWorkersName: StateWorkersManifold(StateWorkersConfig{ 202 StateName: stateName, 203 StartStateWorkers: config.StartStateWorkers, 204 }), 205 206 // The api-config-watcher manifold monitors the API server 207 // addresses in the agent config and bounces when they 208 // change. It's required as part of model migrations. 209 apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{ 210 AgentName: agentName, 211 AgentConfigChanged: config.AgentConfigChanged, 212 }), 213 214 // The api caller is a thin concurrent wrapper around a connection 215 // to some API server. It's used by many other manifolds, which all 216 // select their own desired facades. It will be interesting to see 217 // how this works when we consolidate the agents; might be best to 218 // handle the auth changes server-side..? 219 apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ 220 AgentName: agentName, 221 APIConfigWatcherName: apiConfigWatcherName, 222 APIOpen: api.Open, 223 NewConnection: apicaller.ScaryConnect, 224 Filter: connectFilter, 225 }), 226 227 // The upgrade steps gate is used to coordinate workers which 228 // shouldn't do anything until the upgrade-steps worker has 229 // finished running any required upgrade steps. The flag of 230 // similar name is used to implement the isFullyUpgraded func 231 // that keeps upgrade concerns out of unrelated manifolds. 232 upgradeStepsGateName: gate.ManifoldEx(config.UpgradeStepsLock), 233 upgradeStepsFlagName: gate.FlagManifold(gate.FlagManifoldConfig{ 234 GateName: upgradeStepsGateName, 235 NewWorker: gate.NewFlagWorker, 236 }), 237 238 // The upgrade check gate is used to coordinate workers which 239 // shouldn't do anything until the upgrader worker has 240 // completed its first check for a new tools version to 241 // upgrade to. The flag of similar name is used to implement 242 // the isFullyUpgraded func that keeps upgrade concerns out of 243 // unrelated manifolds. 244 upgradeCheckGateName: gate.ManifoldEx(config.UpgradeCheckLock), 245 upgradeCheckFlagName: gate.FlagManifold(gate.FlagManifoldConfig{ 246 GateName: upgradeCheckGateName, 247 NewWorker: gate.NewFlagWorker, 248 }), 249 250 // The upgrader is a leaf worker that returns a specific error 251 // type recognised by the machine agent, causing other workers 252 // to be stopped and the agent to be restarted running the new 253 // tools. We should only need one of these in a consolidated 254 // agent, but we'll need to be careful about behavioural 255 // differences, and interactions with the upgrade-steps 256 // worker. 257 upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{ 258 AgentName: agentName, 259 APICallerName: apiCallerName, 260 UpgradeStepsGateName: upgradeStepsGateName, 261 UpgradeCheckGateName: upgradeCheckGateName, 262 PreviousAgentVersion: config.PreviousAgentVersion, 263 }), 264 265 // The upgradesteps worker runs soon after the machine agent 266 // starts and runs any steps required to upgrade to the 267 // running jujud version. Once upgrade steps have run, the 268 // upgradesteps gate is unlocked and the worker exits. 269 upgradeStepsName: upgradesteps.Manifold(upgradesteps.ManifoldConfig{ 270 AgentName: agentName, 271 APICallerName: apiCallerName, 272 UpgradeStepsGateName: upgradeStepsGateName, 273 OpenStateForUpgrade: config.OpenStateForUpgrade, 274 PreUpgradeSteps: config.PreUpgradeSteps, 275 }), 276 277 // The migration workers collaborate to run migrations; 278 // and to create a mechanism for running other workers 279 // so they can't accidentally interfere with a migration 280 // in progress. Such a manifold should (1) depend on the 281 // migration-inactive flag, to know when to start or die; 282 // and (2) occupy the migration-fortress, so as to avoid 283 // possible interference with the minion (which will not 284 // take action until it's gained sole control of the 285 // fortress). 286 // 287 // Note that the fortress itself will not be created 288 // until the upgrade process is complete; this frees all 289 // its dependencies from upgrade concerns. 290 migrationFortressName: ifFullyUpgraded(fortress.Manifold()), 291 migrationInactiveFlagName: migrationflag.Manifold(migrationflag.ManifoldConfig{ 292 APICallerName: apiCallerName, 293 Check: migrationflag.IsTerminal, 294 NewFacade: migrationflag.NewFacade, 295 NewWorker: migrationflag.NewWorker, 296 }), 297 migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{ 298 AgentName: agentName, 299 APICallerName: apiCallerName, 300 FortressName: migrationFortressName, 301 APIOpen: api.Open, 302 ValidateMigration: config.ValidateMigration, 303 NewFacade: migrationminion.NewFacade, 304 NewWorker: migrationminion.NewWorker, 305 }), 306 307 // The serving-info-setter manifold sets grabs the state 308 // serving info from the API connection and writes it to the 309 // agent config. 310 servingInfoSetterName: ifNotMigrating(ServingInfoSetterManifold(ServingInfoSetterConfig{ 311 AgentName: agentName, 312 APICallerName: apiCallerName, 313 })), 314 315 // The apiworkers manifold starts workers which rely on the 316 // machine agent's API connection but have not been converted 317 // to work directly under the dependency engine. It waits for 318 // upgrades to be finished before starting these workers. 319 apiWorkersName: ifNotMigrating(APIWorkersManifold(APIWorkersConfig{ 320 APICallerName: apiCallerName, 321 StartAPIWorkers: config.StartAPIWorkers, 322 })), 323 324 // The reboot manifold manages a worker which will reboot the 325 // machine when requested. It needs an API connection and 326 // waits for upgrades to be complete. 327 rebootName: ifNotMigrating(reboot.Manifold(reboot.ManifoldConfig{ 328 AgentName: agentName, 329 APICallerName: apiCallerName, 330 MachineLockName: coreagent.MachineLockName, 331 Clock: config.Clock, 332 })), 333 334 // The logging config updater is a leaf worker that indirectly 335 // controls the messages sent via the log sender or rsyslog, 336 // according to changes in environment config. We should only need 337 // one of these in a consolidated agent. 338 loggingConfigUpdaterName: ifNotMigrating(logger.Manifold(logger.ManifoldConfig{ 339 AgentName: agentName, 340 APICallerName: apiCallerName, 341 })), 342 343 // The diskmanager worker periodically lists block devices on the 344 // machine it runs on. This worker will be run on all Juju-managed 345 // machines (one per machine agent). 346 diskManagerName: ifNotMigrating(diskmanager.Manifold(diskmanager.ManifoldConfig{ 347 AgentName: agentName, 348 APICallerName: apiCallerName, 349 })), 350 351 // The proxy config updater is a leaf worker that sets http/https/apt/etc 352 // proxy settings. 353 proxyConfigUpdater: ifNotMigrating(proxyupdater.Manifold(proxyupdater.ManifoldConfig{ 354 AgentName: agentName, 355 APICallerName: apiCallerName, 356 WorkerFunc: proxyupdater.NewWorker, 357 ExternalUpdate: externalUpdateProxyFunc, 358 })), 359 360 // The api address updater is a leaf worker that rewrites agent config 361 // as the state server addresses change. We should only need one of 362 // these in a consolidated agent. 363 apiAddressUpdaterName: ifNotMigrating(apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{ 364 AgentName: agentName, 365 APICallerName: apiCallerName, 366 })), 367 368 // The machiner Worker will wait for the identified machine to become 369 // Dying and make it Dead; or until the machine becomes Dead by other 370 // means. 371 machinerName: ifNotMigrating(machiner.Manifold(machiner.ManifoldConfig{ 372 AgentName: agentName, 373 APICallerName: apiCallerName, 374 })), 375 376 // The log sender is a leaf worker that sends log messages to some 377 // API server, when configured so to do. We should only need one of 378 // these in a consolidated agent. 379 // 380 // NOTE: the LogSource will buffer a large number of messages as an upgrade 381 // runs; it currently seems better to fill the buffer and send when stable, 382 // optimising for stable controller upgrades rather than up-to-the-moment 383 // observable normal-machine upgrades. 384 logSenderName: ifNotMigrating(logsender.Manifold(logsender.ManifoldConfig{ 385 APICallerName: apiCallerName, 386 LogSource: config.LogSource, 387 })), 388 389 // The deployer worker is responsible for deploying and recalling unit 390 // agents, according to changes in a set of state units; and for the 391 // final removal of its agents' units from state when they are no 392 // longer needed. 393 deployerName: ifNotMigrating(deployer.Manifold(deployer.ManifoldConfig{ 394 NewDeployContext: config.NewDeployContext, 395 AgentName: agentName, 396 APICallerName: apiCallerName, 397 })), 398 399 authenticationWorkerName: ifNotMigrating(authenticationworker.Manifold(authenticationworker.ManifoldConfig{ 400 AgentName: agentName, 401 APICallerName: apiCallerName, 402 })), 403 404 // The storageProvisioner worker manages provisioning 405 // (deprovisioning), and attachment (detachment) of first-class 406 // volumes and filesystems. 407 storageProvisionerName: ifNotMigrating(storageprovisioner.MachineManifold(storageprovisioner.MachineManifoldConfig{ 408 AgentName: agentName, 409 APICallerName: apiCallerName, 410 Clock: config.Clock, 411 })), 412 413 resumerName: ifNotMigrating(resumer.Manifold(resumer.ManifoldConfig{ 414 AgentName: agentName, 415 APICallerName: apiCallerName, 416 Clock: config.Clock, 417 Interval: time.Minute, 418 NewFacade: resumer.NewFacade, 419 NewWorker: resumer.NewWorker, 420 })), 421 422 identityFileWriterName: ifNotMigrating(identityfilewriter.Manifold(identityfilewriter.ManifoldConfig{ 423 AgentName: agentName, 424 APICallerName: apiCallerName, 425 })), 426 427 toolsVersionCheckerName: ifNotMigrating(toolsversionchecker.Manifold(toolsversionchecker.ManifoldConfig{ 428 AgentName: agentName, 429 APICallerName: apiCallerName, 430 })), 431 432 machineActionName: ifNotMigrating(machineactions.Manifold(machineactions.ManifoldConfig{ 433 AgentName: agentName, 434 APICallerName: apiCallerName, 435 NewFacade: machineactions.NewFacade, 436 NewWorker: machineactions.NewMachineActionsWorker, 437 })), 438 439 hostKeyReporterName: ifNotMigrating(hostkeyreporter.Manifold(hostkeyreporter.ManifoldConfig{ 440 AgentName: agentName, 441 APICallerName: apiCallerName, 442 RootDir: config.RootDir, 443 NewFacade: hostkeyreporter.NewFacade, 444 NewWorker: hostkeyreporter.NewWorker, 445 })), 446 logForwarderName: ifFullyUpgraded(logforwarder.Manifold(logforwarder.ManifoldConfig{ 447 StateName: stateName, 448 APICallerName: apiCallerName, 449 Sinks: []logforwarder.LogSinkSpec{{ 450 Name: "juju-log-forward", 451 OpenFn: sinks.OpenSyslog, 452 }}, 453 })), 454 } 455 } 456 457 var ifFullyUpgraded = engine.Housing{ 458 Flags: []string{ 459 upgradeStepsFlagName, 460 upgradeCheckFlagName, 461 }, 462 }.Decorate 463 464 var ifNotMigrating = engine.Housing{ 465 Flags: []string{ 466 migrationInactiveFlagName, 467 }, 468 Occupy: migrationFortressName, 469 }.Decorate 470 471 const ( 472 agentName = "agent" 473 terminationName = "termination-signal-handler" 474 stateConfigWatcherName = "state-config-watcher" 475 stateName = "state" 476 stateWorkersName = "unconverted-state-workers" 477 apiCallerName = "api-caller" 478 apiConfigWatcherName = "api-config-watcher" 479 480 upgraderName = "upgrader" 481 upgradeStepsName = "upgrade-steps-runner" 482 upgradeStepsGateName = "upgrade-steps-gate" 483 upgradeStepsFlagName = "upgrade-steps-flag" 484 upgradeCheckGateName = "upgrade-check-gate" 485 upgradeCheckFlagName = "upgrade-check-flag" 486 487 migrationFortressName = "migration-fortress" 488 migrationInactiveFlagName = "migration-inactive-flag" 489 migrationMinionName = "migration-minion" 490 491 servingInfoSetterName = "serving-info-setter" 492 apiWorkersName = "unconverted-api-workers" 493 rebootName = "reboot-executor" 494 loggingConfigUpdaterName = "logging-config-updater" 495 diskManagerName = "disk-manager" 496 proxyConfigUpdater = "proxy-config-updater" 497 apiAddressUpdaterName = "api-address-updater" 498 machinerName = "machiner" 499 logSenderName = "log-sender" 500 deployerName = "unit-agent-deployer" 501 authenticationWorkerName = "ssh-authkeys-updater" 502 storageProvisionerName = "storage-provisioner" 503 resumerName = "mgo-txn-resumer" 504 identityFileWriterName = "ssh-identity-writer" 505 toolsVersionCheckerName = "tools-version-checker" 506 machineActionName = "machine-action-runner" 507 hostKeyReporterName = "host-key-reporter" 508 logForwarderName = "log-forwarder" 509 )