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