github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  }