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  }