github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/operation/runaction.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package operation 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 11 "github.com/juju/juju/worker/uniter/runner" 12 ) 13 14 type runAction struct { 15 actionId string 16 17 callbacks Callbacks 18 runnerFactory runner.Factory 19 20 name string 21 runner runner.Runner 22 } 23 24 // String is part of the Operation interface. 25 func (ra *runAction) String() string { 26 return fmt.Sprintf("run action %s", ra.actionId) 27 } 28 29 // Prepare ensures that the action is valid and can be executed. If not, it 30 // will return ErrSkipExecute. It preserves any hook recorded in the supplied 31 // state. 32 // Prepare is part of the Operation interface. 33 func (ra *runAction) Prepare(state State) (*State, error) { 34 rnr, err := ra.runnerFactory.NewActionRunner(ra.actionId) 35 if cause := errors.Cause(err); runner.IsBadActionError(cause) { 36 if err := ra.callbacks.FailAction(ra.actionId, err.Error()); err != nil { 37 return nil, err 38 } 39 return nil, ErrSkipExecute 40 } else if cause == runner.ErrActionNotAvailable { 41 return nil, ErrSkipExecute 42 } else if err != nil { 43 return nil, errors.Annotatef(err, "cannot create runner for action %q", ra.actionId) 44 } 45 actionData, err := rnr.Context().ActionData() 46 if err != nil { 47 // this should *really* never happen, but let's not panic 48 return nil, errors.Trace(err) 49 } 50 ra.name = actionData.ActionName 51 ra.runner = rnr 52 return stateChange{ 53 Kind: RunAction, 54 Step: Pending, 55 ActionId: &ra.actionId, 56 Hook: state.Hook, 57 }.apply(state), nil 58 } 59 60 // Execute runs the action, and preserves any hook recorded in the supplied state. 61 // Execute is part of the Operation interface. 62 func (ra *runAction) Execute(state State) (*State, error) { 63 message := fmt.Sprintf("running action %s", ra.name) 64 unlock, err := ra.callbacks.AcquireExecutionLock(message) 65 if err != nil { 66 return nil, err 67 } 68 defer unlock() 69 70 err = ra.runner.RunAction(ra.name) 71 if err != nil { 72 // This indicates an actual error -- an action merely failing should 73 // be handled inside the Runner, and returned as nil. 74 return nil, errors.Annotatef(err, "running action %q", ra.name) 75 } 76 return stateChange{ 77 Kind: RunAction, 78 Step: Done, 79 ActionId: &ra.actionId, 80 Hook: state.Hook, 81 }.apply(state), nil 82 } 83 84 // Commit preserves the recorded hook, and returns a neutral state. 85 // Commit is part of the Operation interface. 86 func (ra *runAction) Commit(state State) (*State, error) { 87 return stateChange{ 88 Kind: Continue, 89 Step: Pending, 90 Hook: state.Hook, 91 }.apply(state), nil 92 }