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