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