1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
9
"github.com/juju/juju/worker"
10
"github.com/juju/juju/worker/dependency"
11
"github.com/juju/juju/worker/fortress"
14
// Decorator creates copies of dependency.Manifolds with additional
16
type Decorator interface {
18
// Decorate returns a new Manifold, based on the one supplied.
19
Decorate(dependency.Manifold) dependency.Manifold
22
// Housing is a Decorator that combines several common mechanisms
23
// for coordinating manifolds independently of their core concerns.
26
// Flags contains a list of names of Flag manifolds, such
27
// that a decorated manifold will not start until all flags
28
// are both present and valid (and will be stopped when that
29
// is no longer true).
32
// Occupy is ignored if empty; otherwise it contains the name
33
// of a fortress.Guest manifold, such that a decorated manifold
34
// will never be run outside a Visit to that fortress.
36
// NOTE: this acquires a lock, and holds it for your manifold's
37
// worker's whole lifetime. It's fine in isolation, but multiple
38
// Occupy~s are almost certainly a Bad Idea.
41
// Filter is ignored if nil; otherwise it's unconditionally set
42
// as the manifold's Filter. Similarly to Occupy, attempted use
43
// of multiple filters is unlikely to be a great idea; it most
44
// likely indicates that either your Engine's IsFatal is too
45
// enthusiastic, or responsibility for termination is spread too
46
// widely across your installed manifolds, or both.
47
Filter dependency.FilterFunc
50
// Decorate is part of the Decorator interface.
51
func (housing Housing) Decorate(base dependency.Manifold) dependency.Manifold {
53
// Apply Occupy wrapping first, so that it will be the last
54
// wrapper to execute before calling the original Start func, so
55
// as to minimise the time we hold the fortress open.
56
if housing.Occupy != "" {
57
manifold.Inputs = maybeAdd(manifold.Inputs, housing.Occupy)
58
manifold.Start = occupyStart(manifold.Start, housing.Occupy)
60
for _, name := range housing.Flags {
61
manifold.Inputs = maybeAdd(manifold.Inputs, name)
62
manifold.Start = flagStart(manifold.Start, name)
64
if housing.Filter != nil {
65
manifold.Filter = housing.Filter
70
func maybeAdd(original []string, add string) []string {
71
for _, name := range original {
76
count := len(original)
77
result := make([]string, count, count+1)
78
copy(result, original)
79
return append(result, add)
82
func occupyStart(inner dependency.StartFunc, name string) dependency.StartFunc {
83
return func(context dependency.Context) (worker.Worker, error) {
84
var guest fortress.Guest
85
if err := context.Get(name, &guest); err != nil {
86
return nil, errors.Trace(err)
88
task := func() (worker.Worker, error) {
91
worker, err := fortress.Occupy(guest, task, context.Abort())
93
return nil, errors.Trace(err)
99
func flagStart(inner dependency.StartFunc, name string) dependency.StartFunc {
100
return func(context dependency.Context) (worker.Worker, error) {
102
if err := context.Get(name, &flag); err != nil {
103
return nil, errors.Trace(err)
106
return nil, errors.Annotatef(dependency.ErrMissing, "%q not set", name)
108
return inner(context)