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  }