github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/uniter/operation/runhook_test.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  	"gopkg.in/juju/charm.v5/hooks"
    14  
    15  	"github.com/juju/juju/worker/uniter/hook"
    16  	"github.com/juju/juju/worker/uniter/operation"
    17  	"github.com/juju/juju/worker/uniter/runner"
    18  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    19  )
    20  
    21  type RunHookSuite struct {
    22  	testing.IsolationSuite
    23  }
    24  
    25  var _ = gc.Suite(&RunHookSuite{})
    26  
    27  type newHook func(operation.Factory, hook.Info) (operation.Operation, error)
    28  
    29  func (s *RunHookSuite) testClearResolvedFlagError(c *gc.C, newHook newHook) {
    30  	callbacks := &PrepareHookCallbacks{
    31  		MockClearResolvedFlag: &MockNoArgs{err: errors.New("biff")},
    32  	}
    33  	factory := operation.NewFactory(nil, nil, callbacks, nil, nil)
    34  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
    35  	c.Assert(err, jc.ErrorIsNil)
    36  
    37  	newState, err := op.Prepare(operation.State{})
    38  	c.Check(newState, gc.IsNil)
    39  	c.Check(callbacks.MockClearResolvedFlag.called, jc.IsTrue)
    40  	c.Check(err, gc.ErrorMatches, "biff")
    41  }
    42  
    43  func (s *RunHookSuite) TestClearResolvedFlagError_Retry(c *gc.C) {
    44  	s.testClearResolvedFlagError(c, (operation.Factory).NewRetryHook)
    45  }
    46  
    47  func (s *RunHookSuite) TestClearResolvedFlagError_Skip(c *gc.C) {
    48  	s.testClearResolvedFlagError(c, (operation.Factory).NewSkipHook)
    49  }
    50  
    51  func (s *RunHookSuite) testPrepareHookError(
    52  	c *gc.C, newHook newHook, expectClearResolvedFlag, expectSkip bool,
    53  ) {
    54  	callbacks := &PrepareHookCallbacks{
    55  		MockPrepareHook:       &MockPrepareHook{err: errors.New("pow")},
    56  		MockClearResolvedFlag: &MockNoArgs{},
    57  	}
    58  	factory := operation.NewFactory(nil, nil, callbacks, nil, nil)
    59  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
    60  	c.Assert(err, jc.ErrorIsNil)
    61  
    62  	newState, err := op.Prepare(operation.State{})
    63  	c.Check(newState, gc.IsNil)
    64  	c.Check(callbacks.MockClearResolvedFlag.called, gc.Equals, expectClearResolvedFlag)
    65  	if expectSkip {
    66  		c.Check(err, gc.Equals, operation.ErrSkipExecute)
    67  		c.Check(callbacks.MockPrepareHook.gotHook, gc.IsNil)
    68  		return
    69  	}
    70  	c.Check(err, gc.ErrorMatches, "pow")
    71  	c.Check(callbacks.MockPrepareHook.gotHook, gc.DeepEquals, &hook.Info{
    72  		Kind: hooks.ConfigChanged,
    73  	})
    74  }
    75  
    76  func (s *RunHookSuite) TestPrepareHookError_Run(c *gc.C) {
    77  	s.testPrepareHookError(c, (operation.Factory).NewRunHook, false, false)
    78  }
    79  
    80  func (s *RunHookSuite) TestPrepareHookError_Retry(c *gc.C) {
    81  	s.testPrepareHookError(c, (operation.Factory).NewRetryHook, true, false)
    82  }
    83  
    84  func (s *RunHookSuite) TestPrepareHookError_Skip(c *gc.C) {
    85  	s.testPrepareHookError(c, (operation.Factory).NewSkipHook, true, true)
    86  }
    87  
    88  func (s *RunHookSuite) testPrepareRunnerError(c *gc.C, newHook newHook) {
    89  	callbacks := NewPrepareHookCallbacks()
    90  	runnerFactory := &MockRunnerFactory{
    91  		MockNewHookRunner: &MockNewHookRunner{err: errors.New("splat")},
    92  	}
    93  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil, nil)
    94  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
    95  	c.Assert(err, jc.ErrorIsNil)
    96  
    97  	newState, err := op.Prepare(operation.State{})
    98  	c.Check(newState, gc.IsNil)
    99  	c.Check(err, gc.ErrorMatches, "splat")
   100  	c.Check(runnerFactory.MockNewHookRunner.gotHook, gc.DeepEquals, &hook.Info{
   101  		Kind: hooks.ConfigChanged,
   102  	})
   103  }
   104  
   105  func (s *RunHookSuite) TestPrepareRunnerError_Run(c *gc.C) {
   106  	s.testPrepareRunnerError(c, (operation.Factory).NewRunHook)
   107  }
   108  
   109  func (s *RunHookSuite) TestPrepareRunnerError_Retry(c *gc.C) {
   110  	s.testPrepareRunnerError(c, (operation.Factory).NewRetryHook)
   111  }
   112  
   113  func (s *RunHookSuite) testPrepareSuccess(
   114  	c *gc.C, newHook newHook, before, after operation.State,
   115  ) {
   116  	runnerFactory := NewRunHookRunnerFactory(errors.New("should not call"))
   117  	callbacks := NewPrepareHookCallbacks()
   118  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil, nil)
   119  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   120  	c.Assert(err, jc.ErrorIsNil)
   121  
   122  	newState, err := op.Prepare(before)
   123  	c.Check(err, jc.ErrorIsNil)
   124  	c.Check(newState, gc.DeepEquals, &after)
   125  }
   126  
   127  func (s *RunHookSuite) TestPrepareSuccess_BlankSlate(c *gc.C) {
   128  	for i, newHook := range []newHook{
   129  		(operation.Factory).NewRunHook,
   130  		(operation.Factory).NewRetryHook,
   131  	} {
   132  		c.Logf("variant %d", i)
   133  		s.testPrepareSuccess(c,
   134  			newHook,
   135  			operation.State{},
   136  			operation.State{
   137  				Kind: operation.RunHook,
   138  				Step: operation.Pending,
   139  				Hook: &hook.Info{Kind: hooks.ConfigChanged},
   140  			},
   141  		)
   142  	}
   143  }
   144  
   145  func (s *RunHookSuite) TestPrepareSuccess_Preserve(c *gc.C) {
   146  	for i, newHook := range []newHook{
   147  		(operation.Factory).NewRunHook,
   148  		(operation.Factory).NewRetryHook,
   149  	} {
   150  		c.Logf("variant %d", i)
   151  		s.testPrepareSuccess(c,
   152  			newHook,
   153  			overwriteState,
   154  			operation.State{
   155  				Started:            true,
   156  				CollectMetricsTime: 1234567,
   157  				UpdateStatusTime:   1234567,
   158  				Kind:               operation.RunHook,
   159  				Step:               operation.Pending,
   160  				Hook:               &hook.Info{Kind: hooks.ConfigChanged},
   161  			},
   162  		)
   163  	}
   164  }
   165  
   166  func (s *RunHookSuite) getExecuteRunnerTest(c *gc.C, newHook newHook, kind hooks.Kind, runErr error) (operation.Operation, *ExecuteHookCallbacks, *MockRunnerFactory) {
   167  	runnerFactory := NewRunHookRunnerFactory(runErr)
   168  	callbacks := &ExecuteHookCallbacks{
   169  		PrepareHookCallbacks:    NewPrepareHookCallbacks(),
   170  		MockNotifyHookCompleted: &MockNotify{},
   171  		MockNotifyHookFailed:    &MockNotify{},
   172  	}
   173  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil, nil)
   174  	op, err := newHook(factory, hook.Info{Kind: kind})
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	return op, callbacks, runnerFactory
   177  }
   178  
   179  func (s *RunHookSuite) testExecuteMissingHookError(c *gc.C, newHook newHook) {
   180  	runErr := runner.NewMissingHookError("blah-blah")
   181  	for _, kind := range hooks.UnitHooks() {
   182  		c.Logf("hook %v", kind)
   183  		op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, kind, runErr)
   184  		_, err := op.Prepare(operation.State{})
   185  		c.Assert(err, jc.ErrorIsNil)
   186  
   187  		newState, err := op.Execute(operation.State{})
   188  		c.Assert(err, jc.ErrorIsNil)
   189  		c.Assert(newState, gc.DeepEquals, &operation.State{
   190  			Kind: operation.RunHook,
   191  			Step: operation.Done,
   192  			Hook: &hook.Info{Kind: kind},
   193  		})
   194  		c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   195  		c.Assert(callbacks.MockNotifyHookCompleted.gotName, gc.IsNil)
   196  		c.Assert(callbacks.MockNotifyHookFailed.gotName, gc.IsNil)
   197  
   198  		status, err := runnerFactory.MockNewHookRunner.runner.Context().UnitStatus()
   199  		c.Assert(err, jc.ErrorIsNil)
   200  		testAfterHookStatus(c, kind, status, false)
   201  	}
   202  }
   203  
   204  func (s *RunHookSuite) TestExecuteMissingHookError_Run(c *gc.C) {
   205  	s.testExecuteMissingHookError(c, (operation.Factory).NewRunHook)
   206  }
   207  
   208  func (s *RunHookSuite) TestExecuteMissingHookError_Retry(c *gc.C) {
   209  	s.testExecuteMissingHookError(c, (operation.Factory).NewRetryHook)
   210  }
   211  
   212  func (s *RunHookSuite) testExecuteRequeueRebootError(c *gc.C, newHook newHook) {
   213  	runErr := runner.ErrRequeueAndReboot
   214  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, hooks.ConfigChanged, runErr)
   215  	_, err := op.Prepare(operation.State{})
   216  	c.Assert(err, jc.ErrorIsNil)
   217  
   218  	newState, err := op.Execute(operation.State{})
   219  	c.Assert(err, gc.Equals, operation.ErrNeedsReboot)
   220  	c.Assert(newState, gc.DeepEquals, &operation.State{
   221  		Kind: operation.RunHook,
   222  		Step: operation.Queued,
   223  		Hook: &hook.Info{Kind: hooks.ConfigChanged},
   224  	})
   225  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   226  	c.Assert(*callbacks.MockNotifyHookCompleted.gotName, gc.Equals, "some-hook-name")
   227  	c.Assert(*callbacks.MockNotifyHookCompleted.gotContext, gc.Equals, runnerFactory.MockNewHookRunner.runner.context)
   228  	c.Assert(callbacks.MockNotifyHookFailed.gotName, gc.IsNil)
   229  }
   230  
   231  func (s *RunHookSuite) TestExecuteRequeueRebootError_Run(c *gc.C) {
   232  	s.testExecuteRequeueRebootError(c, (operation.Factory).NewRunHook)
   233  }
   234  
   235  func (s *RunHookSuite) TestExecuteRequeueRebootError_Retry(c *gc.C) {
   236  	s.testExecuteRequeueRebootError(c, (operation.Factory).NewRetryHook)
   237  }
   238  
   239  func (s *RunHookSuite) testExecuteRebootError(c *gc.C, newHook newHook) {
   240  	runErr := runner.ErrReboot
   241  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, hooks.ConfigChanged, runErr)
   242  	_, err := op.Prepare(operation.State{})
   243  	c.Assert(err, jc.ErrorIsNil)
   244  
   245  	newState, err := op.Execute(operation.State{})
   246  	c.Assert(err, gc.Equals, operation.ErrNeedsReboot)
   247  	c.Assert(newState, gc.DeepEquals, &operation.State{
   248  		Kind: operation.RunHook,
   249  		Step: operation.Done,
   250  		Hook: &hook.Info{Kind: hooks.ConfigChanged},
   251  	})
   252  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   253  	c.Assert(*callbacks.MockNotifyHookCompleted.gotName, gc.Equals, "some-hook-name")
   254  	c.Assert(*callbacks.MockNotifyHookCompleted.gotContext, gc.Equals, runnerFactory.MockNewHookRunner.runner.context)
   255  	c.Assert(callbacks.MockNotifyHookFailed.gotName, gc.IsNil)
   256  }
   257  
   258  func (s *RunHookSuite) TestExecuteRebootError_Run(c *gc.C) {
   259  	s.testExecuteRebootError(c, (operation.Factory).NewRunHook)
   260  }
   261  
   262  func (s *RunHookSuite) TestExecuteRebootError_Retry(c *gc.C) {
   263  	s.testExecuteRebootError(c, (operation.Factory).NewRetryHook)
   264  }
   265  
   266  func (s *RunHookSuite) testExecuteOtherError(c *gc.C, newHook newHook) {
   267  	runErr := errors.New("graaargh")
   268  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, hooks.ConfigChanged, runErr)
   269  	_, err := op.Prepare(operation.State{})
   270  	c.Assert(err, jc.ErrorIsNil)
   271  
   272  	newState, err := op.Execute(operation.State{})
   273  	c.Assert(err, gc.Equals, operation.ErrHookFailed)
   274  	c.Assert(newState, gc.IsNil)
   275  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   276  	c.Assert(*callbacks.MockNotifyHookFailed.gotName, gc.Equals, "some-hook-name")
   277  	c.Assert(*callbacks.MockNotifyHookFailed.gotContext, gc.Equals, runnerFactory.MockNewHookRunner.runner.context)
   278  	c.Assert(callbacks.MockNotifyHookCompleted.gotName, gc.IsNil)
   279  }
   280  
   281  func (s *RunHookSuite) TestExecuteOtherError_Run(c *gc.C) {
   282  	s.testExecuteOtherError(c, (operation.Factory).NewRunHook)
   283  }
   284  
   285  func (s *RunHookSuite) TestExecuteOtherError_Retry(c *gc.C) {
   286  	s.testExecuteOtherError(c, (operation.Factory).NewRetryHook)
   287  }
   288  
   289  func (s *RunHookSuite) testExecuteSuccess(
   290  	c *gc.C, newHook newHook, before, after operation.State, setStatusCalled bool,
   291  ) {
   292  	op, callbacks, f := s.getExecuteRunnerTest(c, newHook, hooks.ConfigChanged, nil)
   293  	f.MockNewHookRunner.runner.MockRunHook.setStatusCalled = setStatusCalled
   294  	midState, err := op.Prepare(before)
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	c.Assert(midState, gc.NotNil)
   297  
   298  	newState, err := op.Execute(*midState)
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	c.Assert(newState, gc.DeepEquals, &after)
   301  	c.Check(callbacks.executingMessage, gc.Equals, "running some-hook-name hook")
   302  }
   303  
   304  func (s *RunHookSuite) TestExecuteSuccess_BlankSlate(c *gc.C) {
   305  	for i, newHook := range []newHook{
   306  		(operation.Factory).NewRunHook,
   307  		(operation.Factory).NewRetryHook,
   308  	} {
   309  		c.Logf("variant %d", i)
   310  		s.testExecuteSuccess(c,
   311  			newHook,
   312  			operation.State{},
   313  			operation.State{
   314  				Kind:      operation.RunHook,
   315  				Step:      operation.Done,
   316  				Hook:      &hook.Info{Kind: hooks.ConfigChanged},
   317  				StatusSet: true,
   318  			},
   319  			true,
   320  		)
   321  	}
   322  }
   323  
   324  func (s *RunHookSuite) TestExecuteSuccess_Preserve(c *gc.C) {
   325  	for i, newHook := range []newHook{
   326  		(operation.Factory).NewRunHook,
   327  		(operation.Factory).NewRetryHook,
   328  	} {
   329  		c.Logf("variant %d", i)
   330  		s.testExecuteSuccess(c,
   331  			newHook,
   332  			overwriteState,
   333  			operation.State{
   334  				Started:            true,
   335  				CollectMetricsTime: 1234567,
   336  				UpdateStatusTime:   1234567,
   337  				Kind:               operation.RunHook,
   338  				Step:               operation.Done,
   339  				Hook:               &hook.Info{Kind: hooks.ConfigChanged},
   340  				StatusSet:          true,
   341  			},
   342  			true,
   343  		)
   344  	}
   345  }
   346  
   347  func (s *RunHookSuite) testExecuteThenCharmStatus(
   348  	c *gc.C, newHook newHook, before, after operation.State, kind hooks.Kind, setStatusCalled bool,
   349  ) {
   350  	op, _, f := s.getExecuteRunnerTest(c, newHook, kind, nil)
   351  	f.MockNewHookRunner.runner.MockRunHook.setStatusCalled = setStatusCalled
   352  	midState, err := op.Prepare(before)
   353  	c.Assert(err, jc.ErrorIsNil)
   354  	c.Assert(midState, gc.NotNil)
   355  
   356  	status, err := f.MockNewHookRunner.runner.Context().UnitStatus()
   357  	c.Assert(err, jc.ErrorIsNil)
   358  
   359  	newState, err := op.Execute(*midState)
   360  	c.Assert(err, jc.ErrorIsNil)
   361  	c.Assert(newState, gc.DeepEquals, &after)
   362  
   363  	status, err = f.MockNewHookRunner.runner.Context().UnitStatus()
   364  	c.Assert(err, jc.ErrorIsNil)
   365  	testAfterHookStatus(c, kind, status, after.StatusSet)
   366  }
   367  
   368  func testBeforeHookStatus(c *gc.C, kind hooks.Kind, status *jujuc.StatusInfo) {
   369  	switch kind {
   370  	case hooks.Install:
   371  		c.Assert(status.Status, gc.Equals, "maintenance")
   372  		c.Assert(status.Info, gc.Equals, "installing charm software")
   373  	case hooks.Stop:
   374  		c.Assert(string(status.Status), gc.Equals, "maintenance")
   375  		c.Assert(status.Info, gc.Equals, "cleaning up prior to charm deletion")
   376  	default:
   377  		c.Assert(string(status.Status), gc.Equals, "")
   378  	}
   379  }
   380  
   381  func testAfterHookStatus(c *gc.C, kind hooks.Kind, status *jujuc.StatusInfo, statusSetCalled bool) {
   382  	switch kind {
   383  	case hooks.Install:
   384  		c.Assert(status.Status, gc.Equals, "maintenance")
   385  		c.Assert(status.Info, gc.Equals, "installing charm software")
   386  	case hooks.Start:
   387  		if statusSetCalled {
   388  			c.Assert(string(status.Status), gc.Equals, "")
   389  		} else {
   390  			c.Assert(status.Status, gc.Equals, "unknown")
   391  		}
   392  	case hooks.Stop:
   393  		c.Assert(status.Status, gc.Equals, "terminated")
   394  	default:
   395  		c.Assert(string(status.Status), gc.Equals, "")
   396  	}
   397  }
   398  
   399  func (s *RunHookSuite) testBeforeHookExecute(c *gc.C, newHook newHook, kind hooks.Kind) {
   400  	// To check what happens in the beforeHook() call, we run a hook with an error
   401  	// so that it does not complete successfully and thus afterHook() does not run,
   402  	// overwriting the values.
   403  	runErr := errors.New("graaargh")
   404  	op, _, runnerFactory := s.getExecuteRunnerTest(c, newHook, kind, runErr)
   405  	_, err := op.Prepare(operation.State{})
   406  	c.Assert(err, jc.ErrorIsNil)
   407  
   408  	newState, err := op.Execute(operation.State{})
   409  	c.Assert(err, gc.Equals, operation.ErrHookFailed)
   410  	c.Assert(newState, gc.IsNil)
   411  
   412  	status, err := runnerFactory.MockNewHookRunner.runner.Context().UnitStatus()
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	testBeforeHookStatus(c, kind, status)
   415  }
   416  
   417  func (s *RunHookSuite) TestBeforeHookStatus(c *gc.C) {
   418  	for _, kind := range hooks.UnitHooks() {
   419  		c.Logf("hook %v", kind)
   420  		for i, newHook := range []newHook{
   421  			(operation.Factory).NewRunHook,
   422  			(operation.Factory).NewRetryHook,
   423  		} {
   424  			c.Logf("variant %d", i)
   425  			s.testBeforeHookExecute(c, newHook, kind)
   426  		}
   427  	}
   428  }
   429  
   430  func (s *RunHookSuite) testExecuteHookWithSetStatus(c *gc.C, kind hooks.Kind, setStatusCalled bool) {
   431  	for i, newHook := range []newHook{
   432  		(operation.Factory).NewRunHook,
   433  		(operation.Factory).NewRetryHook,
   434  	} {
   435  		c.Logf("variant %d", i)
   436  		s.testExecuteThenCharmStatus(c,
   437  			newHook,
   438  			overwriteState,
   439  			operation.State{
   440  				Started:            true,
   441  				CollectMetricsTime: 1234567,
   442  				UpdateStatusTime:   1234567,
   443  				Kind:               operation.RunHook,
   444  				Step:               operation.Done,
   445  				Hook:               &hook.Info{Kind: kind},
   446  				StatusSet:          setStatusCalled,
   447  			},
   448  			kind,
   449  			setStatusCalled,
   450  		)
   451  	}
   452  }
   453  
   454  func (s *RunHookSuite) TestExecuteHookWithSetStatus(c *gc.C) {
   455  	for _, kind := range hooks.UnitHooks() {
   456  		c.Logf("hook %v", kind)
   457  		s.testExecuteHookWithSetStatus(c, kind, true)
   458  		s.testExecuteHookWithSetStatus(c, kind, false)
   459  	}
   460  }
   461  
   462  func (s *RunHookSuite) testCommitError(c *gc.C, newHook newHook) {
   463  	callbacks := &CommitHookCallbacks{
   464  		MockCommitHook: &MockCommitHook{nil, errors.New("pow")},
   465  	}
   466  	factory := operation.NewFactory(nil, nil, callbacks, nil, nil)
   467  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   468  	c.Assert(err, jc.ErrorIsNil)
   469  
   470  	newState, err := op.Commit(operation.State{})
   471  	c.Assert(newState, gc.IsNil)
   472  	c.Assert(err, gc.ErrorMatches, "pow")
   473  }
   474  
   475  func (s *RunHookSuite) TestCommitError_Run(c *gc.C) {
   476  	s.testCommitError(c, (operation.Factory).NewRunHook)
   477  }
   478  
   479  func (s *RunHookSuite) TestCommitError_Retry(c *gc.C) {
   480  	s.testCommitError(c, (operation.Factory).NewRetryHook)
   481  }
   482  
   483  func (s *RunHookSuite) TestCommitError_Skip(c *gc.C) {
   484  	s.testCommitError(c, (operation.Factory).NewSkipHook)
   485  }
   486  
   487  func (s *RunHookSuite) testCommitSuccess(c *gc.C, newHook newHook, hookInfo hook.Info, before, after operation.State) {
   488  	callbacks := &CommitHookCallbacks{
   489  		MockCommitHook: &MockCommitHook{},
   490  	}
   491  	factory := operation.NewFactory(nil, nil, callbacks, nil, nil)
   492  	op, err := newHook(factory, hookInfo)
   493  	c.Assert(err, jc.ErrorIsNil)
   494  
   495  	newState, err := op.Commit(before)
   496  	c.Assert(err, jc.ErrorIsNil)
   497  	c.Assert(newState, gc.DeepEquals, &after)
   498  }
   499  
   500  func (s *RunHookSuite) TestCommitSuccess_ConfigChanged_QueueStartHook(c *gc.C) {
   501  	for i, newHook := range []newHook{
   502  		(operation.Factory).NewRunHook,
   503  		(operation.Factory).NewRetryHook,
   504  		(operation.Factory).NewSkipHook,
   505  	} {
   506  		c.Logf("variant %d", i)
   507  		s.testCommitSuccess(c,
   508  			newHook,
   509  			hook.Info{Kind: hooks.ConfigChanged},
   510  			operation.State{},
   511  			operation.State{
   512  				Kind: operation.RunHook,
   513  				Step: operation.Queued,
   514  				Hook: &hook.Info{Kind: hooks.Start},
   515  			},
   516  		)
   517  	}
   518  }
   519  
   520  func (s *RunHookSuite) TestCommitSuccess_ConfigChanged_Preserve(c *gc.C) {
   521  	for i, newHook := range []newHook{
   522  		(operation.Factory).NewRunHook,
   523  		(operation.Factory).NewRetryHook,
   524  		(operation.Factory).NewSkipHook,
   525  	} {
   526  		c.Logf("variant %d", i)
   527  		s.testCommitSuccess(c,
   528  			newHook,
   529  			hook.Info{Kind: hooks.ConfigChanged},
   530  			overwriteState,
   531  			operation.State{
   532  				Started:            true,
   533  				CollectMetricsTime: 1234567,
   534  				UpdateStatusTime:   1234567,
   535  				Kind:               operation.Continue,
   536  				Step:               operation.Pending,
   537  			},
   538  		)
   539  	}
   540  }
   541  
   542  func (s *RunHookSuite) TestCommitSuccess_Start_SetStarted(c *gc.C) {
   543  	for i, newHook := range []newHook{
   544  		(operation.Factory).NewRunHook,
   545  		(operation.Factory).NewRetryHook,
   546  		(operation.Factory).NewSkipHook,
   547  	} {
   548  		c.Logf("variant %d", i)
   549  		s.testCommitSuccess(c,
   550  			newHook,
   551  			hook.Info{Kind: hooks.Start},
   552  			operation.State{},
   553  			operation.State{
   554  				Started: true,
   555  				Kind:    operation.Continue,
   556  				Step:    operation.Pending,
   557  			},
   558  		)
   559  	}
   560  }
   561  
   562  func (s *RunHookSuite) TestCommitSuccess_Start_Preserve(c *gc.C) {
   563  	for i, newHook := range []newHook{
   564  		(operation.Factory).NewRunHook,
   565  		(operation.Factory).NewRetryHook,
   566  		(operation.Factory).NewSkipHook,
   567  	} {
   568  		c.Logf("variant %d", i)
   569  		s.testCommitSuccess(c,
   570  			newHook,
   571  			hook.Info{Kind: hooks.Start},
   572  			overwriteState,
   573  			operation.State{
   574  				Started:            true,
   575  				CollectMetricsTime: 1234567,
   576  				UpdateStatusTime:   1234567,
   577  				Kind:               operation.Continue,
   578  				Step:               operation.Pending,
   579  			},
   580  		)
   581  	}
   582  }
   583  
   584  func (s *RunHookSuite) testQueueHook_BlankSlate(c *gc.C, cause hooks.Kind) {
   585  	for i, newHook := range []newHook{
   586  		(operation.Factory).NewRunHook,
   587  		(operation.Factory).NewRetryHook,
   588  		(operation.Factory).NewSkipHook,
   589  	} {
   590  		c.Logf("variant %d", i)
   591  		var hi *hook.Info
   592  		switch cause {
   593  		case hooks.UpgradeCharm:
   594  			hi = &hook.Info{Kind: hooks.ConfigChanged}
   595  		default:
   596  			hi = nil
   597  		}
   598  		s.testCommitSuccess(c,
   599  			newHook,
   600  			hook.Info{Kind: cause},
   601  			operation.State{},
   602  			operation.State{
   603  				Kind:    operation.RunHook,
   604  				Step:    operation.Queued,
   605  				Stopped: cause == hooks.Stop,
   606  				Hook:    hi,
   607  			},
   608  		)
   609  	}
   610  }
   611  
   612  func (s *RunHookSuite) testQueueHook_Preserve(c *gc.C, cause hooks.Kind) {
   613  	for i, newHook := range []newHook{
   614  		(operation.Factory).NewRunHook,
   615  		(operation.Factory).NewRetryHook,
   616  		(operation.Factory).NewSkipHook,
   617  	} {
   618  		c.Logf("variant %d", i)
   619  		var hi *hook.Info
   620  		switch cause {
   621  		case hooks.UpgradeCharm:
   622  			hi = &hook.Info{Kind: hooks.ConfigChanged}
   623  		default:
   624  			hi = nil
   625  		}
   626  		s.testCommitSuccess(c,
   627  			newHook,
   628  			hook.Info{Kind: cause},
   629  			overwriteState,
   630  			operation.State{
   631  				Kind:               operation.RunHook,
   632  				Step:               operation.Queued,
   633  				Started:            true,
   634  				Stopped:            cause == hooks.Stop,
   635  				Hook:               hi,
   636  				CollectMetricsTime: 1234567,
   637  				UpdateStatusTime:   1234567,
   638  			},
   639  		)
   640  	}
   641  }
   642  
   643  func (s *RunHookSuite) TestQueueHook_UpgradeCharm_BlankSlate(c *gc.C) {
   644  	s.testQueueHook_BlankSlate(c, hooks.UpgradeCharm)
   645  }
   646  
   647  func (s *RunHookSuite) TestQueueHook_UpgradeCharm_Preserve(c *gc.C) {
   648  	s.testQueueHook_Preserve(c, hooks.UpgradeCharm)
   649  }
   650  
   651  func (s *RunHookSuite) testQueueNothing_BlankSlate(c *gc.C, hookInfo hook.Info) {
   652  	for i, newHook := range []newHook{
   653  		(operation.Factory).NewRunHook,
   654  		(operation.Factory).NewRetryHook,
   655  		(operation.Factory).NewSkipHook,
   656  	} {
   657  		c.Logf("variant %d", i)
   658  		s.testCommitSuccess(c,
   659  			newHook,
   660  			hookInfo,
   661  			operation.State{},
   662  			operation.State{
   663  				Kind:    operation.Continue,
   664  				Step:    operation.Pending,
   665  				Stopped: hookInfo.Kind == hooks.Stop,
   666  			},
   667  		)
   668  	}
   669  }
   670  
   671  func (s *RunHookSuite) testQueueNothing_Preserve(c *gc.C, hookInfo hook.Info) {
   672  	for i, newHook := range []newHook{
   673  		(operation.Factory).NewRunHook,
   674  		(operation.Factory).NewRetryHook,
   675  		(operation.Factory).NewSkipHook,
   676  	} {
   677  		c.Logf("variant %d", i)
   678  		s.testCommitSuccess(c,
   679  			newHook,
   680  			hookInfo,
   681  			overwriteState,
   682  			operation.State{
   683  				Kind:               operation.Continue,
   684  				Step:               operation.Pending,
   685  				Started:            true,
   686  				Stopped:            hookInfo.Kind == hooks.Stop,
   687  				CollectMetricsTime: 1234567,
   688  				UpdateStatusTime:   1234567,
   689  			},
   690  		)
   691  	}
   692  }
   693  
   694  func (s *RunHookSuite) TestQueueNothing_Install_BlankSlate(c *gc.C) {
   695  	s.testQueueNothing_BlankSlate(c, hook.Info{
   696  		Kind: hooks.Install,
   697  	})
   698  }
   699  
   700  func (s *RunHookSuite) TestQueueNothing_Install_Preserve(c *gc.C) {
   701  	s.testQueueNothing_Preserve(c, hook.Info{
   702  		Kind: hooks.Install,
   703  	})
   704  }
   705  
   706  func (s *RunHookSuite) TestQueueNothing_Stop_BlankSlate(c *gc.C) {
   707  	s.testQueueNothing_BlankSlate(c, hook.Info{
   708  		Kind: hooks.Stop,
   709  	})
   710  }
   711  
   712  func (s *RunHookSuite) TestQueueNothing_Stop_Preserve(c *gc.C) {
   713  	s.testQueueNothing_Preserve(c, hook.Info{
   714  		Kind: hooks.Stop,
   715  	})
   716  }
   717  
   718  func (s *RunHookSuite) TestQueueNothing_RelationJoined_BlankSlate(c *gc.C) {
   719  	s.testQueueNothing_BlankSlate(c, hook.Info{
   720  		Kind:       hooks.RelationJoined,
   721  		RemoteUnit: "u/0",
   722  	})
   723  }
   724  
   725  func (s *RunHookSuite) TestQueueNothing_RelationJoined_Preserve(c *gc.C) {
   726  	s.testQueueNothing_Preserve(c, hook.Info{
   727  		Kind:       hooks.RelationJoined,
   728  		RemoteUnit: "u/0",
   729  	})
   730  }
   731  
   732  func (s *RunHookSuite) TestQueueNothing_RelationChanged_BlankSlate(c *gc.C) {
   733  	s.testQueueNothing_BlankSlate(c, hook.Info{
   734  		Kind:       hooks.RelationChanged,
   735  		RemoteUnit: "u/0",
   736  	})
   737  }
   738  
   739  func (s *RunHookSuite) TestQueueNothing_RelationChanged_Preserve(c *gc.C) {
   740  	s.testQueueNothing_Preserve(c, hook.Info{
   741  		Kind:       hooks.RelationChanged,
   742  		RemoteUnit: "u/0",
   743  	})
   744  }
   745  
   746  func (s *RunHookSuite) TestQueueNothing_RelationDeparted_BlankSlate(c *gc.C) {
   747  	s.testQueueNothing_BlankSlate(c, hook.Info{
   748  		Kind:       hooks.RelationDeparted,
   749  		RemoteUnit: "u/0",
   750  	})
   751  }
   752  
   753  func (s *RunHookSuite) TestQueueNothing_RelationDeparted_Preserve(c *gc.C) {
   754  	s.testQueueNothing_Preserve(c, hook.Info{
   755  		Kind:       hooks.RelationDeparted,
   756  		RemoteUnit: "u/0",
   757  	})
   758  }
   759  
   760  func (s *RunHookSuite) TestQueueNothing_RelationBroken_BlankSlate(c *gc.C) {
   761  	s.testQueueNothing_BlankSlate(c, hook.Info{
   762  		Kind: hooks.RelationBroken,
   763  	})
   764  }
   765  
   766  func (s *RunHookSuite) TestQueueNothing_RelationBroken_Preserve(c *gc.C) {
   767  	s.testQueueNothing_Preserve(c, hook.Info{
   768  		Kind: hooks.RelationBroken,
   769  	})
   770  }
   771  
   772  func (s *RunHookSuite) testCommitSuccess_UpdateStatusTime(c *gc.C, newHook newHook) {
   773  	callbacks := &CommitHookCallbacks{
   774  		MockCommitHook: &MockCommitHook{},
   775  	}
   776  	factory := operation.NewFactory(nil, nil, callbacks, nil, nil)
   777  	op, err := newHook(factory, hook.Info{Kind: hooks.UpdateStatus})
   778  	c.Assert(err, jc.ErrorIsNil)
   779  
   780  	nowBefore := time.Now().Unix()
   781  	newState, err := op.Commit(overwriteState)
   782  	c.Assert(err, jc.ErrorIsNil)
   783  
   784  	nowAfter := time.Now().Unix()
   785  	nowWritten := newState.UpdateStatusTime
   786  	c.Logf("%d <= %d <= %d", nowBefore, nowWritten, nowAfter)
   787  	c.Check(nowBefore <= nowWritten, jc.IsTrue)
   788  	c.Check(nowWritten <= nowAfter, jc.IsTrue)
   789  
   790  	// Check the other fields match.
   791  	newState.UpdateStatusTime = 0
   792  	c.Check(newState, gc.DeepEquals, &operation.State{
   793  		Started:            true,
   794  		Kind:               operation.Continue,
   795  		Step:               operation.Pending,
   796  		CollectMetricsTime: 1234567,
   797  	})
   798  }
   799  
   800  func (s *RunHookSuite) TestCommitSuccess_UpdateStatusTime_Run(c *gc.C) {
   801  	s.testCommitSuccess_UpdateStatusTime(c, (operation.Factory).NewRunHook)
   802  }
   803  
   804  func (s *RunHookSuite) TestCommitSuccess_UpdateStatusTime_Retry(c *gc.C) {
   805  	s.testCommitSuccess_UpdateStatusTime(c, (operation.Factory).NewRetryHook)
   806  }
   807  
   808  func (s *RunHookSuite) TestCommitSuccess_UpdateStatusTime_Skip(c *gc.C) {
   809  	s.testCommitSuccess_UpdateStatusTime(c, (operation.Factory).NewSkipHook)
   810  }
   811  
   812  func (s *RunHookSuite) testCommitSuccess_CollectMetricsTime(c *gc.C, newHook newHook) {
   813  	callbacks := &CommitHookCallbacks{
   814  		MockCommitHook: &MockCommitHook{},
   815  	}
   816  	factory := operation.NewFactory(nil, nil, callbacks, nil, nil)
   817  	op, err := newHook(factory, hook.Info{Kind: hooks.CollectMetrics})
   818  	c.Assert(err, jc.ErrorIsNil)
   819  
   820  	nowBefore := time.Now().Unix()
   821  	newState, err := op.Commit(overwriteState)
   822  	c.Assert(err, jc.ErrorIsNil)
   823  
   824  	nowAfter := time.Now().Unix()
   825  	nowWritten := newState.CollectMetricsTime
   826  	c.Logf("%d <= %d <= %d", nowBefore, nowWritten, nowAfter)
   827  	c.Check(nowBefore <= nowWritten, jc.IsTrue)
   828  	c.Check(nowWritten <= nowAfter, jc.IsTrue)
   829  
   830  	// Check the other fields match.
   831  	newState.CollectMetricsTime = 0
   832  	c.Check(newState, gc.DeepEquals, &operation.State{
   833  		Started:          true,
   834  		Kind:             operation.Continue,
   835  		Step:             operation.Pending,
   836  		UpdateStatusTime: 1234567,
   837  	})
   838  }
   839  
   840  func (s *RunHookSuite) TestCommitSuccess_CollectMetricsTime_Run(c *gc.C) {
   841  	s.testCommitSuccess_CollectMetricsTime(c, (operation.Factory).NewRunHook)
   842  }
   843  
   844  func (s *RunHookSuite) TestCommitSuccess_CollectMetricsTime_Retry(c *gc.C) {
   845  	s.testCommitSuccess_CollectMetricsTime(c, (operation.Factory).NewRetryHook)
   846  }
   847  
   848  func (s *RunHookSuite) TestCommitSuccess_CollectMetricsTime_Skip(c *gc.C) {
   849  	s.testCommitSuccess_CollectMetricsTime(c, (operation.Factory).NewSkipHook)
   850  }
   851  
   852  func (s *RunHookSuite) testNeedsGlobalMachineLock(c *gc.C, newHook newHook, expected bool) {
   853  	factory := operation.NewFactory(nil, nil, nil, nil, nil)
   854  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   855  	c.Assert(err, jc.ErrorIsNil)
   856  	c.Assert(op.NeedsGlobalMachineLock(), gc.Equals, expected)
   857  }
   858  
   859  func (s *RunHookSuite) TestNeedsGlobalMachineLock_Run(c *gc.C) {
   860  	s.testNeedsGlobalMachineLock(c, (operation.Factory).NewRunHook, true)
   861  }
   862  
   863  func (s *RunHookSuite) TestNeedsGlobalMachineLock_Retry(c *gc.C) {
   864  	s.testNeedsGlobalMachineLock(c, (operation.Factory).NewRetryHook, true)
   865  }
   866  
   867  func (s *RunHookSuite) TestNeedsGlobalMachineLock_Skip(c *gc.C) {
   868  	s.testNeedsGlobalMachineLock(c, (operation.Factory).NewSkipHook, false)
   869  }