~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/jujud/agent/engine/housing.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 engine
 
5
 
 
6
import (
 
7
        "github.com/juju/errors"
 
8
 
 
9
        "github.com/juju/juju/worker"
 
10
        "github.com/juju/juju/worker/dependency"
 
11
        "github.com/juju/juju/worker/fortress"
 
12
)
 
13
 
 
14
// Decorator creates copies of dependency.Manifolds with additional
 
15
// features.
 
16
type Decorator interface {
 
17
 
 
18
        // Decorate returns a new Manifold, based on the one supplied.
 
19
        Decorate(dependency.Manifold) dependency.Manifold
 
20
}
 
21
 
 
22
// Housing is a Decorator that combines several common mechanisms
 
23
// for coordinating manifolds independently of their core concerns.
 
24
type Housing struct {
 
25
 
 
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).
 
30
        Flags []string
 
31
 
 
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.
 
35
        //
 
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.
 
39
        Occupy string
 
40
 
 
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
 
48
}
 
49
 
 
50
// Decorate is part of the Decorator interface.
 
51
func (housing Housing) Decorate(base dependency.Manifold) dependency.Manifold {
 
52
        manifold := base
 
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)
 
59
        }
 
60
        for _, name := range housing.Flags {
 
61
                manifold.Inputs = maybeAdd(manifold.Inputs, name)
 
62
                manifold.Start = flagStart(manifold.Start, name)
 
63
        }
 
64
        if housing.Filter != nil {
 
65
                manifold.Filter = housing.Filter
 
66
        }
 
67
        return manifold
 
68
}
 
69
 
 
70
func maybeAdd(original []string, add string) []string {
 
71
        for _, name := range original {
 
72
                if name == add {
 
73
                        return original
 
74
                }
 
75
        }
 
76
        count := len(original)
 
77
        result := make([]string, count, count+1)
 
78
        copy(result, original)
 
79
        return append(result, add)
 
80
}
 
81
 
 
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)
 
87
                }
 
88
                task := func() (worker.Worker, error) {
 
89
                        return inner(context)
 
90
                }
 
91
                worker, err := fortress.Occupy(guest, task, context.Abort())
 
92
                if err != nil {
 
93
                        return nil, errors.Trace(err)
 
94
                }
 
95
                return worker, nil
 
96
        }
 
97
}
 
98
 
 
99
func flagStart(inner dependency.StartFunc, name string) dependency.StartFunc {
 
100
        return func(context dependency.Context) (worker.Worker, error) {
 
101
                var flag Flag
 
102
                if err := context.Get(name, &flag); err != nil {
 
103
                        return nil, errors.Trace(err)
 
104
                }
 
105
                if !flag.Check() {
 
106
                        return nil, errors.Annotatef(dependency.ErrMissing, "%q not set", name)
 
107
                }
 
108
                return inner(context)
 
109
        }
 
110
}