github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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 "github.com/juju/juju/environs" 15 "github.com/juju/juju/environs/config" 16 "github.com/juju/juju/state" 17 apiwatcher "github.com/juju/juju/state/api/watcher" 18 "github.com/juju/juju/state/watcher" 19 ) 20 21 var ErrTerminateAgent = errors.New("agent should be terminated") 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.MustErr(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 // EnvironObserver watches the current environment configuration 63 // and makes it available. It discards invalid environment 64 // configurations. 65 type EnvironObserver struct { 66 tomb tomb.Tomb 67 environWatcher state.NotifyWatcher 68 st *state.State 69 mu sync.Mutex 70 environ environs.Environ 71 } 72 73 // NewEnvironObserver waits for the state to have a valid environment 74 // configuration and returns a new environment observer. While waiting 75 // for the first environment configuration, it will return with 76 // tomb.ErrDying if it receives a value on dying. 77 func NewEnvironObserver(st *state.State) (*EnvironObserver, error) { 78 config, err := st.EnvironConfig() 79 if err != nil { 80 return nil, err 81 } 82 environ, err := environs.New(config) 83 if err != nil { 84 return nil, fmt.Errorf("cannot make Environ: %v", err) 85 } 86 environWatcher := st.WatchForEnvironConfigChanges() 87 obs := &EnvironObserver{ 88 st: st, 89 environ: environ, 90 environWatcher: environWatcher, 91 } 92 go func() { 93 defer obs.tomb.Done() 94 defer watcher.Stop(environWatcher, &obs.tomb) 95 obs.tomb.Kill(obs.loop()) 96 }() 97 return obs, nil 98 } 99 100 func (obs *EnvironObserver) loop() error { 101 for { 102 select { 103 case <-obs.tomb.Dying(): 104 return nil 105 case _, ok := <-obs.environWatcher.Changes(): 106 if !ok { 107 return watcher.MustErr(obs.environWatcher) 108 } 109 } 110 config, err := obs.st.EnvironConfig() 111 if err != nil { 112 logger.Warningf("error reading environment config: %v", err) 113 continue 114 } 115 environ, err := environs.New(config) 116 if err != nil { 117 logger.Warningf("error creating Environ: %v", err) 118 continue 119 } 120 obs.mu.Lock() 121 obs.environ = environ 122 obs.mu.Unlock() 123 } 124 } 125 126 // Environ returns the most recent valid Environ. 127 func (obs *EnvironObserver) Environ() environs.Environ { 128 obs.mu.Lock() 129 defer obs.mu.Unlock() 130 return obs.environ 131 } 132 133 func (obs *EnvironObserver) Kill() { 134 obs.tomb.Kill(nil) 135 } 136 137 func (obs *EnvironObserver) Wait() error { 138 return obs.tomb.Wait() 139 }