github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/meterstatus/connected.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package meterstatus
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/charm.v6-unstable/hooks"
     9  
    10  	"github.com/juju/juju/api/meterstatus"
    11  	"github.com/juju/juju/watcher"
    12  	"github.com/juju/juju/worker"
    13  	"github.com/juju/juju/worker/uniter/runner/context"
    14  )
    15  
    16  // connectedStatusHandler implements the NotifyWatchHandler interface.
    17  type connectedStatusHandler struct {
    18  	config ConnectedConfig
    19  
    20  	code string
    21  	info string
    22  }
    23  
    24  // ConnectedConfig contains all the dependencies required to create a new connected status worker.
    25  type ConnectedConfig struct {
    26  	Runner    HookRunner
    27  	StateFile *StateFile
    28  	Status    meterstatus.MeterStatusClient
    29  }
    30  
    31  // Validate validates the config structure and returns an error on failure.
    32  func (c ConnectedConfig) Validate() error {
    33  	if c.Runner == nil {
    34  		return errors.New("hook runner not provided")
    35  	}
    36  	if c.StateFile == nil {
    37  		return errors.New("state file not provided")
    38  	}
    39  	if c.Status == nil {
    40  		return errors.New("meter status API client not provided")
    41  	}
    42  	return nil
    43  }
    44  
    45  // NewConnectedStatusWorker creates a new worker that monitors the meter status of the
    46  // unit and runs the meter-status-changed hook appropriately.
    47  func NewConnectedStatusWorker(cfg ConnectedConfig) (worker.Worker, error) {
    48  	handler, err := NewConnectedStatusHandler(cfg)
    49  	if err != nil {
    50  		return nil, errors.Trace(err)
    51  	}
    52  	return watcher.NewNotifyWorker(watcher.NotifyConfig{
    53  		Handler: handler,
    54  	})
    55  }
    56  
    57  // NewConnectedStatusHandler creates a new meter status handler for handling meter status
    58  // changes as provided by the API.
    59  func NewConnectedStatusHandler(cfg ConnectedConfig) (watcher.NotifyHandler, error) {
    60  	if err := cfg.Validate(); err != nil {
    61  		return nil, errors.Trace(err)
    62  	}
    63  
    64  	w := &connectedStatusHandler{
    65  		config: cfg,
    66  	}
    67  	return w, nil
    68  }
    69  
    70  // SetUp is part of the worker.NotifyWatchHandler interface.
    71  func (w *connectedStatusHandler) SetUp() (watcher.NotifyWatcher, error) {
    72  	var err error
    73  	w.code, w.info, _, err = w.config.StateFile.Read()
    74  	if err != nil {
    75  		return nil, errors.Trace(err)
    76  	}
    77  
    78  	return w.config.Status.WatchMeterStatus()
    79  }
    80  
    81  // TearDown is part of the worker.NotifyWatchHandler interface.
    82  func (w *connectedStatusHandler) TearDown() error {
    83  	return nil
    84  }
    85  
    86  // Handle is part of the worker.NotifyWatchHandler interface.
    87  func (w *connectedStatusHandler) Handle(abort <-chan struct{}) error {
    88  	logger.Debugf("got meter status change signal from watcher")
    89  	currentCode, currentInfo, err := w.config.Status.MeterStatus()
    90  	if err != nil {
    91  		return errors.Trace(err)
    92  	}
    93  	if currentCode == w.code && currentInfo == w.info {
    94  		logger.Tracef("meter status (%q, %q) matches stored information (%q, %q), skipping", currentCode, currentInfo, w.code, w.info)
    95  		return nil
    96  	}
    97  	w.applyStatus(currentCode, currentInfo, abort)
    98  	w.code, w.info = currentCode, currentInfo
    99  	err = w.config.StateFile.Write(w.code, w.info, nil)
   100  	if err != nil {
   101  		return errors.Annotate(err, "failed to record meter status worker state")
   102  	}
   103  	return nil
   104  }
   105  
   106  func (w *connectedStatusHandler) applyStatus(code, info string, abort <-chan struct{}) {
   107  	logger.Tracef("applying meter status change: %q (%q)", code, info)
   108  	err := w.config.Runner.RunHook(code, info, abort)
   109  	cause := errors.Cause(err)
   110  	switch {
   111  	case context.IsMissingHookError(cause):
   112  		logger.Infof("skipped %q hook (missing)", string(hooks.MeterStatusChanged))
   113  	case err != nil:
   114  		logger.Errorf("meter status worker encountered hook error: %v", err)
   115  	}
   116  }