github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/uniter/operation/executor_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation_test
     5  
     6  import (
     7  	"path/filepath"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	ft "github.com/juju/testing/filetesting"
    13  	gc "gopkg.in/check.v1"
    14  	corecharm "gopkg.in/juju/charm.v6-unstable"
    15  	"gopkg.in/juju/charm.v6-unstable/hooks"
    16  
    17  	"github.com/juju/juju/worker/uniter/hook"
    18  	"github.com/juju/juju/worker/uniter/operation"
    19  )
    20  
    21  type NewExecutorSuite struct {
    22  	testing.IsolationSuite
    23  	basePath string
    24  }
    25  
    26  var _ = gc.Suite(&NewExecutorSuite{})
    27  
    28  func failGetInstallCharm() (*corecharm.URL, error) {
    29  	return nil, errors.New("lol!")
    30  }
    31  
    32  func failAcquireLock(string) (func() error, error) {
    33  	return nil, errors.New("wat")
    34  }
    35  
    36  func (s *NewExecutorSuite) SetUpTest(c *gc.C) {
    37  	s.IsolationSuite.SetUpTest(c)
    38  	s.basePath = c.MkDir()
    39  }
    40  
    41  func (s *NewExecutorSuite) path(path string) string {
    42  	return filepath.Join(s.basePath, path)
    43  }
    44  
    45  func (s *NewExecutorSuite) TestNewExecutorNoFileNoCharm(c *gc.C) {
    46  	executor, err := operation.NewExecutor(s.path("missing"), failGetInstallCharm, failAcquireLock)
    47  	c.Assert(executor, gc.IsNil)
    48  	c.Assert(err, gc.ErrorMatches, "lol!")
    49  }
    50  
    51  func (s *NewExecutorSuite) TestNewExecutorInvalidFile(c *gc.C) {
    52  	ft.File{"existing", "", 0666}.Create(c, s.basePath)
    53  	executor, err := operation.NewExecutor(s.path("existing"), failGetInstallCharm, failAcquireLock)
    54  	c.Assert(executor, gc.IsNil)
    55  	c.Assert(err, gc.ErrorMatches, `cannot read ".*": invalid operation state: .*`)
    56  }
    57  
    58  func (s *NewExecutorSuite) TestNewExecutorNoFile(c *gc.C) {
    59  	charmURL := corecharm.MustParseURL("cs:quantal/nyancat-323")
    60  	getInstallCharm := func() (*corecharm.URL, error) {
    61  		return charmURL, nil
    62  	}
    63  	executor, err := operation.NewExecutor(s.path("missing"), getInstallCharm, failAcquireLock)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	c.Assert(executor.State(), gc.DeepEquals, operation.State{
    66  		Kind:     operation.Install,
    67  		Step:     operation.Queued,
    68  		CharmURL: charmURL,
    69  	})
    70  	ft.Removed{"missing"}.Check(c, s.basePath)
    71  }
    72  
    73  func (s *NewExecutorSuite) TestNewExecutorValidFile(c *gc.C) {
    74  	// note: this content matches valid persistent state as of 1.21; we expect
    75  	// that "hook" will have to become "last-hook" to enable action execution
    76  	// during hook error states. If you do this, please leave at least one test
    77  	// with this form of the yaml in place.
    78  	ft.File{"existing", `
    79  started: true
    80  op: continue
    81  opstep: pending
    82  `[1:], 0666}.Create(c, s.basePath)
    83  	executor, err := operation.NewExecutor(s.path("existing"), failGetInstallCharm, failAcquireLock)
    84  	c.Assert(err, jc.ErrorIsNil)
    85  	c.Assert(executor.State(), gc.DeepEquals, operation.State{
    86  		Kind:    operation.Continue,
    87  		Step:    operation.Pending,
    88  		Started: true,
    89  	})
    90  }
    91  
    92  type ExecutorSuite struct {
    93  	testing.IsolationSuite
    94  }
    95  
    96  var _ = gc.Suite(&ExecutorSuite{})
    97  
    98  func assertWroteState(c *gc.C, path string, expect operation.State) {
    99  	actual, err := operation.NewStateFile(path).Read()
   100  	c.Assert(err, jc.ErrorIsNil)
   101  	c.Assert(*actual, gc.DeepEquals, expect)
   102  }
   103  
   104  func newExecutor(c *gc.C, st *operation.State) (operation.Executor, string) {
   105  	path := filepath.Join(c.MkDir(), "state")
   106  	err := operation.NewStateFile(path).Write(st)
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	executor, err := operation.NewExecutor(path, failGetInstallCharm, failAcquireLock)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	return executor, path
   111  }
   112  
   113  func justInstalledState() operation.State {
   114  	return operation.State{
   115  		Kind: operation.Continue,
   116  		Step: operation.Pending,
   117  	}
   118  }
   119  
   120  func (s *ExecutorSuite) TestSucceedNoStateChanges(c *gc.C) {
   121  	initialState := justInstalledState()
   122  	executor, statePath := newExecutor(c, &initialState)
   123  
   124  	op := &mockOperation{
   125  		prepare: newStep(nil, nil),
   126  		execute: newStep(nil, nil),
   127  		commit:  newStep(nil, nil),
   128  	}
   129  
   130  	err := executor.Run(op)
   131  	c.Assert(err, jc.ErrorIsNil)
   132  
   133  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   134  	c.Assert(op.execute.gotState, gc.DeepEquals, initialState)
   135  	c.Assert(op.commit.gotState, gc.DeepEquals, initialState)
   136  	assertWroteState(c, statePath, initialState)
   137  	c.Assert(executor.State(), gc.DeepEquals, initialState)
   138  }
   139  
   140  func (s *ExecutorSuite) TestSucceedWithStateChanges(c *gc.C) {
   141  	initialState := justInstalledState()
   142  	executor, statePath := newExecutor(c, &initialState)
   143  	op := &mockOperation{
   144  		prepare: newStep(&operation.State{
   145  			Kind: operation.RunHook,
   146  			Step: operation.Pending,
   147  			Hook: &hook.Info{Kind: hooks.ConfigChanged},
   148  		}, nil),
   149  		execute: newStep(&operation.State{
   150  			Kind: operation.RunHook,
   151  			Step: operation.Done,
   152  			Hook: &hook.Info{Kind: hooks.ConfigChanged},
   153  		}, nil),
   154  		commit: newStep(&operation.State{
   155  			Kind: operation.RunHook,
   156  			Step: operation.Queued,
   157  			Hook: &hook.Info{Kind: hooks.Start},
   158  		}, nil),
   159  	}
   160  
   161  	err := executor.Run(op)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  
   164  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   165  	c.Assert(op.execute.gotState, gc.DeepEquals, *op.prepare.newState)
   166  	c.Assert(op.commit.gotState, gc.DeepEquals, *op.execute.newState)
   167  	assertWroteState(c, statePath, *op.commit.newState)
   168  	c.Assert(executor.State(), gc.DeepEquals, *op.commit.newState)
   169  }
   170  
   171  func (s *ExecutorSuite) TestErrSkipExecute(c *gc.C) {
   172  	initialState := justInstalledState()
   173  	executor, statePath := newExecutor(c, &initialState)
   174  	op := &mockOperation{
   175  		prepare: newStep(&operation.State{
   176  			Kind: operation.RunHook,
   177  			Step: operation.Pending,
   178  			Hook: &hook.Info{Kind: hooks.ConfigChanged},
   179  		}, operation.ErrSkipExecute),
   180  		commit: newStep(&operation.State{
   181  			Kind: operation.RunHook,
   182  			Step: operation.Queued,
   183  			Hook: &hook.Info{Kind: hooks.Start},
   184  		}, nil),
   185  	}
   186  
   187  	err := executor.Run(op)
   188  	c.Assert(err, jc.ErrorIsNil)
   189  
   190  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   191  	c.Assert(op.commit.gotState, gc.DeepEquals, *op.prepare.newState)
   192  	assertWroteState(c, statePath, *op.commit.newState)
   193  	c.Assert(executor.State(), gc.DeepEquals, *op.commit.newState)
   194  }
   195  
   196  func (s *ExecutorSuite) TestValidateStateChange(c *gc.C) {
   197  	initialState := justInstalledState()
   198  	executor, statePath := newExecutor(c, &initialState)
   199  	op := &mockOperation{
   200  		prepare: newStep(&operation.State{
   201  			Kind: operation.RunHook,
   202  			Step: operation.Pending,
   203  		}, nil),
   204  	}
   205  
   206  	err := executor.Run(op)
   207  	c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": invalid operation state: missing hook info with Kind RunHook`)
   208  	c.Assert(errors.Cause(err), gc.ErrorMatches, "missing hook info with Kind RunHook")
   209  
   210  	assertWroteState(c, statePath, initialState)
   211  	c.Assert(executor.State(), gc.DeepEquals, initialState)
   212  }
   213  
   214  func (s *ExecutorSuite) TestFailPrepareNoStateChange(c *gc.C) {
   215  	initialState := justInstalledState()
   216  	executor, statePath := newExecutor(c, &initialState)
   217  	op := &mockOperation{
   218  		prepare: newStep(nil, errors.New("pow")),
   219  	}
   220  
   221  	err := executor.Run(op)
   222  	c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": pow`)
   223  	c.Assert(errors.Cause(err), gc.ErrorMatches, "pow")
   224  
   225  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   226  	assertWroteState(c, statePath, initialState)
   227  	c.Assert(executor.State(), gc.DeepEquals, initialState)
   228  }
   229  
   230  func (s *ExecutorSuite) TestFailPrepareWithStateChange(c *gc.C) {
   231  	initialState := justInstalledState()
   232  	executor, statePath := newExecutor(c, &initialState)
   233  	op := &mockOperation{
   234  		prepare: newStep(&operation.State{
   235  			Kind: operation.RunHook,
   236  			Step: operation.Pending,
   237  			Hook: &hook.Info{Kind: hooks.Start},
   238  		}, errors.New("blam")),
   239  	}
   240  
   241  	err := executor.Run(op)
   242  	c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": blam`)
   243  	c.Assert(errors.Cause(err), gc.ErrorMatches, "blam")
   244  
   245  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   246  	assertWroteState(c, statePath, *op.prepare.newState)
   247  	c.Assert(executor.State(), gc.DeepEquals, *op.prepare.newState)
   248  }
   249  
   250  func (s *ExecutorSuite) TestFailExecuteNoStateChange(c *gc.C) {
   251  	initialState := justInstalledState()
   252  	executor, statePath := newExecutor(c, &initialState)
   253  	op := &mockOperation{
   254  		prepare: newStep(nil, nil),
   255  		execute: newStep(nil, errors.New("splat")),
   256  	}
   257  
   258  	err := executor.Run(op)
   259  	c.Assert(err, gc.ErrorMatches, `executing operation "mock operation": splat`)
   260  	c.Assert(errors.Cause(err), gc.ErrorMatches, "splat")
   261  
   262  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   263  	assertWroteState(c, statePath, initialState)
   264  	c.Assert(executor.State(), gc.DeepEquals, initialState)
   265  }
   266  
   267  func (s *ExecutorSuite) TestFailExecuteWithStateChange(c *gc.C) {
   268  	initialState := justInstalledState()
   269  	executor, statePath := newExecutor(c, &initialState)
   270  	op := &mockOperation{
   271  		prepare: newStep(nil, nil),
   272  		execute: newStep(&operation.State{
   273  			Kind: operation.RunHook,
   274  			Step: operation.Pending,
   275  			Hook: &hook.Info{Kind: hooks.Start},
   276  		}, errors.New("kerblooie")),
   277  	}
   278  
   279  	err := executor.Run(op)
   280  	c.Assert(err, gc.ErrorMatches, `executing operation "mock operation": kerblooie`)
   281  	c.Assert(errors.Cause(err), gc.ErrorMatches, "kerblooie")
   282  
   283  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   284  	assertWroteState(c, statePath, *op.execute.newState)
   285  	c.Assert(executor.State(), gc.DeepEquals, *op.execute.newState)
   286  }
   287  
   288  func (s *ExecutorSuite) TestFailCommitNoStateChange(c *gc.C) {
   289  	initialState := justInstalledState()
   290  	executor, statePath := newExecutor(c, &initialState)
   291  	op := &mockOperation{
   292  		prepare: newStep(nil, nil),
   293  		execute: newStep(nil, nil),
   294  		commit:  newStep(nil, errors.New("whack")),
   295  	}
   296  
   297  	err := executor.Run(op)
   298  	c.Assert(err, gc.ErrorMatches, `committing operation "mock operation": whack`)
   299  	c.Assert(errors.Cause(err), gc.ErrorMatches, "whack")
   300  
   301  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   302  	assertWroteState(c, statePath, initialState)
   303  	c.Assert(executor.State(), gc.DeepEquals, initialState)
   304  }
   305  
   306  func (s *ExecutorSuite) TestFailCommitWithStateChange(c *gc.C) {
   307  	initialState := justInstalledState()
   308  	executor, statePath := newExecutor(c, &initialState)
   309  	op := &mockOperation{
   310  		prepare: newStep(nil, nil),
   311  		execute: newStep(nil, nil),
   312  		commit: newStep(&operation.State{
   313  			Kind: operation.RunHook,
   314  			Step: operation.Pending,
   315  			Hook: &hook.Info{Kind: hooks.Start},
   316  		}, errors.New("take that you bandit")),
   317  	}
   318  
   319  	err := executor.Run(op)
   320  	c.Assert(err, gc.ErrorMatches, `committing operation "mock operation": take that you bandit`)
   321  	c.Assert(errors.Cause(err), gc.ErrorMatches, "take that you bandit")
   322  
   323  	c.Assert(op.prepare.gotState, gc.DeepEquals, initialState)
   324  	assertWroteState(c, statePath, *op.commit.newState)
   325  	c.Assert(executor.State(), gc.DeepEquals, *op.commit.newState)
   326  }
   327  
   328  func (s *ExecutorSuite) initLockTest(c *gc.C, lockFunc func(string) (func() error, error)) operation.Executor {
   329  
   330  	initialState := justInstalledState()
   331  	statePath := filepath.Join(c.MkDir(), "state")
   332  	err := operation.NewStateFile(statePath).Write(&initialState)
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	executor, err := operation.NewExecutor(statePath, failGetInstallCharm, lockFunc)
   335  	c.Assert(err, jc.ErrorIsNil)
   336  
   337  	return executor
   338  }
   339  
   340  func (s *ExecutorSuite) TestLockSucceedsStepsCalled(c *gc.C) {
   341  	op := &mockOperation{
   342  		needsLock: true,
   343  		prepare:   newStep(nil, nil),
   344  		execute:   newStep(nil, nil),
   345  		commit:    newStep(nil, nil),
   346  	}
   347  
   348  	mockLock := &mockLockFunc{op: op}
   349  	lockFunc := mockLock.newSucceedingLockUnlockSucceeds()
   350  	executor := s.initLockTest(c, lockFunc)
   351  
   352  	err := executor.Run(op)
   353  	c.Assert(err, jc.ErrorIsNil)
   354  
   355  	c.Assert(mockLock.calledLock, jc.IsTrue)
   356  	c.Assert(mockLock.calledUnlock, jc.IsTrue)
   357  	c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue)
   358  
   359  	expectedStepsOnUnlock := []bool{true, true, true}
   360  	c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock)
   361  }
   362  
   363  func (s *ExecutorSuite) TestLockSucceedsStepsCalledUnlockFails(c *gc.C) {
   364  	op := &mockOperation{
   365  		needsLock: true,
   366  		prepare:   newStep(nil, nil),
   367  		execute:   newStep(nil, nil),
   368  		commit:    newStep(nil, nil),
   369  	}
   370  
   371  	mockLock := &mockLockFunc{op: op}
   372  	lockFunc := mockLock.newSucceedingLockUnlockFails()
   373  	executor := s.initLockTest(c, lockFunc)
   374  
   375  	err := executor.Run(op)
   376  	c.Assert(err, gc.ErrorMatches, "but why")
   377  
   378  	c.Assert(mockLock.calledLock, jc.IsTrue)
   379  	c.Assert(mockLock.calledUnlock, jc.IsTrue)
   380  	c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue)
   381  
   382  	expectedStepsOnUnlock := []bool{true, true, true}
   383  	c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock)
   384  }
   385  
   386  func (s *ExecutorSuite) TestOpFailsUnlockFailsUnlockErrPropagated(c *gc.C) {
   387  	op := &mockOperation{
   388  		needsLock: true,
   389  		prepare:   newStep(nil, errors.New("kerblooie")),
   390  		execute:   newStep(nil, nil),
   391  		commit:    newStep(nil, nil),
   392  	}
   393  
   394  	mockLock := &mockLockFunc{op: op}
   395  	lockFunc := mockLock.newSucceedingLockUnlockFails()
   396  	executor := s.initLockTest(c, lockFunc)
   397  
   398  	err := executor.Run(op)
   399  	c.Assert(err, gc.ErrorMatches, "but why")
   400  
   401  	c.Assert(mockLock.calledLock, jc.IsTrue)
   402  	c.Assert(mockLock.calledUnlock, jc.IsTrue)
   403  	c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue)
   404  
   405  	expectedStepsOnUnlock := []bool{true, false, false}
   406  	c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock)
   407  }
   408  
   409  func (s *ExecutorSuite) TestLockFailsOpsStepsNotCalled(c *gc.C) {
   410  	op := &mockOperation{
   411  		needsLock: true,
   412  		prepare:   newStep(nil, nil),
   413  		execute:   newStep(nil, nil),
   414  		commit:    newStep(nil, nil),
   415  	}
   416  
   417  	mockLock := &mockLockFunc{op: op}
   418  	lockFunc := mockLock.newFailingLock()
   419  	executor := s.initLockTest(c, lockFunc)
   420  
   421  	err := executor.Run(op)
   422  	c.Assert(err, gc.ErrorMatches, "could not acquire lock: wat")
   423  
   424  	c.Assert(mockLock.calledLock, jc.IsFalse)
   425  	c.Assert(mockLock.calledUnlock, jc.IsFalse)
   426  	c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue)
   427  
   428  	c.Assert(op.prepare.called, jc.IsFalse)
   429  	c.Assert(op.execute.called, jc.IsFalse)
   430  	c.Assert(op.commit.called, jc.IsFalse)
   431  }
   432  
   433  func (s *ExecutorSuite) testLockUnlocksOnError(c *gc.C, op *mockOperation) (error, *mockLockFunc) {
   434  	mockLock := &mockLockFunc{op: op}
   435  	lockFunc := mockLock.newSucceedingLockUnlockSucceeds()
   436  	executor := s.initLockTest(c, lockFunc)
   437  
   438  	err := executor.Run(op)
   439  
   440  	c.Assert(mockLock.calledLock, jc.IsTrue)
   441  	c.Assert(mockLock.calledUnlock, jc.IsTrue)
   442  	c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue)
   443  
   444  	return err, mockLock
   445  }
   446  
   447  func (s *ExecutorSuite) TestLockUnlocksOnError_Prepare(c *gc.C) {
   448  	op := &mockOperation{
   449  		needsLock: true,
   450  		prepare:   newStep(nil, errors.New("kerblooie")),
   451  		execute:   newStep(nil, nil),
   452  		commit:    newStep(nil, nil),
   453  	}
   454  
   455  	err, mockLock := s.testLockUnlocksOnError(c, op)
   456  	c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": kerblooie`)
   457  	c.Assert(errors.Cause(err), gc.ErrorMatches, "kerblooie")
   458  
   459  	expectedStepsOnUnlock := []bool{true, false, false}
   460  	c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock)
   461  }
   462  
   463  func (s *ExecutorSuite) TestLockUnlocksOnError_Execute(c *gc.C) {
   464  	op := &mockOperation{
   465  		needsLock: true,
   466  		prepare:   newStep(nil, nil),
   467  		execute:   newStep(nil, errors.New("you asked for it")),
   468  		commit:    newStep(nil, nil),
   469  	}
   470  
   471  	err, mockLock := s.testLockUnlocksOnError(c, op)
   472  	c.Assert(err, gc.ErrorMatches, `executing operation "mock operation": you asked for it`)
   473  	c.Assert(errors.Cause(err), gc.ErrorMatches, "you asked for it")
   474  
   475  	expectedStepsOnUnlock := []bool{true, true, false}
   476  	c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock)
   477  }
   478  
   479  func (s *ExecutorSuite) TestLockUnlocksOnError_Commit(c *gc.C) {
   480  	op := &mockOperation{
   481  		needsLock: true,
   482  		prepare:   newStep(nil, nil),
   483  		execute:   newStep(nil, nil),
   484  		commit:    newStep(nil, errors.New("well, shit")),
   485  	}
   486  
   487  	err, mockLock := s.testLockUnlocksOnError(c, op)
   488  	c.Assert(err, gc.ErrorMatches, `committing operation "mock operation": well, shit`)
   489  	c.Assert(errors.Cause(err), gc.ErrorMatches, "well, shit")
   490  
   491  	expectedStepsOnUnlock := []bool{true, true, true}
   492  	c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock)
   493  }
   494  
   495  type mockLockFunc struct {
   496  	noStepsCalledOnLock bool
   497  	stepsCalledOnUnlock []bool
   498  	calledLock          bool
   499  	calledUnlock        bool
   500  	op                  *mockOperation
   501  }
   502  
   503  func (mock *mockLockFunc) newFailingLock() func(string) (func() error, error) {
   504  	return func(string) (func() error, error) {
   505  		mock.noStepsCalledOnLock = mock.op.prepare.called == false &&
   506  			mock.op.commit.called == false &&
   507  			mock.op.prepare.called == false
   508  		return nil, errors.New("wat")
   509  	}
   510  
   511  }
   512  
   513  func (mock *mockLockFunc) newSucceedingLock(unlockFails bool) func(string) (func() error, error) {
   514  	return func(string) (func() error, error) {
   515  		mock.calledLock = true
   516  		// Ensure that when we lock no operation has been called
   517  		mock.noStepsCalledOnLock = mock.op.prepare.called == false &&
   518  			mock.op.commit.called == false &&
   519  			mock.op.prepare.called == false
   520  		return func() error {
   521  			// Record steps called when unlocking
   522  			mock.stepsCalledOnUnlock = []bool{mock.op.prepare.called,
   523  				mock.op.execute.called,
   524  				mock.op.commit.called}
   525  			mock.calledUnlock = true
   526  			if unlockFails {
   527  				return errors.New("but why")
   528  			}
   529  			return nil
   530  		}, nil
   531  	}
   532  }
   533  
   534  func (mock *mockLockFunc) newSucceedingLockUnlockFails() func(string) (func() error, error) {
   535  	return mock.newSucceedingLock(true)
   536  }
   537  
   538  func (mock *mockLockFunc) newSucceedingLockUnlockSucceeds() func(string) (func() error, error) {
   539  	return mock.newSucceedingLock(false)
   540  }
   541  
   542  type mockStep struct {
   543  	gotState operation.State
   544  	newState *operation.State
   545  	err      error
   546  	called   bool
   547  }
   548  
   549  func newStep(newState *operation.State, err error) *mockStep {
   550  	return &mockStep{newState: newState, err: err}
   551  }
   552  
   553  func (step *mockStep) run(state operation.State) (*operation.State, error) {
   554  	step.called = true
   555  	step.gotState = state
   556  	return step.newState, step.err
   557  }
   558  
   559  type mockOperation struct {
   560  	needsLock bool
   561  	prepare   *mockStep
   562  	execute   *mockStep
   563  	commit    *mockStep
   564  }
   565  
   566  func (op *mockOperation) String() string {
   567  	return "mock operation"
   568  }
   569  
   570  func (op *mockOperation) NeedsGlobalMachineLock() bool {
   571  	return op.needsLock
   572  }
   573  
   574  func (op *mockOperation) Prepare(state operation.State) (*operation.State, error) {
   575  	return op.prepare.run(state)
   576  }
   577  
   578  func (op *mockOperation) Execute(state operation.State) (*operation.State, error) {
   579  	return op.execute.run(state)
   580  }
   581  
   582  func (op *mockOperation) Commit(state operation.State) (*operation.State, error) {
   583  	return op.commit.run(state)
   584  }