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