github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/model/manifolds.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package model 5 6 import ( 7 "time" 8 9 "github.com/juju/juju/api" 10 "github.com/juju/utils/clock" 11 "github.com/juju/utils/voyeur" 12 13 coreagent "github.com/juju/juju/agent" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/cmd/jujud/agent/engine" 16 "github.com/juju/juju/core/life" 17 "github.com/juju/juju/environs" 18 "github.com/juju/juju/worker" 19 "github.com/juju/juju/worker/agent" 20 "github.com/juju/juju/worker/apicaller" 21 "github.com/juju/juju/worker/apiconfigwatcher" 22 "github.com/juju/juju/worker/applicationscaler" 23 "github.com/juju/juju/worker/charmrevision" 24 "github.com/juju/juju/worker/charmrevision/charmrevisionmanifold" 25 "github.com/juju/juju/worker/cleaner" 26 "github.com/juju/juju/worker/dependency" 27 "github.com/juju/juju/worker/discoverspaces" 28 "github.com/juju/juju/worker/environ" 29 "github.com/juju/juju/worker/firewaller" 30 "github.com/juju/juju/worker/fortress" 31 "github.com/juju/juju/worker/gate" 32 "github.com/juju/juju/worker/instancepoller" 33 "github.com/juju/juju/worker/lifeflag" 34 "github.com/juju/juju/worker/machineundertaker" 35 "github.com/juju/juju/worker/metricworker" 36 "github.com/juju/juju/worker/migrationflag" 37 "github.com/juju/juju/worker/migrationmaster" 38 "github.com/juju/juju/worker/provisioner" 39 "github.com/juju/juju/worker/singular" 40 "github.com/juju/juju/worker/statushistorypruner" 41 "github.com/juju/juju/worker/storageprovisioner" 42 "github.com/juju/juju/worker/undertaker" 43 "github.com/juju/juju/worker/unitassigner" 44 ) 45 46 // ManifoldsConfig holds the dependencies and configuration options for a 47 // model agent: that is, for the set of interdependent workers that observe 48 // and manipulate a single model. 49 type ManifoldsConfig struct { 50 51 // Agent identifies, and exposes configuration for, the controller 52 // machine running these manifolds and the model the manifolds 53 // should administer. 54 // 55 // You should almost certainly set this value to one created by 56 // model.WrapAgent. 57 Agent coreagent.Agent 58 59 // AgentConfigChanged will be set whenever the agent's api config 60 // is updated 61 AgentConfigChanged *voyeur.Value 62 63 // Clock supplies timing services to any manifolds that need them. 64 // Only a few workers have been converted to use them fo far. 65 Clock clock.Clock 66 67 // InstPollerAggregationDelay is the delay before sending a batch of 68 // requests in the instancpoller.Worker's aggregate loop. 69 InstPollerAggregationDelay time.Duration 70 71 // RunFlagDuration defines for how long this controller will ask 72 // for model administration rights; most of the workers controlled 73 // by this agent will only be started when the run flag is known 74 // to be held. 75 RunFlagDuration time.Duration 76 77 // CharmRevisionUpdateInterval determines how often the charm- 78 // revision worker will check for new revisions of known charms. 79 CharmRevisionUpdateInterval time.Duration 80 81 // StatusHistoryPruner* values control status-history pruning 82 // behaviour. 83 StatusHistoryPrunerMaxHistoryTime time.Duration 84 StatusHistoryPrunerMaxHistoryMB uint 85 StatusHistoryPrunerInterval time.Duration 86 87 // SpacesImportedGate will be unlocked when spaces are known to 88 // have been imported. 89 SpacesImportedGate gate.Lock 90 91 // NewEnvironFunc is a function opens a provider "environment" 92 // (typically environs.New). 93 NewEnvironFunc environs.NewEnvironFunc 94 95 // NewMigrationMaster is called to create a new migrationmaster 96 // worker. 97 NewMigrationMaster func(migrationmaster.Config) (worker.Worker, error) 98 } 99 100 // Manifolds returns a set of interdependent dependency manifolds that will 101 // run together to administer a model, as configured. 102 func Manifolds(config ManifoldsConfig) dependency.Manifolds { 103 modelTag := config.Agent.CurrentConfig().Model() 104 return dependency.Manifolds{ 105 106 // The first group are foundational; the agent and clock 107 // which wrap those supplied in config, and the api-caller 108 // through which everything else communicates with the 109 // controller. 110 agentName: agent.Manifold(config.Agent), 111 clockName: clockManifold(config.Clock), 112 apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{ 113 AgentName: agentName, 114 AgentConfigChanged: config.AgentConfigChanged, 115 }), 116 apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ 117 AgentName: agentName, 118 APIOpen: api.Open, 119 NewConnection: apicaller.OnlyConnect, 120 Filter: apiConnectFilter, 121 }), 122 123 // The spaces-imported gate will be unlocked when space 124 // discovery is known to be complete. Various manifolds 125 // should also come to depend upon it (or rather, on a 126 // Flag depending on it) in the future. 127 spacesImportedGateName: gate.ManifoldEx(config.SpacesImportedGate), 128 129 // All other manifolds should depend on at least one of these 130 // three, which handle all the tasks that are safe and sane 131 // to run in *all* controller machines. 132 notDeadFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{ 133 APICallerName: apiCallerName, 134 Entity: modelTag, 135 Result: life.IsNotDead, 136 Filter: LifeFilter, 137 138 NewFacade: lifeflag.NewFacade, 139 NewWorker: lifeflag.NewWorker, 140 }), 141 notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{ 142 APICallerName: apiCallerName, 143 Entity: modelTag, 144 Result: life.IsNotAlive, 145 Filter: LifeFilter, 146 147 NewFacade: lifeflag.NewFacade, 148 NewWorker: lifeflag.NewWorker, 149 }), 150 isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{ 151 ClockName: clockName, 152 AgentName: agentName, 153 APICallerName: apiCallerName, 154 Duration: config.RunFlagDuration, 155 156 NewFacade: singular.NewFacade, 157 NewWorker: singular.NewWorker, 158 }), 159 160 // The migration workers collaborate to run migrations; 161 // and to create a mechanism for running other workers 162 // so they can't accidentally interfere with a migration 163 // in progress. Such a manifold should (1) depend on the 164 // migration-inactive flag, to know when to start or die; 165 // and (2) occupy the migration-fortress, so as to avoid 166 // possible interference with the minion (which will not 167 // take action until it's gained sole control of the 168 // fortress). 169 // 170 // Note that the fortress and flag will only exist while 171 // the model is not dead; this frees their dependencies 172 // from model-lifetime concerns. 173 migrationFortressName: ifNotDead(fortress.Manifold()), 174 migrationInactiveFlagName: ifNotDead(migrationflag.Manifold(migrationflag.ManifoldConfig{ 175 APICallerName: apiCallerName, 176 Check: migrationflag.IsTerminal, 177 NewFacade: migrationflag.NewFacade, 178 NewWorker: migrationflag.NewWorker, 179 })), 180 migrationMasterName: ifNotDead(migrationmaster.Manifold(migrationmaster.ManifoldConfig{ 181 AgentName: agentName, 182 APICallerName: apiCallerName, 183 FortressName: migrationFortressName, 184 Clock: config.Clock, 185 NewFacade: migrationmaster.NewFacade, 186 NewWorker: config.NewMigrationMaster, 187 })), 188 189 // Everything else should be wrapped in ifResponsible, 190 // ifNotAlive, ifNotDead, or ifNotMigrating (which also 191 // implies NotDead), to ensure that only a single 192 // controller is attempting to administer this model at 193 // any one time. 194 // 195 // NOTE: not perfectly reliable at this stage? i.e. a 196 // worker that ignores its stop signal for "too long" 197 // might continue to take admin actions after the window 198 // of responsibility closes. This *is* a pre-existing 199 // problem, but demands some thought/care: e.g. should 200 // we make sure the apiserver also closes any 201 // connections that lose responsibility..? can we make 202 // sure all possible environ operations are either time- 203 // bounded or interruptible? etc 204 // 205 // On the other hand, all workers *should* be written in 206 // the expectation of dealing with sucky infrastructure 207 // running things in parallel unexpectedly, just because 208 // the universe hates us and will engineer matters such 209 // that it happens sometimes, even when we try to avoid 210 // it. 211 212 // The environ tracker could/should be used by several other 213 // workers (firewaller, provisioners, address-cleaner?). 214 environTrackerName: ifResponsible(environ.Manifold(environ.ManifoldConfig{ 215 APICallerName: apiCallerName, 216 NewEnvironFunc: config.NewEnvironFunc, 217 })), 218 219 // The undertaker is currently the only ifNotAlive worker. 220 undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{ 221 APICallerName: apiCallerName, 222 EnvironName: environTrackerName, 223 224 NewFacade: undertaker.NewFacade, 225 NewWorker: undertaker.NewWorker, 226 })), 227 228 // All the rest depend on ifNotMigrating. 229 spaceImporterName: ifNotMigrating(discoverspaces.Manifold(discoverspaces.ManifoldConfig{ 230 EnvironName: environTrackerName, 231 APICallerName: apiCallerName, 232 UnlockerName: spacesImportedGateName, 233 234 NewFacade: discoverspaces.NewFacade, 235 NewWorker: discoverspaces.NewWorker, 236 })), 237 computeProvisionerName: ifNotMigrating(provisioner.Manifold(provisioner.ManifoldConfig{ 238 AgentName: agentName, 239 APICallerName: apiCallerName, 240 EnvironName: environTrackerName, 241 NewProvisionerFunc: provisioner.NewEnvironProvisioner, 242 })), 243 storageProvisionerName: ifNotMigrating(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{ 244 APICallerName: apiCallerName, 245 ClockName: clockName, 246 EnvironName: environTrackerName, 247 Scope: modelTag, 248 })), 249 firewallerName: ifNotMigrating(firewaller.Manifold(firewaller.ManifoldConfig{ 250 APICallerName: apiCallerName, 251 })), 252 unitAssignerName: ifNotMigrating(unitassigner.Manifold(unitassigner.ManifoldConfig{ 253 APICallerName: apiCallerName, 254 })), 255 applicationScalerName: ifNotMigrating(applicationscaler.Manifold(applicationscaler.ManifoldConfig{ 256 APICallerName: apiCallerName, 257 NewFacade: applicationscaler.NewFacade, 258 NewWorker: applicationscaler.New, 259 })), 260 instancePollerName: ifNotMigrating(instancepoller.Manifold(instancepoller.ManifoldConfig{ 261 APICallerName: apiCallerName, 262 EnvironName: environTrackerName, 263 ClockName: clockName, 264 Delay: config.InstPollerAggregationDelay, 265 })), 266 charmRevisionUpdaterName: ifNotMigrating(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{ 267 APICallerName: apiCallerName, 268 ClockName: clockName, 269 Period: config.CharmRevisionUpdateInterval, 270 271 NewFacade: charmrevisionmanifold.NewAPIFacade, 272 NewWorker: charmrevision.NewWorker, 273 })), 274 metricWorkerName: ifNotMigrating(metricworker.Manifold(metricworker.ManifoldConfig{ 275 APICallerName: apiCallerName, 276 })), 277 stateCleanerName: ifNotMigrating(cleaner.Manifold(cleaner.ManifoldConfig{ 278 APICallerName: apiCallerName, 279 })), 280 statusHistoryPrunerName: ifNotMigrating(statushistorypruner.Manifold(statushistorypruner.ManifoldConfig{ 281 APICallerName: apiCallerName, 282 MaxHistoryTime: config.StatusHistoryPrunerMaxHistoryTime, 283 MaxHistoryMB: config.StatusHistoryPrunerMaxHistoryMB, 284 PruneInterval: config.StatusHistoryPrunerInterval, 285 // TODO(fwereade): 2016-03-17 lp:1558657 286 NewTimer: worker.NewTimer, 287 })), 288 machineUndertakerName: ifNotMigrating(machineundertaker.Manifold(machineundertaker.ManifoldConfig{ 289 APICallerName: apiCallerName, 290 EnvironName: environTrackerName, 291 NewWorker: machineundertaker.NewWorker, 292 })), 293 } 294 } 295 296 // clockManifold expresses a Clock as a ValueWorker manifold. 297 func clockManifold(clock clock.Clock) dependency.Manifold { 298 return dependency.Manifold{ 299 Start: func(_ dependency.Context) (worker.Worker, error) { 300 return engine.NewValueWorker(clock) 301 }, 302 Output: engine.ValueWorkerOutput, 303 } 304 } 305 306 func apiConnectFilter(err error) error { 307 // If the model is no longer there, then convert to ErrRemoved so 308 // that the dependency engine for the model is stopped. 309 // See http://pad.lv/1614809 310 if params.IsCodeModelNotFound(err) { 311 return ErrRemoved 312 } 313 return err 314 } 315 316 var ( 317 // ifResponsible wraps a manifold such that it only runs if the 318 // responsibility flag is set. 319 ifResponsible = engine.Housing{ 320 Flags: []string{ 321 isResponsibleFlagName, 322 }, 323 }.Decorate 324 325 // ifNotAlive wraps a manifold such that it only runs if the 326 // responsibility flag is set and the model is Dying or Dead. 327 ifNotAlive = engine.Housing{ 328 Flags: []string{ 329 isResponsibleFlagName, 330 notAliveFlagName, 331 }, 332 }.Decorate 333 334 // ifNotDead wraps a manifold such that it only runs if the 335 // responsibility flag is set and the model is Alive or Dying. 336 ifNotDead = engine.Housing{ 337 Flags: []string{ 338 isResponsibleFlagName, 339 notDeadFlagName, 340 }, 341 }.Decorate 342 343 // ifNotMigrating wraps a manifold such that it only runs if the 344 // migration-inactive flag is set; and then runs workers only 345 // within Visits to the migration fortress. To avoid redundancy, 346 // it takes advantage of the fact that those migration manifolds 347 // themselves depend on ifNotDead, and eschews repeating those 348 // dependencies. 349 ifNotMigrating = engine.Housing{ 350 Flags: []string{ 351 migrationInactiveFlagName, 352 }, 353 Occupy: migrationFortressName, 354 }.Decorate 355 ) 356 357 const ( 358 agentName = "agent" 359 clockName = "clock" 360 apiConfigWatcherName = "api-config-watcher" 361 apiCallerName = "api-caller" 362 363 spacesImportedGateName = "spaces-imported-gate" 364 isResponsibleFlagName = "is-responsible-flag" 365 notDeadFlagName = "not-dead-flag" 366 notAliveFlagName = "not-alive-flag" 367 368 migrationFortressName = "migration-fortress" 369 migrationInactiveFlagName = "migration-inactive-flag" 370 migrationMasterName = "migration-master" 371 372 environTrackerName = "environ-tracker" 373 undertakerName = "undertaker" 374 spaceImporterName = "space-importer" 375 computeProvisionerName = "compute-provisioner" 376 storageProvisionerName = "storage-provisioner" 377 firewallerName = "firewaller" 378 unitAssignerName = "unit-assigner" 379 applicationScalerName = "application-scaler" 380 instancePollerName = "instance-poller" 381 charmRevisionUpdaterName = "charm-revision-updater" 382 metricWorkerName = "metric-worker" 383 stateCleanerName = "state-cleaner" 384 statusHistoryPrunerName = "status-history-pruner" 385 machineUndertakerName = "machine-undertaker" 386 )