github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/environ/environ.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package environ 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/worker.v1/catacomb" 10 11 "github.com/juju/juju/core/watcher" 12 "github.com/juju/juju/environs" 13 ) 14 15 var logger = loggo.GetLogger("juju.worker.environ") 16 17 // ConfigObserver exposes a model configuration and a watch constructor 18 // that allows clients to be informed of changes to the configuration. 19 type ConfigObserver interface { 20 environs.EnvironConfigGetter 21 WatchForModelConfigChanges() (watcher.NotifyWatcher, error) 22 } 23 24 // Config describes the dependencies of a Tracker. 25 // 26 // It's arguable that it should be called TrackerConfig, because of the heavy 27 // use of model config in this package. 28 type Config struct { 29 Observer ConfigObserver 30 NewEnvironFunc environs.NewEnvironFunc 31 } 32 33 // Validate returns an error if the config cannot be used to start a Tracker. 34 func (config Config) Validate() error { 35 if config.Observer == nil { 36 return errors.NotValidf("nil Observer") 37 } 38 if config.NewEnvironFunc == nil { 39 return errors.NotValidf("nil NewEnvironFunc") 40 } 41 return nil 42 } 43 44 // Tracker loads an environment, makes it available to clients, and updates 45 // the environment in response to config changes until it is killed. 46 type Tracker struct { 47 config Config 48 catacomb catacomb.Catacomb 49 environ environs.Environ 50 } 51 52 // NewTracker loads an environment from the observer and returns a new Tracker, 53 // or an error if anything goes wrong. If a tracker is returned, its Environ() 54 // method is immediately usable. 55 // 56 // The caller is responsible for Kill()ing the returned Tracker and Wait()ing 57 // for any errors it might return. 58 func NewTracker(config Config) (*Tracker, error) { 59 if err := config.Validate(); err != nil { 60 return nil, errors.Trace(err) 61 } 62 environ, err := environs.GetEnviron(config.Observer, config.NewEnvironFunc) 63 if err != nil { 64 return nil, errors.Annotate(err, "cannot create environ") 65 } 66 67 t := &Tracker{ 68 config: config, 69 environ: environ, 70 } 71 err = catacomb.Invoke(catacomb.Plan{ 72 Site: &t.catacomb, 73 Work: t.loop, 74 }) 75 if err != nil { 76 return nil, errors.Trace(err) 77 } 78 return t, nil 79 } 80 81 // Environ returns the encapsulated Environ. It will continue to be updated in 82 // the background for as long as the Tracker continues to run. 83 func (t *Tracker) Environ() environs.Environ { 84 return t.environ 85 } 86 87 func (t *Tracker) loop() error { 88 environWatcher, err := t.config.Observer.WatchForModelConfigChanges() 89 if err != nil { 90 return errors.Annotate(err, "cannot watch environ config") 91 } 92 if err := t.catacomb.Add(environWatcher); err != nil { 93 return errors.Trace(err) 94 } 95 for { 96 logger.Debugf("waiting for environ watch notification") 97 select { 98 case <-t.catacomb.Dying(): 99 return t.catacomb.ErrDying() 100 case _, ok := <-environWatcher.Changes(): 101 if !ok { 102 return errors.New("environ config watch closed") 103 } 104 } 105 logger.Debugf("reloading environ config") 106 modelConfig, err := t.config.Observer.ModelConfig() 107 if err != nil { 108 return errors.Annotate(err, "cannot read environ config") 109 } 110 if err = t.environ.SetConfig(modelConfig); err != nil { 111 return errors.Annotate(err, "cannot update environ config") 112 } 113 } 114 } 115 116 // Kill is part of the worker.Worker interface. 117 func (t *Tracker) Kill() { 118 t.catacomb.Kill(nil) 119 } 120 121 // Wait is part of the worker.Worker interface. 122 func (t *Tracker) Wait() error { 123 return t.catacomb.Wait() 124 }