github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/engine_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 "fmt" 8 "sync" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/utils/set" 13 gc "gopkg.in/check.v1" 14 goyaml "gopkg.in/yaml.v2" 15 16 "github.com/juju/juju/cmd/jujud/agent/machine" 17 "github.com/juju/juju/cmd/jujud/agent/model" 18 "github.com/juju/juju/cmd/jujud/agent/unit" 19 coretesting "github.com/juju/juju/testing" 20 "github.com/juju/juju/worker" 21 "github.com/juju/juju/worker/dependency" 22 ) 23 24 var ( 25 // These vars hold the per-model workers we expect to run in 26 // various circumstances. Note the absence of worker lists for 27 // dying/dead states, because those states are not stable: if 28 // they're working correctly the engine will be shut down. 29 alwaysModelWorkers = []string{ 30 "agent", 31 "api-caller", 32 "api-config-watcher", 33 "clock", 34 "is-responsible-flag", 35 "not-alive-flag", 36 "not-dead-flag", 37 "spaces-imported-gate", 38 } 39 aliveModelWorkers = []string{ 40 "charm-revision-updater", 41 "compute-provisioner", 42 "environ-tracker", 43 "firewaller", 44 "instance-poller", 45 "machine-undertaker", 46 "metric-worker", 47 "migration-fortress", 48 "migration-inactive-flag", 49 "migration-master", 50 "application-scaler", 51 "space-importer", 52 "state-cleaner", 53 "status-history-pruner", 54 "storage-provisioner", 55 "unit-assigner", 56 } 57 migratingModelWorkers = []string{ 58 "environ-tracker", 59 "migration-fortress", 60 "migration-inactive-flag", 61 "migration-master", 62 } 63 // ReallyLongTimeout should be long enough for the model-tracker 64 // tests that depend on a hosted model; its backing state is not 65 // accessible for StartSyncs, so we generally have to wait for at 66 // least two 5s ticks to pass, and should expect rare circumstances 67 // to take even longer. 68 ReallyLongWait = coretesting.LongWait * 3 69 70 alwaysUnitWorkers = []string{ 71 "agent", 72 "api-caller", 73 "api-config-watcher", 74 "log-sender", 75 "migration-fortress", 76 "migration-inactive-flag", 77 "migration-minion", 78 "upgrader", 79 } 80 notMigratingUnitWorkers = []string{ 81 "api-address-updater", 82 "charm-dir", 83 "hook-retry-strategy", 84 "leadership-tracker", 85 "logging-config-updater", 86 "meter-status", 87 "metric-collect", 88 "metric-sender", 89 "metric-spool", 90 "proxy-config-updater", 91 "uniter", 92 } 93 94 alwaysMachineWorkers = []string{ 95 "agent", 96 "api-caller", 97 "api-config-watcher", 98 "log-forwarder", 99 "migration-fortress", 100 "migration-inactive-flag", 101 "migration-minion", 102 "state-config-watcher", 103 "termination-signal-handler", 104 "upgrade-check-flag", 105 "upgrade-check-gate", 106 "upgrade-steps-flag", 107 "upgrade-steps-gate", 108 "upgrader", 109 } 110 notMigratingMachineWorkers = []string{ 111 "api-address-updater", 112 "disk-manager", 113 // "host-key-reporter", not stable, exits when done 114 "log-sender", 115 "logging-config-updater", 116 "machine-action-runner", 117 "machiner", 118 "proxy-config-updater", 119 "reboot-executor", 120 "ssh-authkeys-updater", 121 "storage-provisioner", 122 "unconverted-api-workers", 123 "unit-agent-deployer", 124 } 125 ) 126 127 type ModelManifoldsFunc func(config model.ManifoldsConfig) dependency.Manifolds 128 129 func TrackModels(c *gc.C, tracker *engineTracker, inner ModelManifoldsFunc) ModelManifoldsFunc { 130 return func(config model.ManifoldsConfig) dependency.Manifolds { 131 raw := inner(config) 132 id := config.Agent.CurrentConfig().Model().Id() 133 if err := tracker.Install(raw, id); err != nil { 134 c.Errorf("cannot install tracker: %v", err) 135 } 136 return raw 137 } 138 } 139 140 type MachineManifoldsFunc func(config machine.ManifoldsConfig) dependency.Manifolds 141 142 func TrackMachines(c *gc.C, tracker *engineTracker, inner MachineManifoldsFunc) MachineManifoldsFunc { 143 return func(config machine.ManifoldsConfig) dependency.Manifolds { 144 raw := inner(config) 145 id := config.Agent.CurrentConfig().Tag().String() 146 if err := tracker.Install(raw, id); err != nil { 147 c.Errorf("cannot install tracker: %v", err) 148 } 149 return raw 150 } 151 } 152 153 type UnitManifoldsFunc func(config unit.ManifoldsConfig) dependency.Manifolds 154 155 func TrackUnits(c *gc.C, tracker *engineTracker, inner UnitManifoldsFunc) UnitManifoldsFunc { 156 return func(config unit.ManifoldsConfig) dependency.Manifolds { 157 raw := inner(config) 158 id := config.Agent.CurrentConfig().Tag().String() 159 if err := tracker.Install(raw, id); err != nil { 160 c.Errorf("cannot install tracker: %v", err) 161 } 162 return raw 163 } 164 } 165 166 // NewWorkerManager takes an engineTracker, an engine manager id to 167 // monitor and the workers that are expected to be running and sets up 168 // a WorkerManager. 169 func NewWorkerMatcher(c *gc.C, tracker *engineTracker, id string, workers []string) *WorkerMatcher { 170 return &WorkerMatcher{ 171 c: c, 172 tracker: tracker, 173 id: id, 174 expect: set.NewStrings(workers...), 175 } 176 } 177 178 // WorkerMatcher monitors the workers of a single engine manager, 179 // using an engineTracker, for a given set of workers to be running. 180 type WorkerMatcher struct { 181 c *gc.C 182 tracker *engineTracker 183 id string 184 expect set.Strings 185 matchTime time.Time 186 } 187 188 // Check returns true if the workers which are expected to be running 189 // (as specified in the call to NewWorkerMatcher) are running and have 190 // been running for a short period (i.e. some indication of stability). 191 func (m *WorkerMatcher) Check() bool { 192 if m.checkOnce() { 193 now := time.Now() 194 if m.matchTime.IsZero() { 195 m.matchTime = now 196 return false 197 } 198 // Only return that the required workers have started if they 199 // have been stable for a little while. 200 return now.Sub(m.matchTime) >= time.Second 201 } 202 // Required workers not running, reset the timestamp. 203 m.matchTime = time.Time{} 204 return false 205 } 206 207 func (m *WorkerMatcher) checkOnce() bool { 208 actual := m.tracker.Workers(m.id) 209 m.c.Logf("\n%s: has workers %v", m.id, actual.SortedValues()) 210 extras := actual.Difference(m.expect) 211 missed := m.expect.Difference(actual) 212 if len(extras) == 0 && len(missed) == 0 { 213 return true 214 } 215 m.c.Logf("%s: waiting for %v", m.id, missed.SortedValues()) 216 m.c.Logf("%s: unexpected %v", m.id, extras.SortedValues()) 217 report, _ := goyaml.Marshal(m.tracker.Report(m.id)) 218 m.c.Logf("%s: report: \n%s\n", m.id, report) 219 return false 220 } 221 222 // WaitMatch returns only when the match func succeeds, or it times out. 223 func WaitMatch(c *gc.C, match func() bool, maxWait time.Duration, sync func()) { 224 timeout := time.After(maxWait) 225 for { 226 if match() { 227 return 228 } 229 select { 230 case <-time.After(coretesting.ShortWait): 231 sync() 232 case <-timeout: 233 c.Fatalf("timed out waiting for workers") 234 } 235 } 236 } 237 238 // NewEngineTracker creates a type that can Install itself into a 239 // Manifolds map, and expose recent snapshots of running Workers. 240 func NewEngineTracker() *engineTracker { 241 return &engineTracker{ 242 current: make(map[string]set.Strings), 243 reports: make(map[string]map[string]interface{}), 244 } 245 } 246 247 type engineTracker struct { 248 mu sync.Mutex 249 current map[string]set.Strings 250 reports map[string]map[string]interface{} 251 } 252 253 // Workers returns the most-recently-reported set of running workers. 254 func (tracker *engineTracker) Workers(id string) set.Strings { 255 tracker.mu.Lock() 256 defer tracker.mu.Unlock() 257 return tracker.current[id] 258 } 259 260 // Report returns the most-recently-reported self-report. It will 261 // only work if you hack up the relevant engine-starting code to 262 // include: 263 // 264 // manifolds["self"] = dependency.SelfManifold(engine) 265 // 266 // or otherwise inject a suitable "self" manifold. 267 func (tracker *engineTracker) Report(id string) map[string]interface{} { 268 tracker.mu.Lock() 269 defer tracker.mu.Unlock() 270 return tracker.reports[id] 271 } 272 273 // Install injects a manifold named TEST-TRACKER into raw, which will 274 // depend on all other manifolds in raw and write currently-available 275 // worker information to the tracker (differentiating it from other 276 // tracked engines via the id param). 277 func (tracker *engineTracker) Install(raw dependency.Manifolds, id string) error { 278 const trackerName = "TEST-TRACKER" 279 280 names := make([]string, 0, len(raw)) 281 for name := range raw { 282 if name == trackerName { 283 return errors.New("engine tracker installed repeatedly") 284 } 285 names = append(names, name) 286 } 287 288 tracker.mu.Lock() 289 defer tracker.mu.Unlock() 290 if _, exists := tracker.current[id]; exists { 291 return errors.Errorf("manifolds for %s created repeatedly", id) 292 } 293 raw[trackerName] = dependency.Manifold{ 294 Inputs: append(names, "self"), 295 Start: tracker.startFunc(id, names), 296 } 297 return nil 298 } 299 300 func (tracker *engineTracker) startFunc(id string, names []string) dependency.StartFunc { 301 return func(context dependency.Context) (worker.Worker, error) { 302 303 seen := set.NewStrings() 304 for _, name := range names { 305 err := context.Get(name, nil) 306 switch errors.Cause(err) { 307 case nil: 308 case dependency.ErrMissing: 309 continue 310 default: 311 name = fmt.Sprintf("%s [%v]", name, err) 312 } 313 seen.Add(name) 314 } 315 316 var report map[string]interface{} 317 var reporter dependency.Reporter 318 if err := context.Get("self", &reporter); err == nil { 319 report = reporter.Report() 320 } 321 322 select { 323 case <-context.Abort(): 324 // don't bother to report if it's about to change 325 default: 326 tracker.mu.Lock() 327 defer tracker.mu.Unlock() 328 tracker.current[id] = seen 329 tracker.reports[id] = report 330 } 331 return nil, dependency.ErrMissing 332 } 333 }