github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/environ/environ.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environ
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/environs"
    11  	"github.com/juju/juju/watcher"
    12  	"github.com/juju/juju/worker/catacomb"
    13  )
    14  
    15  // ConfigObserver exposes a model configuration and a watch constructor
    16  // that allows clients to be informed of changes to the configuration.
    17  type ConfigObserver interface {
    18  	environs.EnvironConfigGetter
    19  	WatchForModelConfigChanges() (watcher.NotifyWatcher, error)
    20  	WatchCredential(tag names.CloudCredentialTag) (watcher.NotifyWatcher, error)
    21  }
    22  
    23  // Config describes the dependencies of a Tracker.
    24  //
    25  // It's arguable that it should be called TrackerConfig, because of the heavy
    26  // use of model config in this package.
    27  type Config struct {
    28  	Observer       ConfigObserver
    29  	NewEnvironFunc environs.NewEnvironFunc
    30  }
    31  
    32  // Validate returns an error if the config cannot be used to start a Tracker.
    33  func (config Config) Validate() error {
    34  	if config.Observer == nil {
    35  		return errors.NotValidf("nil Observer")
    36  	}
    37  	if config.NewEnvironFunc == nil {
    38  		return errors.NotValidf("nil NewEnvironFunc")
    39  	}
    40  	return nil
    41  }
    42  
    43  // Tracker loads an environment, makes it available to clients, and updates
    44  // the environment in response to config changes until it is killed.
    45  type Tracker struct {
    46  	config   Config
    47  	catacomb catacomb.Catacomb
    48  	environ  environs.Environ
    49  }
    50  
    51  // NewTracker loads an environment from the observer and returns a new Tracker,
    52  // or an error if anything goes wrong. If a tracker is returned, its Environ()
    53  // method is immediately usable.
    54  //
    55  // The caller is responsible for Kill()ing the returned Tracker and Wait()ing
    56  // for any errors it might return.
    57  func NewTracker(config Config) (*Tracker, error) {
    58  	if err := config.Validate(); err != nil {
    59  		return nil, errors.Trace(err)
    60  	}
    61  	environ, err := environs.GetEnviron(config.Observer, config.NewEnvironFunc)
    62  	if err != nil {
    63  		return nil, errors.Annotate(err, "cannot create environ")
    64  	}
    65  
    66  	t := &Tracker{
    67  		config:  config,
    68  		environ: environ,
    69  	}
    70  	err = catacomb.Invoke(catacomb.Plan{
    71  		Site: &t.catacomb,
    72  		Work: t.loop,
    73  	})
    74  	if err != nil {
    75  		return nil, errors.Trace(err)
    76  	}
    77  	return t, nil
    78  }
    79  
    80  // Environ returns the encapsulated Environ. It will continue to be updated in
    81  // the background for as long as the Tracker continues to run.
    82  func (t *Tracker) Environ() environs.Environ {
    83  	return t.environ
    84  }
    85  
    86  func (t *Tracker) loop() error {
    87  	environWatcher, err := t.config.Observer.WatchForModelConfigChanges()
    88  	if err != nil {
    89  		return errors.Annotate(err, "cannot watch environ config")
    90  	}
    91  	if err := t.catacomb.Add(environWatcher); err != nil {
    92  		return errors.Trace(err)
    93  	}
    94  	for {
    95  		logger.Debugf("waiting for environ watch notification")
    96  		select {
    97  		case <-t.catacomb.Dying():
    98  			return t.catacomb.ErrDying()
    99  		case _, ok := <-environWatcher.Changes():
   100  			if !ok {
   101  				return errors.New("environ config watch closed")
   102  			}
   103  		}
   104  		logger.Debugf("reloading environ config")
   105  		modelConfig, err := t.config.Observer.ModelConfig()
   106  		if err != nil {
   107  			return errors.Annotate(err, "cannot read environ config")
   108  		}
   109  		if err = t.environ.SetConfig(modelConfig); err != nil {
   110  			return errors.Annotate(err, "cannot update environ config")
   111  		}
   112  	}
   113  }
   114  
   115  // Kill is part of the worker.Worker interface.
   116  func (t *Tracker) Kill() {
   117  	t.catacomb.Kill(nil)
   118  }
   119  
   120  // Wait is part of the worker.Worker interface.
   121  func (t *Tracker) Wait() error {
   122  	return t.catacomb.Wait()
   123  }