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  }