github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/machineactions/worker.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Copyright 2016 Cloudbase Solutions 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package machineactions 6 7 import ( 8 "github.com/juju/errors" 9 "github.com/juju/juju/api/machineactions" 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/watcher" 12 "github.com/juju/juju/worker" 13 "github.com/juju/loggo" 14 "gopkg.in/juju/names.v2" 15 ) 16 17 var logger = loggo.GetLogger("juju.worker.machineactions") 18 19 // Facade defines the capabilities required by the worker from the API. 20 type Facade interface { 21 WatchActionNotifications(agent names.MachineTag) (watcher.StringsWatcher, error) 22 RunningActions(agent names.MachineTag) ([]params.ActionResult, error) 23 24 Action(names.ActionTag) (*machineactions.Action, error) 25 ActionBegin(names.ActionTag) error 26 ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error 27 } 28 29 // WorkerConfig defines the worker's dependencies. 30 type WorkerConfig struct { 31 Facade Facade 32 MachineTag names.MachineTag 33 HandleAction func(name string, params map[string]interface{}) (results map[string]interface{}, err error) 34 } 35 36 // Validate returns an error if the configuration is not complete. 37 func (c WorkerConfig) Validate() error { 38 if c.Facade == nil { 39 return errors.NotValidf("nil Facade") 40 } 41 if c.MachineTag == (names.MachineTag{}) { 42 return errors.NotValidf("unspecified MachineTag") 43 } 44 if c.HandleAction == nil { 45 return errors.NotValidf("nil HandleAction") 46 } 47 return nil 48 } 49 50 // NewMachineActionsWorker returns a worker.Worker that watches for actions 51 // enqueued on this machine and tries to execute them. 52 func NewMachineActionsWorker(config WorkerConfig) (worker.Worker, error) { 53 if err := config.Validate(); err != nil { 54 return nil, errors.Trace(err) 55 } 56 swConfig := watcher.StringsConfig{ 57 Handler: &handler{config}, 58 } 59 return watcher.NewStringsWorker(swConfig) 60 } 61 62 // handler implements watcher.StringsHandler 63 type handler struct { 64 config WorkerConfig 65 } 66 67 // SetUp is part of the watcher.StringsHandler interface. 68 func (h *handler) SetUp() (watcher.StringsWatcher, error) { 69 actions, err := h.config.Facade.RunningActions(h.config.MachineTag) 70 if err != nil { 71 return nil, errors.Trace(err) 72 } 73 // We try to cancel any running action before starting up so actions don't linger around 74 // We *should* really have only one action coming up here if the execution is serial but 75 // this is best effort anyway. 76 for _, action := range actions { 77 tag, err := names.ParseActionTag(action.Action.Tag) 78 if err != nil { 79 logger.Infof("tried to cancel action %s but failed with error %v", action.Action.Tag, err) 80 continue 81 } 82 err = h.config.Facade.ActionFinish(tag, params.ActionFailed, nil, "action cancelled") 83 if err != nil { 84 logger.Infof("tried to cancel action %s but failed with error %v", action.Action.Tag, err) 85 } 86 } 87 return h.config.Facade.WatchActionNotifications(h.config.MachineTag) 88 } 89 90 // Handle is part of the watcher.StringsHandler interface. 91 // It should give us any actions currently enqueued for this machine. 92 // We try to execute every action before returning 93 func (h *handler) Handle(_ <-chan struct{}, actionsSlice []string) error { 94 for _, actionId := range actionsSlice { 95 ok := names.IsValidAction(actionId) 96 if !ok { 97 return errors.Errorf("got invalid action id %s", actionId) 98 } 99 100 actionTag := names.NewActionTag(actionId) 101 action, err := h.config.Facade.Action(actionTag) 102 if err != nil { 103 return errors.Annotatef(err, "could not retrieve action %s", actionId) 104 } 105 106 err = h.config.Facade.ActionBegin(actionTag) 107 if err != nil { 108 return errors.Annotatef(err, "could not begin action %s", action.Name()) 109 } 110 111 // We try to handle the action. The result returned from handling the action is 112 // sent through using ActionFinish. We only stop the loop if ActionFinish fails. 113 var finishErr error 114 results, err := h.config.HandleAction(action.Name(), action.Params()) 115 if err != nil { 116 finishErr = h.config.Facade.ActionFinish(actionTag, params.ActionFailed, nil, err.Error()) 117 } else { 118 finishErr = h.config.Facade.ActionFinish(actionTag, params.ActionCompleted, results, "") 119 } 120 if finishErr != nil { 121 return errors.Trace(finishErr) 122 } 123 } 124 return nil 125 } 126 127 // TearDown is part of the watcher.NotifyHandler interface. 128 func (h *handler) TearDown() error { 129 // Nothing to cleanup, only state is the watcher 130 return nil 131 }