1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/utils/clock"
10
"github.com/juju/utils/voyeur"
12
coreagent "github.com/juju/juju/agent"
13
"github.com/juju/juju/cmd/jujud/agent/engine"
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/agent"
18
"github.com/juju/juju/worker/apicaller"
19
"github.com/juju/juju/worker/apiconfigwatcher"
20
"github.com/juju/juju/worker/applicationscaler"
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/migrationflag"
34
"github.com/juju/juju/worker/migrationmaster"
35
"github.com/juju/juju/worker/provisioner"
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"
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 {
48
// Agent identifies, and exposes configuration for, the controller
49
// machine running these manifolds and the model the manifolds
52
// You should almost certainly set this value to one created by
56
// AgentConfigChanged will be set whenever the agent's api config
58
AgentConfigChanged *voyeur.Value
60
// Clock supplies timing services to any manifolds that need them.
61
// Only a few workers have been converted to use them fo far.
64
// InstPollerAggregationDelay is the delay before sending a batch of
65
// requests in the instancpoller.Worker's aggregate loop.
66
InstPollerAggregationDelay time.Duration
68
// RunFlagDuration defines for how long this controller will ask
69
// for model administration rights; most of the workers controlled
70
// by this agent will only be started when the run flag is known
72
RunFlagDuration time.Duration
74
// CharmRevisionUpdateInterval determines how often the charm-
75
// revision worker will check for new revisions of known charms.
76
CharmRevisionUpdateInterval time.Duration
78
// StatusHistoryPruner* values control status-history pruning
80
StatusHistoryPrunerMaxHistoryTime time.Duration
81
StatusHistoryPrunerMaxHistoryMB uint
82
StatusHistoryPrunerInterval time.Duration
84
// SpacesImportedGate will be unlocked when spaces are known to
85
// have been imported.
86
SpacesImportedGate gate.Lock
88
// NewEnvironFunc is a function opens a provider "environment"
89
// (typically environs.New).
90
NewEnvironFunc environs.NewEnvironFunc
93
// Manifolds returns a set of interdependent dependency manifolds that will
94
// run together to administer a model, as configured.
95
func Manifolds(config ManifoldsConfig) dependency.Manifolds {
96
modelTag := config.Agent.CurrentConfig().Model()
97
return dependency.Manifolds{
99
// The first group are foundational; the agent and clock
100
// which wrap those supplied in config, and the api-caller
101
// through which everything else communicates with the
103
agentName: agent.Manifold(config.Agent),
104
clockName: clockManifold(config.Clock),
105
apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
106
AgentName: agentName,
107
AgentConfigChanged: config.AgentConfigChanged,
109
apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
110
AgentName: agentName,
111
APIOpen: apicaller.APIOpen,
112
NewConnection: apicaller.OnlyConnect,
115
// The spaces-imported gate will be unlocked when space
116
// discovery is known to be complete. Various manifolds
117
// should also come to depend upon it (or rather, on a
118
// Flag depending on it) in the future.
119
spacesImportedGateName: gate.ManifoldEx(config.SpacesImportedGate),
121
// All other manifolds should depend on at least one of these
122
// three, which handle all the tasks that are safe and sane
123
// to run in *all* controller machines.
124
notDeadFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
125
APICallerName: apiCallerName,
127
Result: life.IsNotDead,
130
NewFacade: lifeflag.NewFacade,
131
NewWorker: lifeflag.NewWorker,
133
notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
134
APICallerName: apiCallerName,
136
Result: life.IsNotAlive,
139
NewFacade: lifeflag.NewFacade,
140
NewWorker: lifeflag.NewWorker,
142
isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{
143
ClockName: clockName,
144
AgentName: agentName,
145
APICallerName: apiCallerName,
146
Duration: config.RunFlagDuration,
148
NewFacade: singular.NewFacade,
149
NewWorker: singular.NewWorker,
152
// The migration workers collaborate to run migrations;
153
// and to create a mechanism for running other workers
154
// so they can't accidentally interfere with a migration
155
// in progress. Such a manifold should (1) depend on the
156
// migration-inactive flag, to know when to start or die;
157
// and (2) occupy the migration-fortress, so as to avoid
158
// possible interference with the minion (which will not
159
// take action until it's gained sole control of the
162
// Note that the fortress and flag will only exist while
163
// the model is not dead; this frees their dependencies
164
// from model-lifetime concerns.
165
migrationFortressName: ifNotDead(fortress.Manifold()),
166
migrationInactiveFlagName: ifNotDead(migrationflag.Manifold(migrationflag.ManifoldConfig{
167
APICallerName: apiCallerName,
168
Check: migrationflag.IsTerminal,
169
NewFacade: migrationflag.NewFacade,
170
NewWorker: migrationflag.NewWorker,
172
migrationMasterName: ifNotDead(migrationmaster.Manifold(migrationmaster.ManifoldConfig{
173
AgentName: agentName,
174
APICallerName: apiCallerName,
175
FortressName: migrationFortressName,
177
NewFacade: migrationmaster.NewFacade,
178
NewWorker: migrationmaster.NewWorker,
181
// Everything else should be wrapped in ifResponsible,
182
// ifNotAlive, ifNotDead, or ifNotMigrating (which also
183
// implies NotDead), to ensure that only a single
184
// controller is attempting to administer this model at
187
// NOTE: not perfectly reliable at this stage? i.e. a
188
// worker that ignores its stop signal for "too long"
189
// might continue to take admin actions after the window
190
// of responsibility closes. This *is* a pre-existing
191
// problem, but demands some thought/care: e.g. should
192
// we make sure the apiserver also closes any
193
// connections that lose responsibility..? can we make
194
// sure all possible environ operations are either time-
195
// bounded or interruptible? etc
197
// On the other hand, all workers *should* be written in
198
// the expectation of dealing with sucky infrastructure
199
// running things in parallel unexpectedly, just because
200
// the universe hates us and will engineer matters such
201
// that it happens sometimes, even when we try to avoid
204
// The environ tracker could/should be used by several other
205
// workers (firewaller, provisioners, address-cleaner?).
206
environTrackerName: ifResponsible(environ.Manifold(environ.ManifoldConfig{
207
APICallerName: apiCallerName,
208
NewEnvironFunc: config.NewEnvironFunc,
211
// The undertaker is currently the only ifNotAlive worker.
212
undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{
213
APICallerName: apiCallerName,
214
EnvironName: environTrackerName,
216
NewFacade: undertaker.NewFacade,
217
NewWorker: undertaker.NewWorker,
220
// All the rest depend on ifNotMigrating.
221
spaceImporterName: ifNotMigrating(discoverspaces.Manifold(discoverspaces.ManifoldConfig{
222
EnvironName: environTrackerName,
223
APICallerName: apiCallerName,
224
UnlockerName: spacesImportedGateName,
226
NewFacade: discoverspaces.NewFacade,
227
NewWorker: discoverspaces.NewWorker,
229
computeProvisionerName: ifNotMigrating(provisioner.Manifold(provisioner.ManifoldConfig{
230
AgentName: agentName,
231
APICallerName: apiCallerName,
232
EnvironName: environTrackerName,
233
NewProvisionerFunc: provisioner.NewEnvironProvisioner,
235
storageProvisionerName: ifNotMigrating(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{
236
APICallerName: apiCallerName,
237
ClockName: clockName,
238
EnvironName: environTrackerName,
241
firewallerName: ifNotMigrating(firewaller.Manifold(firewaller.ManifoldConfig{
242
APICallerName: apiCallerName,
244
unitAssignerName: ifNotMigrating(unitassigner.Manifold(unitassigner.ManifoldConfig{
245
APICallerName: apiCallerName,
247
applicationScalerName: ifNotMigrating(applicationscaler.Manifold(applicationscaler.ManifoldConfig{
248
APICallerName: apiCallerName,
249
NewFacade: applicationscaler.NewFacade,
250
NewWorker: applicationscaler.New,
252
instancePollerName: ifNotMigrating(instancepoller.Manifold(instancepoller.ManifoldConfig{
253
APICallerName: apiCallerName,
254
EnvironName: environTrackerName,
255
ClockName: clockName,
256
Delay: config.InstPollerAggregationDelay,
258
charmRevisionUpdaterName: ifNotMigrating(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{
259
APICallerName: apiCallerName,
260
ClockName: clockName,
261
Period: config.CharmRevisionUpdateInterval,
263
NewFacade: charmrevisionmanifold.NewAPIFacade,
264
NewWorker: charmrevision.NewWorker,
266
metricWorkerName: ifNotMigrating(metricworker.Manifold(metricworker.ManifoldConfig{
267
APICallerName: apiCallerName,
269
stateCleanerName: ifNotMigrating(cleaner.Manifold(cleaner.ManifoldConfig{
270
APICallerName: apiCallerName,
272
statusHistoryPrunerName: ifNotMigrating(statushistorypruner.Manifold(statushistorypruner.ManifoldConfig{
273
APICallerName: apiCallerName,
274
MaxHistoryTime: config.StatusHistoryPrunerMaxHistoryTime,
275
MaxHistoryMB: config.StatusHistoryPrunerMaxHistoryMB,
276
PruneInterval: config.StatusHistoryPrunerInterval,
277
// TODO(fwereade): 2016-03-17 lp:1558657
278
NewTimer: worker.NewTimer,
283
// clockManifold expresses a Clock as a ValueWorker manifold.
284
func clockManifold(clock clock.Clock) dependency.Manifold {
285
return dependency.Manifold{
286
Start: func(_ dependency.Context) (worker.Worker, error) {
287
return engine.NewValueWorker(clock)
289
Output: engine.ValueWorkerOutput,
294
// ifResponsible wraps a manifold such that it only runs if the
295
// responsibility flag is set.
296
ifResponsible = engine.Housing{
298
isResponsibleFlagName,
302
// ifNotAlive wraps a manifold such that it only runs if the
303
// responsibility flag is set and the model is Dying or Dead.
304
ifNotAlive = engine.Housing{
306
isResponsibleFlagName,
311
// ifNotDead wraps a manifold such that it only runs if the
312
// responsibility flag is set and the model is Alive or Dying.
313
ifNotDead = engine.Housing{
315
isResponsibleFlagName,
320
// ifNotMigrating wraps a manifold such that it only runs if the
321
// migration-inactive flag is set; and then runs workers only
322
// within Visits to the migration fortress. To avoid redundancy,
323
// it takes advantage of the fact that those migration manifolds
324
// themselves depend on ifNotDead, and eschews repeating those
326
ifNotMigrating = engine.Housing{
328
migrationInactiveFlagName,
330
Occupy: migrationFortressName,
337
apiConfigWatcherName = "api-config-watcher"
338
apiCallerName = "api-caller"
340
spacesImportedGateName = "spaces-imported-gate"
341
isResponsibleFlagName = "is-responsible-flag"
342
notDeadFlagName = "not-dead-flag"
343
notAliveFlagName = "not-alive-flag"
345
migrationFortressName = "migration-fortress"
346
migrationInactiveFlagName = "migration-inactive-flag"
347
migrationMasterName = "migration-master"
349
environTrackerName = "environ-tracker"
350
undertakerName = "undertaker"
351
spaceImporterName = "space-importer"
352
computeProvisionerName = "compute-provisioner"
353
storageProvisionerName = "storage-provisioner"
354
firewallerName = "firewaller"
355
unitAssignerName = "unit-assigner"
356
applicationScalerName = "application-scaler"
357
instancePollerName = "instance-poller"
358
charmRevisionUpdaterName = "charm-revision-updater"
359
metricWorkerName = "metric-worker"
360
stateCleanerName = "state-cleaner"
361
statusHistoryPrunerName = "status-history-pruner"