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