github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 RequiresMachineLock 24 } 25 26 // String is part of the Operation interface. 27 func (ra *runAction) String() string { 28 return fmt.Sprintf("run action %s", ra.actionId) 29 } 30 31 // Prepare ensures that the action is valid and can be executed. If not, it 32 // will return ErrSkipExecute. It preserves any hook recorded in the supplied 33 // state. 34 // Prepare is part of the Operation interface. 35 func (ra *runAction) Prepare(state State) (*State, error) { 36 rnr, err := ra.runnerFactory.NewActionRunner(ra.actionId) 37 if cause := errors.Cause(err); runner.IsBadActionError(cause) { 38 if err := ra.callbacks.FailAction(ra.actionId, err.Error()); err != nil { 39 return nil, err 40 } 41 return nil, ErrSkipExecute 42 } else if cause == runner.ErrActionNotAvailable { 43 return nil, ErrSkipExecute 44 } else if err != nil { 45 return nil, errors.Annotatef(err, "cannot create runner for action %q", ra.actionId) 46 } 47 actionData, err := rnr.Context().ActionData() 48 if err != nil { 49 // this should *really* never happen, but let's not panic 50 return nil, errors.Trace(err) 51 } 52 err = rnr.Context().Prepare() 53 if err != nil { 54 return nil, errors.Trace(err) 55 } 56 ra.name = actionData.Name 57 ra.runner = rnr 58 return stateChange{ 59 Kind: RunAction, 60 Step: Pending, 61 ActionId: &ra.actionId, 62 Hook: state.Hook, 63 }.apply(state), nil 64 } 65 66 // Execute runs the action, and preserves any hook recorded in the supplied state. 67 // Execute is part of the Operation interface. 68 func (ra *runAction) Execute(state State) (*State, error) { 69 message := fmt.Sprintf("running action %s", ra.name) 70 71 if err := ra.callbacks.SetExecutingStatus(message); err != nil { 72 return nil, err 73 } 74 75 err := ra.runner.RunAction(ra.name) 76 if err != nil { 77 // This indicates an actual error -- an action merely failing should 78 // be handled inside the Runner, and returned as nil. 79 return nil, errors.Annotatef(err, "running action %q", ra.name) 80 } 81 return stateChange{ 82 Kind: RunAction, 83 Step: Done, 84 ActionId: &ra.actionId, 85 Hook: state.Hook, 86 }.apply(state), nil 87 } 88 89 // Commit preserves the recorded hook, and returns a neutral state. 90 // Commit is part of the Operation interface. 91 func (ra *runAction) Commit(state State) (*State, error) { 92 return stateChange{ 93 Kind: continuationKind(state), 94 Step: Pending, 95 Hook: state.Hook, 96 }.apply(state), nil 97 } 98 99 // continuationKind determines what State Kind the operation 100 // should return after Commit. 101 func continuationKind(state State) Kind { 102 switch { 103 case state.Hook != nil: 104 return RunHook 105 default: 106 return Continue 107 } 108 }