github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/engine/housing.go (about) 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 }