~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/jujud/agent/model/manifolds.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/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"
 
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
        // InstPollerAggregationDelay is the delay before sending a batch of
 
65
        // requests in the instancpoller.Worker's aggregate loop.
 
66
        InstPollerAggregationDelay time.Duration
 
67
 
 
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
 
71
        // to be held.
 
72
        RunFlagDuration time.Duration
 
73
 
 
74
        // CharmRevisionUpdateInterval determines how often the charm-
 
75
        // revision worker will check for new revisions of known charms.
 
76
        CharmRevisionUpdateInterval time.Duration
 
77
 
 
78
        // StatusHistoryPruner* values control status-history pruning
 
79
        // behaviour.
 
80
        StatusHistoryPrunerMaxHistoryTime time.Duration
 
81
        StatusHistoryPrunerMaxHistoryMB   uint
 
82
        StatusHistoryPrunerInterval       time.Duration
 
83
 
 
84
        // SpacesImportedGate will be unlocked when spaces are known to
 
85
        // have been imported.
 
86
        SpacesImportedGate gate.Lock
 
87
 
 
88
        // NewEnvironFunc is a function opens a provider "environment"
 
89
        // (typically environs.New).
 
90
        NewEnvironFunc environs.NewEnvironFunc
 
91
}
 
92
 
 
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{
 
98
 
 
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
 
102
                // controller.
 
103
                agentName: agent.Manifold(config.Agent),
 
104
                clockName: clockManifold(config.Clock),
 
105
                apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
 
106
                        AgentName:          agentName,
 
107
                        AgentConfigChanged: config.AgentConfigChanged,
 
108
                }),
 
109
                apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
 
110
                        AgentName:     agentName,
 
111
                        APIOpen:       apicaller.APIOpen,
 
112
                        NewConnection: apicaller.OnlyConnect,
 
113
                }),
 
114
 
 
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),
 
120
 
 
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,
 
126
                        Entity:        modelTag,
 
127
                        Result:        life.IsNotDead,
 
128
                        Filter:        LifeFilter,
 
129
 
 
130
                        NewFacade: lifeflag.NewFacade,
 
131
                        NewWorker: lifeflag.NewWorker,
 
132
                }),
 
133
                notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{
 
134
                        APICallerName: apiCallerName,
 
135
                        Entity:        modelTag,
 
136
                        Result:        life.IsNotAlive,
 
137
                        Filter:        LifeFilter,
 
138
 
 
139
                        NewFacade: lifeflag.NewFacade,
 
140
                        NewWorker: lifeflag.NewWorker,
 
141
                }),
 
142
                isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{
 
143
                        ClockName:     clockName,
 
144
                        AgentName:     agentName,
 
145
                        APICallerName: apiCallerName,
 
146
                        Duration:      config.RunFlagDuration,
 
147
 
 
148
                        NewFacade: singular.NewFacade,
 
149
                        NewWorker: singular.NewWorker,
 
150
                }),
 
151
 
 
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
 
160
                // fortress).
 
161
                //
 
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,
 
171
                })),
 
172
                migrationMasterName: ifNotDead(migrationmaster.Manifold(migrationmaster.ManifoldConfig{
 
173
                        AgentName:     agentName,
 
174
                        APICallerName: apiCallerName,
 
175
                        FortressName:  migrationFortressName,
 
176
                        Clock:         config.Clock,
 
177
                        NewFacade:     migrationmaster.NewFacade,
 
178
                        NewWorker:     migrationmaster.NewWorker,
 
179
                })),
 
180
 
 
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
 
185
                // any one time.
 
186
                //
 
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
 
196
                //
 
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
 
202
                // it.
 
203
 
 
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,
 
209
                })),
 
210
 
 
211
                // The undertaker is currently the only ifNotAlive worker.
 
212
                undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{
 
213
                        APICallerName: apiCallerName,
 
214
                        EnvironName:   environTrackerName,
 
215
 
 
216
                        NewFacade: undertaker.NewFacade,
 
217
                        NewWorker: undertaker.NewWorker,
 
218
                })),
 
219
 
 
220
                // All the rest depend on ifNotMigrating.
 
221
                spaceImporterName: ifNotMigrating(discoverspaces.Manifold(discoverspaces.ManifoldConfig{
 
222
                        EnvironName:   environTrackerName,
 
223
                        APICallerName: apiCallerName,
 
224
                        UnlockerName:  spacesImportedGateName,
 
225
 
 
226
                        NewFacade: discoverspaces.NewFacade,
 
227
                        NewWorker: discoverspaces.NewWorker,
 
228
                })),
 
