github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/agent/util_test.go (about) 1 // Copyright 2012-2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agent 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 jc "github.com/juju/testing/checkers" 11 "github.com/juju/utils/set" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/cmd/jujud/agent/model" 15 coretesting "github.com/juju/juju/testing" 16 "github.com/juju/juju/worker" 17 "github.com/juju/juju/worker/dependency" 18 ) 19 20 var ( 21 // These vars hold the per-model workers we expect to run in 22 // various circumstances. Note the absence of dyingModelWorkers: 23 // it's not a stable state, because it's responsible for making 24 // the model Dead via the undertaker, so it can't be waited for 25 // reliably. 26 alwaysModelWorkers = []string{ 27 "agent", 28 "api-caller", 29 "api-config-watcher", 30 "clock", 31 "spaces-imported-gate", 32 "is-responsible-flag", 33 "not-alive-flag", 34 "not-dead-flag", 35 } 36 aliveModelWorkers = []string{ 37 "charm-revision-updater", 38 "compute-provisioner", 39 "environ-tracker", 40 "firewaller", 41 "instance-poller", 42 "metric-worker", 43 "migration-fortress", 44 "migration-master", 45 "service-scaler", 46 "space-importer", 47 "state-cleaner", 48 "status-history-pruner", 49 "storage-provisioner", 50 "unit-assigner", 51 } 52 deadModelWorkers = []string{ 53 "environ-tracker", "undertaker", 54 } 55 56 // ReallyLongTimeout should be long enough for the model-tracker 57 // tests that depend on a hosted model; its backing state is not 58 // accessible for StartSyncs, so we generally have to wait for at 59 // least two 5s ticks to pass, and should expect rare circumstances 60 // to take even longer. 61 ReallyLongWait = coretesting.LongWait * 3 62 ) 63 64 // modelMatchFunc returns a func that will return whether the current 65 // set of workers running for the supplied model matches those supplied; 66 // and will log what it saw in some detail. 67 func modelMatchFunc(c *gc.C, tracker *modelTracker, workers []string) func(string) bool { 68 expect := set.NewStrings(workers...) 69 return func(uuid string) bool { 70 actual := tracker.Workers(uuid) 71 c.Logf("\n%s: has workers %v", uuid, actual.SortedValues()) 72 extras := actual.Difference(expect) 73 missed := expect.Difference(actual) 74 if len(extras) == 0 && len(missed) == 0 { 75 return true 76 } 77 c.Logf("%s: waiting for %v", uuid, missed.SortedValues()) 78 c.Logf("%s: unexpected %v", uuid, extras.SortedValues()) 79 return false 80 } 81 } 82 83 // newModelTracker creates a type whose Manifolds method can 84 // be patched over modelManifolds, and whose Workers method 85 // will tell you what workers are currently running stably 86 // within the requested model's dependency engine. 87 func newModelTracker(c *gc.C) *modelTracker { 88 return &modelTracker{ 89 c: c, 90 current: make(map[string]set.Strings), 91 } 92 } 93 94 type modelTracker struct { 95 c *gc.C 96 mu sync.Mutex 97 current map[string]set.Strings 98 } 99 100 func (tracker *modelTracker) Workers(model string) set.Strings { 101 tracker.mu.Lock() 102 defer tracker.mu.Unlock() 103 return tracker.current[model] 104 } 105 106 func (tracker *modelTracker) Manifolds(config model.ManifoldsConfig) dependency.Manifolds { 107 const trackerName = "TEST-TRACKER" 108 raw := model.Manifolds(config) 109 uuid := config.Agent.CurrentConfig().Model().Id() 110 111 names := make([]string, 0, len(raw)) 112 for name := range raw { 113 if name == trackerName { 114 tracker.c.Errorf("manifold tracker used repeatedly") 115 return raw 116 } else { 117 names = append(names, name) 118 } 119 } 120 121 tracker.mu.Lock() 122 defer tracker.mu.Unlock() 123 if _, exists := tracker.current[uuid]; exists { 124 tracker.c.Errorf("model %s started repeatedly", uuid) 125 return raw 126 } 127 128 raw[trackerName] = tracker.manifold(uuid, names) 129 return raw 130 } 131 132 func (tracker *modelTracker) manifold(uuid string, names []string) dependency.Manifold { 133 return dependency.Manifold{ 134 Inputs: names, 135 Start: func(context dependency.Context) (worker.Worker, error) { 136 seen := set.NewStrings() 137 for _, name := range names { 138 err := context.Get(name, nil) 139 if errors.Cause(err) == dependency.ErrMissing { 140 continue 141 } 142 if tracker.c.Check(err, jc.ErrorIsNil) { 143 seen.Add(name) 144 } 145 } 146 select { 147 case <-context.Abort(): 148 // don't bother to report if it's about to change 149 default: 150 tracker.mu.Lock() 151 defer tracker.mu.Unlock() 152 tracker.current[uuid] = seen 153 } 154 return nil, dependency.ErrMissing 155 }, 156 } 157 }