github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/migrationflag/worker.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package migrationflag 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/utils" 9 10 "github.com/juju/juju/core/migration" 11 "github.com/juju/juju/watcher" 12 "github.com/juju/juju/worker/catacomb" 13 ) 14 15 // ErrChanged indicates that a Worker has stopped because its 16 // Check result is no longer valid. 17 var ErrChanged = errors.New("migration flag value changed") 18 19 // Facade exposes controller functionality required by a Worker. 20 type Facade interface { 21 Watch(uuid string) (watcher.NotifyWatcher, error) 22 Phase(uuid string) (migration.Phase, error) 23 } 24 25 // Predicate defines a predicate. 26 type Predicate func(migration.Phase) bool 27 28 // IsNone is a predicate. 29 func IsNone(phase migration.Phase) bool { 30 return phase == migration.NONE 31 } 32 33 // Config holds the dependencies and configuration for a Worker. 34 type Config struct { 35 Facade Facade 36 Model string 37 Check Predicate 38 } 39 40 // Validate returns an error if the config cannot be expected to 41 // drive a functional Worker. 42 func (config Config) Validate() error { 43 if config.Facade == nil { 44 return errors.NotValidf("nil Facade") 45 } 46 if !utils.IsValidUUIDString(config.Model) { 47 return errors.NotValidf("Model %q", config.Model) 48 } 49 if config.Check == nil { 50 return errors.NotValidf("nil Check") 51 } 52 return nil 53 } 54 55 // New returns a Worker that tracks the result of the configured 56 // Check on the Model's migration phase, as exposed by the Facade. 57 func New(config Config) (*Worker, error) { 58 if err := config.Validate(); err != nil { 59 return nil, errors.Trace(err) 60 } 61 phase, err := config.Facade.Phase(config.Model) 62 if err != nil { 63 return nil, errors.Trace(err) 64 } 65 66 w := &Worker{ 67 config: config, 68 phase: phase, 69 } 70 err = catacomb.Invoke(catacomb.Plan{ 71 Site: &w.catacomb, 72 Work: w.loop, 73 }) 74 if err != nil { 75 return nil, errors.Trace(err) 76 } 77 return w, nil 78 } 79 80 // Worker implements worker.Worker and util.Flag, and exits 81 // with ErrChanged whenever the result of its configured Check of 82 // the Model's migration phase changes. 83 type Worker struct { 84 catacomb catacomb.Catacomb 85 config Config 86 phase migration.Phase 87 } 88 89 // Kill is part of the worker.Worker interface. 90 func (w *Worker) Kill() { 91 w.catacomb.Kill(nil) 92 } 93 94 // Wait is part of the worker.Worker interface. 95 func (w *Worker) Wait() error { 96 return w.catacomb.Wait() 97 } 98 99 // Check is part of the util.Flag interface. 100 func (w *Worker) Check() bool { 101 return w.config.Check(w.phase) 102 } 103 104 func (w *Worker) loop() error { 105 model := w.config.Model 106 facade := w.config.Facade 107 watcher, err := facade.Watch(model) 108 if err != nil { 109 return errors.Trace(err) 110 } 111 if err := w.catacomb.Add(watcher); err != nil { 112 return errors.Trace(err) 113 } 114 for { 115 select { 116 case <-w.catacomb.Dying(): 117 return w.catacomb.ErrDying() 118 case <-watcher.Changes(): 119 phase, err := facade.Phase(model) 120 if err != nil { 121 return errors.Trace(err) 122 } 123 if w.Check() != w.config.Check(phase) { 124 return ErrChanged 125 } 126 } 127 } 128 }