github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 ra.name = actionData.ActionName 53 ra.runner = rnr 54 return stateChange{ 55 Kind: RunAction, 56 Step: Pending, 57 ActionId: &ra.actionId, 58 Hook: state.Hook, 59 }.apply(state), nil 60 } 61 62 // Execute runs the action, and preserves any hook recorded in the supplied state. 63 // Execute is part of the Operation interface. 64 func (ra *runAction) Execute(state State) (*State, error) { 65 message := fmt.Sprintf("running action %s", ra.name) 66 67 if err := ra.callbacks.SetExecutingStatus(message); err != nil { 68 return nil, err 69 } 70 71 err := ra.runner.RunAction(ra.name) 72 if err != nil { 73 // This indicates an actual error -- an action merely failing should 74 // be handled inside the Runner, and returned as nil. 75 return nil, errors.Annotatef(err, "running action %q", ra.name) 76 } 77 return stateChange{ 78 Kind: RunAction, 79 Step: Done, 80 ActionId: &ra.actionId, 81 Hook: state.Hook, 82 }.apply(state), nil 83 } 84 85 // Commit preserves the recorded hook, and returns a neutral state. 86 // Commit is part of the Operation interface. 87 func (ra *runAction) Commit(state State) (*State, error) { 88 return stateChange{ 89 Kind: continuationKind(state), 90 Step: Pending, 91 Hook: state.Hook, 92 }.apply(state), nil 93 } 94 95 // continuationKind determines what State Kind the operation 96 // should return after Commit. 97 func continuationKind(state State) Kind { 98 switch { 99 case state.Hook != nil: 100 return RunHook 101 default: 102 return Continue 103 } 104 }