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