229
                computeProvisionerName: ifNotMigrating(provisioner.Manifold(provisioner.ManifoldConfig{
 
230
                        AgentName:          agentName,
 
231
                        APICallerName:      apiCallerName,
 
232
                        EnvironName:        environTrackerName,
 
233
                        NewProvisionerFunc: provisioner.NewEnvironProvisioner,
 
234
                })),
 
235
                storageProvisionerName: ifNotMigrating(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{
 
236
                        APICallerName: apiCallerName,
 
237
                        ClockName:     clockName,
 
238
                        EnvironName:   environTrackerName,
 
239
                        Scope:         modelTag,
 
240
                })),
 
241
                firewallerName: ifNotMigrating(firewaller.Manifold(firewaller.ManifoldConfig{
 
242
                        APICallerName: apiCallerName,
 
243
                })),
 
244
                unitAssignerName: ifNotMigrating(unitassigner.Manifold(unitassigner.ManifoldConfig{
 
245
                        APICallerName: apiCallerName,
 
246
                })),
 
247
                applicationScalerName: ifNotMigrating(applicationscaler.Manifold(applicationscaler.ManifoldConfig{
 
248
                        APICallerName: apiCallerName,
 
249
                        NewFacade:     applicationscaler.NewFacade,
 
250
                        NewWorker:     applicationscaler.New,
 
251
                })),
 
252
                instancePollerName: ifNotMigrating(instancepoller.Manifold(instancepoller.ManifoldConfig{
 
253
                        APICallerName: apiCallerName,
 
254
                        EnvironName:   environTrackerName,
 
255
                        ClockName:     clockName,
 
256
                        Delay:         config.InstPollerAggregationDelay,
 
257
                })),
 
258
                charmRevisionUpdaterName: ifNotMigrating(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{
 
259
                        APICallerName: apiCallerName,
 
260
                        ClockName:     clockName,
 
261
                        Period:        config.CharmRevisionUpdateInterval,
 
262
 
 
263
                        NewFacade: charmrevisionmanifold.NewAPIFacade,
 
264
                        NewWorker: charmrevision.NewWorker,
 
265
                })),
 
266
                metricWorkerName: ifNotMigrating(metricworker.Manifold(metricworker.ManifoldConfig{
 
267
                        APICallerName: apiCallerName,
 
268
                })),
 
269
                stateCleanerName: ifNotMigrating(cleaner.Manifold(cleaner.ManifoldConfig{
 
270
                        APICallerName: apiCallerName,
 
271
                })),
 
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,
 
279
                })),
 
280
        }
 
281
}
 
282
 
 
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)
 
288
                },
 
289
                Output: engine.ValueWorkerOutput,
 
290
        }
 
291
}
 
292
 
 
293
var (
 
294
        // ifResponsible wraps a manifold such that it only runs if the
 
295
        // responsibility flag is set.
 
296
        ifResponsible = engine.Housing{
 
297
                Flags: []string{
 
298
                        isResponsibleFlagName,
 
299
                },
 
300
        }.Decorate
 
301
 
 
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{
 
305
                Flags: []string{
 
306
                        isResponsibleFlagName,
 
307
                        notAliveFlagName,
 
308
                },
 
309
        }.Decorate
 
310
 
 
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{
 
314
                Flags: []string{
 
315
                        isResponsibleFlagName,
 
316
                        notDeadFlagName,
 
317
                },
 
318
        }.Decorate
 
319
 
 
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
 
325
        // dependencies.
 
326
        ifNotMigrating = engine.Housing{
 
327
                Flags: []string{
 
328
                        migrationInactiveFlagName,
 
329
                },
 
330
                Occupy: migrationFortressName,
 
331
        }.Decorate
 
332
)
 
333
 
 
334
const (
 
335
        agentName            = "agent"
 
336
        clockName            = "clock"
 
337
        apiConfigWatcherName = "api-config-watcher"
 
338
        apiCallerName        = "api-caller"
 
339
 
 
340
        spacesImportedGateName = "spaces-imported-gate"
 
341
        isResponsibleFlagName  = "is-responsible-flag"
 
342
        notDeadFlagName        = "not-dead-flag"
 
343
        notAliveFlagName       = "not-alive-flag"
 
344
 
 
345
        migrationFortressName     = "migration-fortress"
 
346
        migrationInactiveFlagName = "migration-inactive-flag"
 
347
        migrationMasterName       = "migration-master"
 
348
 
 
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"
 
362
)