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