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 }