github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/agent/util/housing.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package util 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/juju/worker" 9 "github.com/juju/juju/worker/dependency" 10 "github.com/juju/juju/worker/fortress" 11 ) 12 13 // Decorator creates copies of dependency.Manifolds with additional 14 // features. 15 type Decorator interface { 16 17 // Decorate returns a new Manifold, based on the one supplied. 18 Decorate(dependency.Manifold) dependency.Manifold 19 } 20 21 // Housing is a Decorator that combines several common mechanisms 22 // for coordinating manifolds independently of their core concerns. 23 type Housing struct { 24 25 // Flags contains a list of names of Flag manifolds, such 26 // that a decorated manifold will not start until all flags 27 // are both present and valid (and will be stopped when that 28 // is no longer true). 29 Flags []string 30 31 // Occupy is ignored if empty; otherwise it contains the name 32 // of a fortress.Guest manifold, such that a decorated manifold 33 // will never be run outside a Visit to that fortress. 34 // 35 // NOTE: this acquires a lock, and holds it for your manifold's 36 // worker's whole lifetime. It's fine in isolation, but multiple 37 // Occupy~s are almost certainly a Bad Idea. 38 Occupy string 39 40 // Filter is ignored if nil; otherwise it's unconditionally set 41 // as the manifold's Filter. Similarly to Occupy, attempted use 42 // of multiple filters is unlikely to be a great idea; it most 43 // likely indicates that either your Engine's IsFatal is too 44 // enthusiastic, or responsibility for termination is spread too 45 // widely across your installed manifolds, or both. 46 Filter dependency.FilterFunc 47 } 48 49 // Decorate is part of the Decorator interface. 50 func (housing Housing) Decorate(base dependency.Manifold) dependency.Manifold { 51 manifold := base 52 // Apply Occupy wrapping first, so that it will be the last 53 // wrapper to execute before calling the original Start func, so 54 // as to minimise the time we hold the fortress open. 55 if housing.Occupy != "" { 56 manifold.Inputs = maybeAdd(manifold.Inputs, housing.Occupy) 57 manifold.Start = occupyStart(manifold.Start, housing.Occupy) 58 } 59 for _, name := range housing.Flags { 60 manifold.Inputs = maybeAdd(manifold.Inputs, name) 61 manifold.Start = flagStart(manifold.Start, name) 62 } 63 if housing.Filter != nil { 64 manifold.Filter = housing.Filter 65 } 66 return manifold 67 } 68 69 func maybeAdd(original []string, add string) []string { 70 for _, name := range original { 71 if name == add { 72 return original 73 } 74 } 75 count := len(original) 76 result := make([]string, count, count+1) 77 copy(result, original) 78 return append(result, add) 79 } 80 81 func occupyStart(inner dependency.StartFunc, name string) dependency.StartFunc { 82 return func(context dependency.Context) (worker.Worker, error) { 83 var guest fortress.Guest 84 if err := context.Get(name, &guest); err != nil { 85 return nil, errors.Trace(err) 86 } 87 task := func() (worker.Worker, error) { 88 return inner(context) 89 } 90 worker, err := fortress.Occupy(guest, task, context.Abort()) 91 if err != nil { 92 return nil, errors.Trace(err) 93 } 94 return worker, nil 95 } 96 } 97 98 func flagStart(inner dependency.StartFunc, name string) dependency.StartFunc { 99 return func(context dependency.Context) (worker.Worker, error) { 100 var flag Flag 101 if err := context.Get(name, &flag); err != nil { 102 return nil, errors.Trace(err) 103 } 104 if !flag.Check() { 105 return nil, dependency.ErrMissing 106 } 107 return inner(context) 108 } 109 }