github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "gopkg.in/juju/worker.v1/catacomb" 10 11 "github.com/juju/juju/core/migration" 12 "github.com/juju/juju/core/watcher" 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 // IsTerminal returns true when the given phase means a migration has 29 // finished (successfully or otherwise). 30 func IsTerminal(phase migration.Phase) bool { 31 return phase.IsTerminal() 32 } 33 34 // Config holds the dependencies and configuration for a Worker. 35 type Config struct { 36 Facade Facade 37 Model string 38 Check Predicate 39 } 40 41 // Validate returns an error if the config cannot be expected to 42 // drive a functional Worker. 43 func (config Config) Validate() error { 44 if config.Facade == nil { 45 return errors.NotValidf("nil Facade") 46 } 47 if !utils.IsValidUUIDString(config.Model) { 48 return errors.NotValidf("Model %q", config.Model) 49 } 50 if config.Check == nil { 51 return errors.NotValidf("nil Check") 52 } 53 return nil 54 } 55 56 // New returns a Worker that tracks the result of the configured 57 // Check on the Model's migration phase, as exposed by the Facade. 58 func New(config Config) (*Worker, error) { 59 if err := config.Validate(); err != nil { 60 return nil, errors.Trace(err) 61 } 62 phase, err := config.Facade.Phase(config.Model) 63 if err != nil { 64 return nil, errors.Trace(err) 65 } 66 67 w := &Worker{ 68 config: config, 69 phase: phase, 70 } 71 err = catacomb.Invoke(catacomb.Plan{ 72 Site: &w.catacomb, 73 Work: w.loop, 74 }) 75 if err != nil { 76 return nil, errors.Trace(err) 77 } 78 return w, nil 79 } 80 81 // Worker implements worker.Worker and util.Flag, and exits 82 // with ErrChanged whenever the result of its configured Check of 83 // the Model's migration phase changes. 84 type Worker struct { 85 catacomb catacomb.Catacomb 86 config Config 87 phase migration.Phase 88 } 89 90 // Kill is part of the worker.Worker interface. 91 func (w *Worker) Kill() { 92 w.catacomb.Kill(nil) 93 } 94 95 // Wait is part of the worker.Worker interface. 96 func (w *Worker) Wait() error { 97 return w.catacomb.Wait() 98 } 99 100 // Check is part of the util.Flag interface. 101 func (w *Worker) Check() bool { 102 return w.config.Check(w.phase) 103 } 104 105 func (w *Worker) loop() error { 106 model := w.config.Model 107 facade := w.config.Facade 108 watcher, err := facade.Watch(model) 109 if err != nil { 110 return errors.Trace(err) 111 } 112 if err := w.catacomb.Add(watcher); err != nil { 113 return errors.Trace(err) 114 } 115 for { 116 select { 117 case <-w.catacomb.Dying(): 118 return w.catacomb.ErrDying() 119 case <-watcher.Changes(): 120 phase, err := facade.Phase(model) 121 if err != nil { 122 return errors.Trace(err) 123 } 124 if w.Check() != w.config.Check(phase) { 125 return ErrChanged 126 } 127 } 128 } 129 }