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  }