github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/environ.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package worker 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "launchpad.net/tomb" 12 13 apiwatcher "github.com/juju/juju/api/watcher" 14 "github.com/juju/juju/environs" 15 "github.com/juju/juju/environs/config" 16 "github.com/juju/juju/state/watcher" 17 ) 18 19 var ErrTerminateAgent = errors.New("agent should be terminated") 20 var ErrRebootMachine = errors.New("machine needs to reboot") 21 var ErrShutdownMachine = errors.New("machine needs to shutdown") 22 23 var loadedInvalid = func() {} 24 25 var logger = loggo.GetLogger("juju.worker") 26 27 // EnvironConfigGetter interface defines a way to read the environment 28 // configuration. 29 type EnvironConfigGetter interface { 30 EnvironConfig() (*config.Config, error) 31 } 32 33 // TODO(rog) remove WaitForEnviron, as we now should always 34 // start with a valid environ config. 35 36 // WaitForEnviron waits for an valid environment to arrive from 37 // the given watcher. It terminates with tomb.ErrDying if 38 // it receives a value on dying. 39 func WaitForEnviron(w apiwatcher.NotifyWatcher, st EnvironConfigGetter, dying <-chan struct{}) (environs.Environ, error) { 40 for { 41 select { 42 case <-dying: 43 return nil, tomb.ErrDying 44 case _, ok := <-w.Changes(): 45 if !ok { 46 return nil, watcher.EnsureErr(w) 47 } 48 config, err := st.EnvironConfig() 49 if err != nil { 50 return nil, err 51 } 52 environ, err := environs.New(config) 53 if err == nil { 54 return environ, nil 55 } 56 logger.Errorf("loaded invalid environment configuration: %v", err) 57 loadedInvalid() 58 } 59 } 60 } 61 62 // EnvironConfigObserver interface defines a way to read the 63 // environment configuration and watch for changes. 64 type EnvironConfigObserver interface { 65 EnvironConfigGetter 66 WatchForEnvironConfigChanges() (apiwatcher.NotifyWatcher, error) 67 } 68 69 // EnvironObserver watches the current environment configuration 70 // and makes it available. It discards invalid environment 71 // configurations. 72 type EnvironObserver struct { 73 tomb tomb.Tomb 74 environWatcher apiwatcher.NotifyWatcher 75 st EnvironConfigObserver 76 mu sync.Mutex 77 environ environs.Environ 78 } 79 80 // NewEnvironObserver waits for the environment to have a valid 81 // environment configuration and returns a new environment observer. 82 // While waiting for the first environment configuration, it will 83 // return with tomb.ErrDying if it receives a value on dying. 84 func NewEnvironObserver(st EnvironConfigObserver) (*EnvironObserver, error) { 85 config, err := st.EnvironConfig() 86 if err != nil { 87 return nil, err 88 } 89 environ, err := environs.New(config) 90 if err != nil { 91 return nil, errors.Annotate(err, "cannot create an environment") 92 } 93 environWatcher, err := st.WatchForEnvironConfigChanges() 94 if err != nil { 95 return nil, errors.Annotate(err, "cannot watch environment config") 96 } 97 obs := &EnvironObserver{ 98 st: st, 99 environ: environ, 100 environWatcher: environWatcher, 101 } 102 go func() { 103 defer obs.tomb.Done() 104 defer watcher.Stop(environWatcher, &obs.tomb) 105 obs.tomb.Kill(obs.loop()) 106 }() 107 return obs, nil 108 } 109 110 func (obs *EnvironObserver) loop() error { 111 for { 112 select { 113 case <-obs.tomb.Dying(): 114 return nil 115 case _, ok := <-obs.environWatcher.Changes(): 116 if !ok { 117 return watcher.EnsureErr(obs.environWatcher) 118 } 119 } 120 config, err := obs.st.EnvironConfig() 121 if err != nil { 122 logger.Warningf("error reading environment config: %v", err) 123 continue 124 } 125 environ, err := environs.New(config) 126 if err != nil { 127 logger.Warningf("error creating an environment: %v", err) 128 continue 129 } 130 obs.mu.Lock() 131 obs.environ = environ 132 obs.mu.Unlock() 133 } 134 } 135 136 // Environ returns the most recent valid Environ. 137 func (obs *EnvironObserver) Environ() environs.Environ { 138 obs.mu.Lock() 139 defer obs.mu.Unlock() 140 return obs.environ 141 } 142 143 func (obs *EnvironObserver) Kill() { 144 obs.tomb.Kill(nil) 145 } 146 147 func (obs *EnvironObserver) Wait() error { 148 return obs.tomb.Wait() 149 }