github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/uniter/operation/executor.go (about)

     1  // Copyright 2014-2015 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  	corecharm "gopkg.in/juju/charm.v6-unstable"
    11  )
    12  
    13  type executorStep struct {
    14  	verb string
    15  	run  func(op Operation, state State) (*State, error)
    16  }
    17  
    18  func (step executorStep) message(op Operation) string {
    19  	return fmt.Sprintf("%s operation %q", step.verb, op)
    20  }
    21  
    22  var (
    23  	stepPrepare = executorStep{"preparing", Operation.Prepare}
    24  	stepExecute = executorStep{"executing", Operation.Execute}
    25  	stepCommit  = executorStep{"committing", Operation.Commit}
    26  )
    27  
    28  type executor struct {
    29  	file               *StateFile
    30  	state              *State
    31  	acquireMachineLock func(string) (func() error, error)
    32  }
    33  
    34  // NewExecutor returns an Executor which takes its starting state from the
    35  // supplied path, and records state changes there. If no state file exists,
    36  // the executor's starting state will include a queued Install hook, for
    37  // the charm identified by the supplied func.
    38  func NewExecutor(stateFilePath string, getInstallCharm func() (*corecharm.URL, error), acquireLock func(string) (func() error, error)) (Executor, error) {
    39  	file := NewStateFile(stateFilePath)
    40  	state, err := file.Read()
    41  	if err == ErrNoStateFile {
    42  		charmURL, err := getInstallCharm()
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		state = &State{
    47  			Kind:     Install,
    48  			Step:     Queued,
    49  			CharmURL: charmURL,
    50  		}
    51  	} else if err != nil {
    52  		return nil, err
    53  	}
    54  	return &executor{
    55  		file:               file,
    56  		state:              state,
    57  		acquireMachineLock: acquireLock,
    58  	}, nil
    59  }
    60  
    61  // State is part of the Executor interface.
    62  func (x *executor) State() State {
    63  	return *x.state
    64  }
    65  
    66  // Run is part of the Executor interface.
    67  func (x *executor) Run(op Operation) (runErr error) {
    68  	logger.Debugf("running operation %v", op)
    69  
    70  	if op.NeedsGlobalMachineLock() {
    71  		unlock, err := x.acquireMachineLock(fmt.Sprintf("executing operation: %s", op.String()))
    72  		if err != nil {
    73  			return errors.Annotate(err, "could not acquire lock")
    74  		}
    75  		// There is nothing theoretically stopping us from unlocking
    76  		// between execute and commit, but since we're not looking for the
    77  		// efficiency provided by that right now, we prefer to keep the logic
    78  		// simple. This could be changed in the future.
    79  		defer func() {
    80  			unlockErr := unlock()
    81  			if unlockErr != nil {
    82  				logger.Errorf("operation failed with error %v; error overriden by unlock failure error", runErr)
    83  				runErr = unlockErr
    84  			}
    85  		}()
    86  	}
    87  
    88  	switch err := x.do(op, stepPrepare); errors.Cause(err) {
    89  	case ErrSkipExecute:
    90  	case nil:
    91  		if err := x.do(op, stepExecute); err != nil {
    92  			return err
    93  		}
    94  	default:
    95  		return err
    96  	}
    97  	return x.do(op, stepCommit)
    98  }
    99  
   100  // Skip is part of the Executor interface.
   101  func (x *executor) Skip(op Operation) error {
   102  	logger.Debugf("skipping operation %v", op)
   103  	return x.do(op, stepCommit)
   104  }
   105  
   106  func (x *executor) do(op Operation, step executorStep) (err error) {
   107  	message := step.message(op)
   108  	logger.Debugf(message)
   109  	newState, firstErr := step.run(op, *x.state)
   110  	if newState != nil {
   111  		writeErr := x.writeState(*newState)
   112  		if firstErr == nil {
   113  			firstErr = writeErr
   114  		} else if writeErr != nil {
   115  			logger.Errorf("after %s: %v", message, writeErr)
   116  		}
   117  	}
   118  	return errors.Annotatef(firstErr, message)
   119  }
   120  
   121  func (x *executor) writeState(newState State) error {
   122  	if err := newState.validate(); err != nil {
   123  		return err
   124  	}
   125  	if err := x.file.Write(&newState); err != nil {
   126  		return errors.Annotatef(err, "writing state")
   127  	}
   128  	x.state = &newState
   129  	return nil
   130  }