github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/utils/clock" 10 "github.com/juju/utils/voyeur" 11 12 coreagent "github.com/juju/juju/agent" 13 "github.com/juju/juju/cmd/jujud/agent/util" 14 "github.com/juju/juju/core/life" 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/worker" 17 "github.com/juju/juju/worker/addresser" 18 "github.com/juju/juju/worker/agent" 19 "github.com/juju/juju/worker/apicaller" 20 "github.com/juju/juju/worker/apiconfigwatcher" 21 "github.com/juju/juju/worker/charmrevision" 22 "github.com/juju/juju/worker/charmrevision/charmrevisionmanifold" 23 "github.com/juju/juju/worker/cleaner" 24 "github.com/juju/juju/worker/dependency" 25 "github.com/juju/juju/worker/discoverspaces" 26 "github.com/juju/juju/worker/environ" 27 "github.com/juju/juju/worker/firewaller" 28 "github.com/juju/juju/worker/fortress" 29 "github.com/juju/juju/worker/gate" 30 "github.com/juju/juju/worker/instancepoller" 31 "github.com/juju/juju/worker/lifeflag" 32 "github.com/juju/juju/worker/metricworker" 33 "github.com/juju/juju/worker/migrationmaster" 34 "github.com/juju/juju/worker/provisioner" 35 "github.com/juju/juju/worker/servicescaler" 36 "github.com/juju/juju/worker/singular" 37 "github.com/juju/juju/worker/statushistorypruner" 38 "github.com/juju/juju/worker/storageprovisioner" 39 "github.com/juju/juju/worker/undertaker" 40 "github.com/juju/juju/worker/unitassigner" 41 ) 42 43 // ManifoldsConfig holds the dependencies and configuration options for a 44 // model agent: that is, for the set of interdependent workers that observe 45 // and manipulate a single model. 46 type ManifoldsConfig struct { 47 48 // Agent identifies, and exposes configuration for, the controller 49 // machine running these manifolds and the model the manifolds 50 // should administer. 51 // 52 // You should almost certainly set this value to one created by 53 // model.WrapAgent. 54 Agent coreagent.Agent 55 56 // AgentConfigChanged will be set whenever the agent's api config 57 // is updated 58 AgentConfigChanged *voyeur.Value 59 60 // Clock supplies timing services to any manifolds that need them. 61 // Only a few workers have been converted to use them fo far. 62 Clock clock.Clock 63 64 // RunFlagDuration defines for how long this controller will ask 65 // for model administration rights; most of the workers controlled 66 // by this agent will only be started when the run flag is known 67 // to be held. 68 RunFlagDuration time.Duration 69 70 // CharmRevisionUpdateInterval determines how often the charm- 71 // revision worker will check for new revisions of known charms. 72 CharmRevisionUpdateInterval time.Duration 73 74 // EntityStatusHistory* values control status-history pruning 75 // behaviour per entity. 76 EntityStatusHistoryCount uint 77 EntityStatusHistoryInterval time.Duration 78 79 // SpacesImportedGate will be unlocked when spaces are known to 80 // have been imported. 81 SpacesImportedGate gate.Lock 82 } 83 84 // Manifolds returns a set of interdependent dependency manifolds that will 85 // run together to administer a model, as configured. 86 func Manifolds(config ManifoldsConfig) dependency.Manifolds { 87 modelTag := config.Agent.CurrentConfig().Model() 88 return dependency.Manifolds{ 89 90 // The first group are foundational; the agent and clock 91 // which wrap those supplied in config, and the api-caller 92 // through which everything else communicates with the 93 // controller. 94 agentName: agent.Manifold(config.Agent), 95 clockName: clockManifold(config.Clock), 96 apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{ 97 AgentName: agentName, 98 AgentConfigChanged: config.AgentConfigChanged, 99 }), 100 apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ 101 AgentName: agentName, 102 APIOpen: apicaller.APIOpen, 103 NewConnection: apicaller.OnlyConnect, 104 }), 105 106 // The spaces-imported gate will be unlocked when space 107 // discovery is known to be complete. Various manifolds 108 // should also come to depend upon it (or rather, on a 109 // Flag depending on it) in the future. 110 spacesImportedGateName: gate.ManifoldEx(config.SpacesImportedGate), 111 112 // All other manifolds should depend on at least one of these 113 // three, which handle all the tasks that are safe and sane 114 // to run in *all* controller machines. 115 notDeadFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{ 116 APICallerName: apiCallerName, 117 Entity: modelTag, 118 Result: life.IsNotDead, 119 Filter: lifeFilter, 120 121 NewFacade: lifeflag.NewFacade, 122 NewWorker: lifeflag.NewWorker, 123 }), 124 notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{ 125 APICallerName: apiCallerName, 126 Entity: modelTag, 127 Result: life.IsNotAlive, 128 Filter: lifeFilter, 129 130 NewFacade: lifeflag.NewFacade, 131 NewWorker: lifeflag.NewWorker, 132 }), 133 isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{ 134 ClockName: clockName, 135 AgentName: agentName, 136 APICallerName: apiCallerName, 137 Duration: config.RunFlagDuration, 138 139 NewFacade: singular.NewFacade, 140 NewWorker: singular.NewWorker, 141 }), 142 143 migrationFortressName: ifNotDead(fortress.Manifold()), 144 migrationMasterName: ifNotDead(migrationmaster.Manifold(migrationmaster.ManifoldConfig{ 145 APICallerName: apiCallerName, 146 FortressName: migrationFortressName, 147 148 NewFacade: migrationmaster.NewFacade, 149 NewWorker: migrationmaster.NewWorker, 150 })), 151 152 // Everything else should be wrapped in ifResponsible, 153 // ifNotAlive, or ifNotDead, to ensure that only a single 154 // controller is administering this model at a time. 155 // 156 // NOTE: not perfectly reliable at this stage? i.e. a worker 157 // that ignores its stop signal for "too long" might continue 158 // to take admin actions after the window of responsibility 159 // closes. This *is* a pre-existing problem, but demands some 160 // thought/care: e.g. should we make sure the apiserver also 161 // closes any connections that lose responsibility..? can we 162 // make sure all possible environ operations are either time- 163 // bounded or interruptible? etc 164 // 165 // On the other hand, all workers *should* be written in the 166 // expectation of dealing with a sucky infrastructure running 167 // things in parallel unexpectedly, just because the universe 168 // hates us and will engineer matters such that it happens 169 // sometimes, even when we try to avoid it. 170 171 // The environ tracker could/should be used by several other 172 // workers (firewaller, provisioners, address-cleaner?). 173 environTrackerName: ifResponsible(environ.Manifold(environ.ManifoldConfig{ 174 APICallerName: apiCallerName, 175 NewEnvironFunc: environs.New, 176 })), 177 178 // The undertaker is currently the only ifNotAlive worker. 179 undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{ 180 APICallerName: apiCallerName, 181 EnvironName: environTrackerName, 182 183 NewFacade: undertaker.NewFacade, 184 NewWorker: undertaker.NewWorker, 185 })), 186 187 // All the rest depend on ifNotDead. 188 spaceImporterName: ifNotDead(discoverspaces.Manifold(discoverspaces.ManifoldConfig{ 189 EnvironName: environTrackerName, 190 APICallerName: apiCallerName, 191 UnlockerName: spacesImportedGateName, 192 193 NewFacade: discoverspaces.NewFacade, 194 NewWorker: discoverspaces.NewWorker, 195 })), 196 197 computeProvisionerName: ifNotDead(provisioner.Manifold(provisioner.ManifoldConfig{ 198 AgentName: agentName, 199 APICallerName: apiCallerName, 200 })), 201 storageProvisionerName: ifNotDead(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{ 202 APICallerName: apiCallerName, 203 ClockName: clockName, 204 Scope: modelTag, 205 })), 206 firewallerName: ifNotDead(firewaller.Manifold(firewaller.ManifoldConfig{ 207 APICallerName: apiCallerName, 208 })), 209 unitAssignerName: ifNotDead(unitassigner.Manifold(unitassigner.ManifoldConfig{ 210 APICallerName: apiCallerName, 211 })), 212 serviceScalerName: ifNotDead(servicescaler.Manifold(servicescaler.ManifoldConfig{ 213 APICallerName: apiCallerName, 214 NewFacade: servicescaler.NewFacade, 215 NewWorker: servicescaler.New, 216 })), 217 instancePollerName: ifNotDead(instancepoller.Manifold(instancepoller.ManifoldConfig{ 218 APICallerName: apiCallerName, 219 EnvironName: environTrackerName, 220 })), 221 charmRevisionUpdaterName: ifNotDead(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{ 222 APICallerName: apiCallerName, 223 ClockName: clockName, 224 Period: config.CharmRevisionUpdateInterval, 225 226 NewFacade: charmrevisionmanifold.NewAPIFacade, 227 NewWorker: charmrevision.NewWorker, 228 })), 229 metricWorkerName: ifNotDead(metricworker.Manifold(metricworker.ManifoldConfig{ 230 APICallerName: apiCallerName, 231 })), 232 stateCleanerName: ifNotDead(cleaner.Manifold(cleaner.ManifoldConfig{ 233 APICallerName: apiCallerName, 234 })), 235 addressCleanerName: ifNotDead(addresser.Manifold(addresser.ManifoldConfig{ 236 APICallerName: apiCallerName, 237 })), 238 statusHistoryPrunerName: ifNotDead(statushistorypruner.Manifold(statushistorypruner.ManifoldConfig{ 239 APICallerName: apiCallerName, 240 MaxLogsPerEntity: config.EntityStatusHistoryCount, 241 PruneInterval: config.EntityStatusHistoryInterval, 242 // TODO(fwereade): 2016-03-17 lp:1558657 243 NewTimer: worker.NewTimer, 244 })), 245 } 246 } 247 248 // clockManifold expresses a Clock as a ValueWorker manifold. 249 func clockManifold(clock clock.Clock) dependency.Manifold { 250 return dependency.Manifold{ 251 Start: func(_ dependency.Context) (worker.Worker, error) { 252 return util.NewValueWorker(clock) 253 }, 254 Output: util.ValueWorkerOutput, 255 } 256 } 257 258 var ( 259 // ifResponsible wraps a manifold such that it only runs if the 260 // responsibility flag is set. 261 ifResponsible = util.Housing{ 262 Flags: []string{ 263 isResponsibleFlagName, 264 }, 265 }.Decorate 266 267 // ifNotAlive wraps a manifold such that it only runs if the 268 // responsibility flag is set and the model is Dying or Dead. 269 ifNotAlive = util.Housing{ 270 Flags: []string{ 271 isResponsibleFlagName, 272 notAliveFlagName, 273 }, 274 }.Decorate 275 276 // ifNotDead wraps a manifold such that it only runs if the 277 // responsibility flag is set and the model is Alive or Dying. 278 ifNotDead = util.Housing{ 279 Flags: []string{ 280 isResponsibleFlagName, 281 notDeadFlagName, 282 }, 283 }.Decorate 284 ) 285 286 const ( 287 agentName = "agent" 288 clockName = "clock" 289 apiConfigWatcherName = "api-config-watcher" 290 apiCallerName = "api-caller" 291 292 spacesImportedGateName = "spaces-imported-gate" 293 isResponsibleFlagName = "is-responsible-flag" 294 notDeadFlagName = "not-dead-flag" 295 notAliveFlagName = "not-alive-flag" 296 297 migrationFortressName = "migration-fortress" 298 migrationMasterName = "migration-master" 299 300 environTrackerName = "environ-tracker" 301 undertakerName = "undertaker" 302 spaceImporterName = "space-importer" 303 computeProvisionerName = "compute-provisioner" 304 storageProvisionerName = "storage-provisioner" 305 firewallerName = "firewaller" 306 unitAssignerName = "unit-assigner" 307 serviceScalerName = "service-scaler" 308 instancePollerName = "instance-poller" 309 charmRevisionUpdaterName = "charm-revision-updater" 310 metricWorkerName = "metric-worker" 311 stateCleanerName = "state-cleaner" 312 addressCleanerName = "address-cleaner" 313 statusHistoryPrunerName = "status-history-pruner" 314 )