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