github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/errors" 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/api" 15 "github.com/juju/juju/api/base" 16 msapi "github.com/juju/juju/api/meterstatus" 17 "github.com/juju/juju/cmd/jujud/agent/engine" 18 "github.com/juju/juju/worker" 19 "github.com/juju/juju/worker/agent" 20 "github.com/juju/juju/worker/apiaddressupdater" 21 "github.com/juju/juju/worker/apicaller" 22 "github.com/juju/juju/worker/apiconfigwatcher" 23 "github.com/juju/juju/worker/dependency" 24 "github.com/juju/juju/worker/fortress" 25 "github.com/juju/juju/worker/leadership" 26 "github.com/juju/juju/worker/logger" 27 "github.com/juju/juju/worker/logsender" 28 "github.com/juju/juju/worker/meterstatus" 29 "github.com/juju/juju/worker/metrics/collect" 30 "github.com/juju/juju/worker/metrics/sender" 31 "github.com/juju/juju/worker/metrics/spool" 32 "github.com/juju/juju/worker/migrationflag" 33 "github.com/juju/juju/worker/migrationminion" 34 "github.com/juju/juju/worker/proxyupdater" 35 "github.com/juju/juju/worker/retrystrategy" 36 "github.com/juju/juju/worker/uniter" 37 "github.com/juju/juju/worker/upgrader" 38 ) 39 40 // ManifoldsConfig allows specialisation of the result of Manifolds. 41 type ManifoldsConfig struct { 42 43 // Agent contains the agent that will be wrapped and made available to 44 // its dependencies via a dependency.Engine. 45 Agent coreagent.Agent 46 47 // LogSource will be read from by the logsender component. 48 LogSource logsender.LogRecordCh 49 50 // LeadershipGuarantee controls the behaviour of the leadership tracker. 51 LeadershipGuarantee time.Duration 52 53 // AgentConfigChanged is set whenever the unit agent's config 54 // is updated. 55 AgentConfigChanged *voyeur.Value 56 57 // ValidateMigration is called by the migrationminion during the 58 // migration process to check that the agent will be ok when 59 // connected to the new target controller. 60 ValidateMigration func(base.APICaller) error 61 } 62 63 // Manifolds returns a set of co-configured manifolds covering the various 64 // responsibilities of a standalone unit agent. It also accepts the logSource 65 // argument because we haven't figured out how to thread all the logging bits 66 // through a dependency engine yet. 67 // 68 // Thou Shalt Not Use String Literals In This Function. Or Else. 69 func Manifolds(config ManifoldsConfig) dependency.Manifolds { 70 71 // connectFilter exists to let us retry api connections immediately 72 // on password change, rather than causing the dependency engine to 73 // wait for a while. 74 connectFilter := func(err error) error { 75 cause := errors.Cause(err) 76 if cause == apicaller.ErrChangedPassword { 77 return dependency.ErrBounce 78 } else if cause == apicaller.ErrConnectImpossible { 79 return worker.ErrTerminateAgent 80 } 81 return err 82 } 83 84 return dependency.Manifolds{ 85 86 // The agent manifold references the enclosing agent, and is the 87 // foundation stone on which most other manifolds ultimately depend. 88 // (Currently, that is "all manifolds", but consider a shared clock.) 89 agentName: agent.Manifold(config.Agent), 90 91 // The api-config-watcher manifold monitors the API server 92 // addresses in the agent config and bounces when they 93 // change. It's required as part of model migrations. 94 apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{ 95 AgentName: agentName, 96 AgentConfigChanged: config.AgentConfigChanged, 97 }), 98 99 // The api caller is a thin concurrent wrapper around a connection 100 // to some API server. It's used by many other manifolds, which all 101 // select their own desired facades. It will be interesting to see 102 // how this works when we consolidate the agents; might be best to 103 // handle the auth changes server-side..? 104 apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ 105 AgentName: agentName, 106 APIConfigWatcherName: apiConfigWatcherName, 107 APIOpen: api.Open, 108 NewConnection: apicaller.ScaryConnect, 109 Filter: connectFilter, 110 }), 111 112 // The log sender is a leaf worker that sends log messages to some 113 // API server, when configured so to do. We should only need one of 114 // these in a consolidated agent. 115 logSenderName: logsender.Manifold(logsender.ManifoldConfig{ 116 APICallerName: apiCallerName, 117 LogSource: config.LogSource, 118 }), 119 120 // The upgrader is a leaf worker that returns a specific error type 121 // recognised by the unit agent, causing other workers to be stopped 122 // and the agent to be restarted running the new tools. We should only 123 // need one of these in a consolidated agent, but we'll need to be 124 // careful about behavioural differences, and interactions with the 125 // upgradesteps worker. 126 upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{ 127 AgentName: agentName, 128 APICallerName: apiCallerName, 129 }), 130 131 // The migration workers collaborate to run migrations; 132 // and to create a mechanism for running other workers 133 // so they can't accidentally interfere with a migration 134 // in progress. Such a manifold should (1) depend on the 135 // migration-inactive flag, to know when to start or die; 136 // and (2) occupy the migration-fortress, so as to avoid 137 // possible interference with the minion (which will not 138 // take action until it's gained sole control of the 139 // fortress). 140 migrationFortressName: fortress.Manifold(), 141 migrationInactiveFlagName: migrationflag.Manifold(migrationflag.ManifoldConfig{ 142 APICallerName: apiCallerName, 143 Check: migrationflag.IsTerminal, 144 NewFacade: migrationflag.NewFacade, 145 NewWorker: migrationflag.NewWorker, 146 }), 147 migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{ 148 AgentName: agentName, 149 APICallerName: apiCallerName, 150 FortressName: migrationFortressName, 151 APIOpen: api.Open, 152 ValidateMigration: config.ValidateMigration, 153 NewFacade: migrationminion.NewFacade, 154 NewWorker: migrationminion.NewWorker, 155 }), 156 157 // The logging config updater is a leaf worker that indirectly 158 // controls the messages sent via the log sender according to 159 // changes in environment config. We should only need one of 160 // these in a consolidated agent. 161 loggingConfigUpdaterName: ifNotMigrating(logger.Manifold(logger.ManifoldConfig{ 162 AgentName: agentName, 163 APICallerName: apiCallerName, 164 })), 165 166 // The api address updater is a leaf worker that rewrites agent config 167 // as the controller addresses change. We should only need one of 168 // these in a consolidated agent. 169 apiAddressUpdaterName: ifNotMigrating(apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{ 170 AgentName: agentName, 171 APICallerName: apiCallerName, 172 })), 173 174 // The proxy config updater is a leaf worker that sets http/https/apt/etc 175 // proxy settings. 176 // TODO(fwereade): timing of this is suspicious. There was superstitious 177 // code trying to run this early; if that ever helped, it was only by 178 // coincidence. Probably we ought to be making components that might 179 // need proxy config into explicit dependencies of the proxy updater... 180 proxyConfigUpdaterName: ifNotMigrating(proxyupdater.Manifold(proxyupdater.ManifoldConfig{ 181 AgentName: agentName, 182 APICallerName: apiCallerName, 183 WorkerFunc: proxyupdater.NewWorker, 184 })), 185 186 // The charmdir resource coordinates whether the charm directory is 187 // available or not; after 'start' hook and before 'stop' hook 188 // executes, and not during upgrades. 189 charmDirName: ifNotMigrating(fortress.Manifold()), 190 191 // The leadership tracker attempts to secure and retain leadership of 192 // the unit's service, and is consulted on such matters by the 193 // uniter. As it stannds today, we'll need one per unit in a 194 // consolidated agent. 195 leadershipTrackerName: ifNotMigrating(leadership.Manifold(leadership.ManifoldConfig{ 196 AgentName: agentName, 197 APICallerName: apiCallerName, 198 Clock: clock.WallClock, 199 LeadershipGuarantee: config.LeadershipGuarantee, 200 })), 201 202 // HookRetryStrategy uses a retrystrategy worker to get a 203 // retry strategy that will be used by the uniter to run its hooks. 204 hookRetryStrategyName: ifNotMigrating(retrystrategy.Manifold(retrystrategy.ManifoldConfig{ 205 AgentName: agentName, 206 APICallerName: apiCallerName, 207 NewFacade: retrystrategy.NewFacade, 208 NewWorker: retrystrategy.NewRetryStrategyWorker, 209 })), 210 211 // The uniter installs charms; manages the unit's presence in its 212 // relations; creates suboordinate units; runs all the hooks; sends 213 // metrics; etc etc etc. We expect to break it up further in the 214 // coming weeks, and to need one per unit in a consolidated agent 215 // (and probably one for each component broken out). 216 uniterName: ifNotMigrating(uniter.Manifold(uniter.ManifoldConfig{ 217 AgentName: agentName, 218 APICallerName: apiCallerName, 219 MachineLockName: coreagent.MachineLockName, 220 Clock: clock.WallClock, 221 LeadershipTrackerName: leadershipTrackerName, 222 CharmDirName: charmDirName, 223 HookRetryStrategyName: hookRetryStrategyName, 224 })), 225 226 // TODO (mattyw) should be added to machine agent. 227 metricSpoolName: ifNotMigrating(spool.Manifold(spool.ManifoldConfig{ 228 AgentName: agentName, 229 })), 230 231 // The metric collect worker executes the collect-metrics hook in a 232 // restricted context that can safely run concurrently with other hooks. 233 metricCollectName: ifNotMigrating(collect.Manifold(collect.ManifoldConfig{ 234 AgentName: agentName, 235 MetricSpoolName: metricSpoolName, 236 CharmDirName: charmDirName, 237 })), 238 239 // The meter status worker executes the meter-status-changed hook when it detects 240 // that the meter status has changed. 241 meterStatusName: ifNotMigrating(meterstatus.Manifold(meterstatus.ManifoldConfig{ 242 AgentName: agentName, 243 APICallerName: apiCallerName, 244 MachineLockName: coreagent.MachineLockName, 245 Clock: clock.WallClock, 246 NewHookRunner: meterstatus.NewHookRunner, 247 NewMeterStatusAPIClient: msapi.NewClient, 248 NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker, 249 NewIsolatedStatusWorker: meterstatus.NewIsolatedStatusWorker, 250 })), 251 252 // The metric sender worker periodically sends accumulated metrics to the controller. 253 metricSenderName: ifNotMigrating(sender.Manifold(sender.ManifoldConfig{ 254 AgentName: agentName, 255 APICallerName: apiCallerName, 256 MetricSpoolName: metricSpoolName, 257 })), 258 } 259 } 260 261 var ifNotMigrating = engine.Housing{ 262 Flags: []string{ 263 migrationInactiveFlagName, 264 }, 265 Occupy: migrationFortressName, 266 }.Decorate 267 268 const ( 269 agentName = "agent" 270 apiConfigWatcherName = "api-config-watcher" 271 apiCallerName = "api-caller" 272 logSenderName = "log-sender" 273 upgraderName = "upgrader" 274 275 migrationFortressName = "migration-fortress" 276 migrationInactiveFlagName = "migration-inactive-flag" 277 migrationMinionName = "migration-minion" 278 279 loggingConfigUpdaterName = "logging-config-updater" 280 proxyConfigUpdaterName = "proxy-config-updater" 281 apiAddressUpdaterName = "api-address-updater" 282 283 charmDirName = "charm-dir" 284 leadershipTrackerName = "leadership-tracker" 285 hookRetryStrategyName = "hook-retry-strategy" 286 uniterName = "uniter" 287 288 metricSpoolName = "metric-spool" 289 meterStatusName = "meter-status" 290 metricCollectName = "metric-collect" 291 metricSenderName = "metric-sender" 292 )