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