github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/uniter/operation/deploy_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  	corecharm "gopkg.in/juju/charm.v5"
    12  	"gopkg.in/juju/charm.v5/hooks"
    13  
    14  	"github.com/juju/juju/worker/uniter/charm"
    15  	"github.com/juju/juju/worker/uniter/hook"
    16  	"github.com/juju/juju/worker/uniter/operation"
    17  )
    18  
    19  type DeploySuite struct {
    20  	testing.IsolationSuite
    21  }
    22  
    23  var _ = gc.Suite(&DeploySuite{})
    24  
    25  type newDeploy func(operation.Factory, *corecharm.URL) (operation.Operation, error)
    26  
    27  func (s *DeploySuite) testPrepareAlreadyDone(
    28  	c *gc.C, newDeploy newDeploy, kind operation.Kind, expectClearResolvedFlag bool,
    29  ) {
    30  	callbacks := &DeployCallbacks{
    31  		MockClearResolvedFlag: &MockNoArgs{},
    32  	}
    33  	factory := operation.NewFactory(operation.FactoryParams{Callbacks: callbacks})
    34  	op, err := newDeploy(factory, curl("cs:quantal/hive-23"))
    35  	c.Assert(err, jc.ErrorIsNil)
    36  	newState, err := op.Prepare(operation.State{
    37  		Kind:     kind,
    38  		Step:     operation.Done,
    39  		CharmURL: curl("cs:quantal/hive-23"),
    40  	})
    41  	c.Check(newState, gc.IsNil)
    42  	c.Check(errors.Cause(err), gc.Equals, operation.ErrSkipExecute)
    43  	c.Check(callbacks.MockClearResolvedFlag.called, gc.Equals, expectClearResolvedFlag)
    44  }
    45  
    46  func (s *DeploySuite) TestPrepareAlreadyDone_Install(c *gc.C) {
    47  	s.testPrepareAlreadyDone(c,
    48  		(operation.Factory).NewInstall,
    49  		operation.Install,
    50  		false,
    51  	)
    52  }
    53  
    54  func (s *DeploySuite) TestPrepareAlreadyDone_Upgrade(c *gc.C) {
    55  	s.testPrepareAlreadyDone(c,
    56  		(operation.Factory).NewUpgrade,
    57  		operation.Upgrade,
    58  		false,
    59  	)
    60  }
    61  
    62  func (s *DeploySuite) TestPrepareAlreadyDone_RevertUpgrade(c *gc.C) {
    63  	s.testPrepareAlreadyDone(c,
    64  		(operation.Factory).NewRevertUpgrade,
    65  		operation.Upgrade,
    66  		true,
    67  	)
    68  }
    69  
    70  func (s *DeploySuite) TestPrepareAlreadyDone_ResolvedUpgrade(c *gc.C) {
    71  	s.testPrepareAlreadyDone(c,
    72  		(operation.Factory).NewResolvedUpgrade,
    73  		operation.Upgrade,
    74  		true,
    75  	)
    76  }
    77  
    78  func (s *DeploySuite) testClearResolvedFlagError(c *gc.C, newDeploy newDeploy) {
    79  	callbacks := &DeployCallbacks{
    80  		MockClearResolvedFlag: &MockNoArgs{err: errors.New("blort")},
    81  	}
    82  	factory := operation.NewFactory(operation.FactoryParams{Callbacks: callbacks})
    83  	op, err := newDeploy(factory, curl("cs:quantal/hive-23"))
    84  	c.Assert(err, jc.ErrorIsNil)
    85  	newState, err := op.Prepare(operation.State{})
    86  	c.Check(newState, gc.IsNil)
    87  	c.Check(err, gc.ErrorMatches, "blort")
    88  	c.Check(callbacks.MockClearResolvedFlag.called, jc.IsTrue)
    89  }
    90  
    91  func (s *DeploySuite) TestClearResolvedFlagError_RevertUpgrade(c *gc.C) {
    92  	s.testClearResolvedFlagError(c, (operation.Factory).NewRevertUpgrade)
    93  }
    94  
    95  func (s *DeploySuite) TestClearResolvedFlagError_ResolvedUpgrade(c *gc.C) {
    96  	s.testClearResolvedFlagError(c, (operation.Factory).NewResolvedUpgrade)
    97  }
    98  
    99  func (s *DeploySuite) testNotifyDeployerError(
   100  	c *gc.C, newDeploy newDeploy, expectNotifyRevert bool,
   101  ) {
   102  	callbacks := &DeployCallbacks{
   103  		MockClearResolvedFlag: &MockNoArgs{},
   104  	}
   105  	deployer := &MockDeployer{}
   106  	expectCall := &MockNoArgs{err: errors.New("snh")}
   107  	if expectNotifyRevert {
   108  		deployer.MockNotifyRevert = expectCall
   109  	} else {
   110  		deployer.MockNotifyResolved = expectCall
   111  	}
   112  	factory := operation.NewFactory(operation.FactoryParams{
   113  		Callbacks: callbacks,
   114  		Deployer:  deployer,
   115  	})
   116  	op, err := newDeploy(factory, curl("cs:quantal/hive-23"))
   117  	c.Assert(err, jc.ErrorIsNil)
   118  
   119  	newState, err := op.Prepare(operation.State{})
   120  	c.Check(newState, gc.IsNil)
   121  	c.Check(err, gc.ErrorMatches, "snh")
   122  	c.Check(expectCall.called, jc.IsTrue)
   123  }
   124  
   125  func (s *DeploySuite) TestNotifyDeployerError_RevertUpgrade(c *gc.C) {
   126  	s.testNotifyDeployerError(c, (operation.Factory).NewRevertUpgrade, true)
   127  }
   128  
   129  func (s *DeploySuite) TestNotifyDeployerError_ResolvedUpgrade(c *gc.C) {
   130  	s.testNotifyDeployerError(c, (operation.Factory).NewResolvedUpgrade, false)
   131  }
   132  
   133  func (s *DeploySuite) testPrepareArchiveInfoError(c *gc.C, newDeploy newDeploy) {
   134  	callbacks := &DeployCallbacks{
   135  		MockClearResolvedFlag: &MockNoArgs{},
   136  		MockGetArchiveInfo:    &MockGetArchiveInfo{err: errors.New("pew")},
   137  	}
   138  	deployer := &MockDeployer{
   139  		MockNotifyRevert:   &MockNoArgs{},
   140  		MockNotifyResolved: &MockNoArgs{},
   141  	}
   142  	factory := operation.NewFactory(operation.FactoryParams{
   143  		Deployer:  deployer,
   144  		Callbacks: callbacks,
   145  	})
   146  	op, err := newDeploy(factory, curl("cs:quantal/hive-23"))
   147  	c.Assert(err, jc.ErrorIsNil)
   148  
   149  	newState, err := op.Prepare(operation.State{})
   150  	c.Check(newState, gc.IsNil)
   151  	c.Check(err, gc.ErrorMatches, "pew")
   152  	c.Check(callbacks.MockGetArchiveInfo.gotCharmURL, gc.DeepEquals, curl("cs:quantal/hive-23"))
   153  }
   154  
   155  func (s *DeploySuite) TestPrepareArchiveInfoError_Install(c *gc.C) {
   156  	s.testPrepareArchiveInfoError(c, (operation.Factory).NewInstall)
   157  }
   158  
   159  func (s *DeploySuite) TestPrepareArchiveInfoError_Upgrade(c *gc.C) {
   160  	s.testPrepareArchiveInfoError(c, (operation.Factory).NewUpgrade)
   161  }
   162  
   163  func (s *DeploySuite) TestPrepareArchiveInfoError_RevertUpgrade(c *gc.C) {
   164  	s.testPrepareArchiveInfoError(c, (operation.Factory).NewRevertUpgrade)
   165  }
   166  
   167  func (s *DeploySuite) TestPrepareArchiveInfoError_ResolvedUpgrade(c *gc.C) {
   168  	s.testPrepareArchiveInfoError(c, (operation.Factory).NewResolvedUpgrade)
   169  }
   170  
   171  func (s *DeploySuite) testPrepareStageError(c *gc.C, newDeploy newDeploy) {
   172  	callbacks := &DeployCallbacks{
   173  		MockClearResolvedFlag: &MockNoArgs{},
   174  		MockGetArchiveInfo:    &MockGetArchiveInfo{info: &MockBundleInfo{}},
   175  	}
   176  	deployer := &MockDeployer{
   177  		MockNotifyRevert:   &MockNoArgs{},
   178  		MockNotifyResolved: &MockNoArgs{},
   179  		MockStage:          &MockStage{err: errors.New("squish")},
   180  	}
   181  	var abort <-chan struct{} = make(chan struct{})
   182  	factory := operation.NewFactory(operation.FactoryParams{
   183  		Deployer:  deployer,
   184  		Callbacks: callbacks,
   185  		Abort:     abort,
   186  	})
   187  	op, err := newDeploy(factory, curl("cs:quantal/hive-23"))
   188  	c.Assert(err, jc.ErrorIsNil)
   189  
   190  	newState, err := op.Prepare(operation.State{})
   191  	c.Check(newState, gc.IsNil)
   192  	c.Check(err, gc.ErrorMatches, "squish")
   193  	c.Check(*deployer.MockStage.gotInfo, gc.Equals, callbacks.MockGetArchiveInfo.info)
   194  	c.Check(*deployer.MockStage.gotAbort, gc.Equals, abort)
   195  }
   196  
   197  func (s *DeploySuite) TestPrepareStageError_Install(c *gc.C) {
   198  	s.testPrepareStageError(c, (operation.Factory).NewInstall)
   199  }
   200  
   201  func (s *DeploySuite) TestPrepareStageError_Upgrade(c *gc.C) {
   202  	s.testPrepareStageError(c, (operation.Factory).NewUpgrade)
   203  }
   204  
   205  func (s *DeploySuite) TestPrepareStageError_RevertUpgrade(c *gc.C) {
   206  	s.testPrepareStageError(c, (operation.Factory).NewRevertUpgrade)
   207  }
   208  
   209  func (s *DeploySuite) TestPrepareStageError_ResolvedUpgrade(c *gc.C) {
   210  	s.testPrepareStageError(c, (operation.Factory).NewResolvedUpgrade)
   211  }
   212  
   213  func (s *DeploySuite) testPrepareSetCharmError(c *gc.C, newDeploy newDeploy) {
   214  	callbacks := &DeployCallbacks{
   215  		MockClearResolvedFlag: &MockNoArgs{},
   216  		MockGetArchiveInfo:    &MockGetArchiveInfo{},
   217  		MockSetCurrentCharm:   &MockSetCurrentCharm{err: errors.New("blargh")},
   218  	}
   219  	deployer := &MockDeployer{
   220  		MockNotifyRevert:   &MockNoArgs{},
   221  		MockNotifyResolved: &MockNoArgs{},
   222  		MockStage:          &MockStage{},
   223  	}
   224  	factory := operation.NewFactory(operation.FactoryParams{
   225  		Deployer:  deployer,
   226  		Callbacks: callbacks,
   227  	})
   228  
   229  	op, err := newDeploy(factory, curl("cs:quantal/hive-23"))
   230  	c.Assert(err, jc.ErrorIsNil)
   231  
   232  	newState, err := op.Prepare(operation.State{})
   233  	c.Check(newState, gc.IsNil)
   234  	c.Check(err, gc.ErrorMatches, "blargh")
   235  	c.Check(callbacks.MockSetCurrentCharm.gotCharmURL, gc.DeepEquals, curl("cs:quantal/hive-23"))
   236  }
   237  
   238  func (s *DeploySuite) TestPrepareSetCharmError_Install(c *gc.C) {
   239  	s.testPrepareSetCharmError(c, (operation.Factory).NewInstall)
   240  }
   241  
   242  func (s *DeploySuite) TestPrepareSetCharmError_Upgrade(c *gc.C) {
   243  	s.testPrepareSetCharmError(c, (operation.Factory).NewUpgrade)
   244  }
   245  
   246  func (s *DeploySuite) TestPrepareSetCharmError_RevertUpgrade(c *gc.C) {
   247  	s.testPrepareSetCharmError(c, (operation.Factory).NewRevertUpgrade)
   248  }
   249  
   250  func (s *DeploySuite) TestPrepareSetCharmError_ResolvedUpgrade(c *gc.C) {
   251  	s.testPrepareSetCharmError(c, (operation.Factory).NewResolvedUpgrade)
   252  }
   253  
   254  func (s *DeploySuite) testPrepareSuccess(c *gc.C, newDeploy newDeploy, before, after operation.State) {
   255  	callbacks := NewDeployCallbacks()
   256  	deployer := &MockDeployer{
   257  		MockNotifyRevert:   &MockNoArgs{},
   258  		MockNotifyResolved: &MockNoArgs{},
   259  		MockStage:          &MockStage{},
   260  	}
   261  	factory := operation.NewFactory(operation.FactoryParams{
   262  		Deployer:  deployer,
   263  		Callbacks: callbacks,
   264  	})
   265  	op, err := newDeploy(factory, curl("cs:quantal/nyancat-4"))
   266  	c.Assert(err, jc.ErrorIsNil)
   267  
   268  	newState, err := op.Prepare(before)
   269  	c.Check(err, jc.ErrorIsNil)
   270  	c.Check(newState, gc.DeepEquals, &after)
   271  	c.Check(callbacks.MockSetCurrentCharm.gotCharmURL, gc.DeepEquals, curl("cs:quantal/nyancat-4"))
   272  }
   273  
   274  func (s *DeploySuite) TestPrepareSuccess_Install_BlankSlate(c *gc.C) {
   275  	s.testPrepareSuccess(c,
   276  		(operation.Factory).NewInstall,
   277  		operation.State{},
   278  		operation.State{
   279  			Kind:     operation.Install,
   280  			Step:     operation.Pending,
   281  			CharmURL: curl("cs:quantal/nyancat-4"),
   282  		},
   283  	)
   284  }
   285  
   286  func (s *DeploySuite) TestPrepareSuccess_Install_Queued(c *gc.C) {
   287  	s.testPrepareSuccess(c,
   288  		(operation.Factory).NewInstall,
   289  		operation.State{
   290  			Kind:     operation.Install,
   291  			Step:     operation.Queued,
   292  			CharmURL: curl("cs:quantal/nyancat-4"),
   293  		},
   294  		operation.State{
   295  			Kind:     operation.Install,
   296  			Step:     operation.Pending,
   297  			CharmURL: curl("cs:quantal/nyancat-4"),
   298  		},
   299  	)
   300  }
   301  
   302  func (s *DeploySuite) TestPrepareSuccess_Upgrade_PreservePendingHook(c *gc.C) {
   303  	for i, newDeploy := range []newDeploy{
   304  		(operation.Factory).NewUpgrade,
   305  		(operation.Factory).NewRevertUpgrade,
   306  		(operation.Factory).NewResolvedUpgrade,
   307  	} {
   308  		c.Logf("variant %d", i)
   309  		s.testPrepareSuccess(c,
   310  			newDeploy,
   311  			operation.State{
   312  				Kind: operation.RunHook,
   313  				Step: operation.Pending,
   314  				Hook: &hook.Info{Kind: hooks.ConfigChanged},
   315  			},
   316  			operation.State{
   317  				Kind:     operation.Upgrade,
   318  				Step:     operation.Pending,
   319  				CharmURL: curl("cs:quantal/nyancat-4"),
   320  				Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   321  			},
   322  		)
   323  	}
   324  }
   325  
   326  func (s *DeploySuite) TestPrepareSuccess_Upgrade_PreserveOriginalPendingHook(c *gc.C) {
   327  	for i, newDeploy := range []newDeploy{
   328  		(operation.Factory).NewUpgrade,
   329  		(operation.Factory).NewRevertUpgrade,
   330  		(operation.Factory).NewResolvedUpgrade,
   331  	} {
   332  		c.Logf("variant %d", i)
   333  		s.testPrepareSuccess(c,
   334  			newDeploy,
   335  			operation.State{
   336  				Kind:     operation.Upgrade,
   337  				Step:     operation.Pending,
   338  				CharmURL: curl("cs:quantal/random-23"),
   339  				Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   340  			},
   341  			operation.State{
   342  				Kind:     operation.Upgrade,
   343  				Step:     operation.Pending,
   344  				CharmURL: curl("cs:quantal/nyancat-4"),
   345  				Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   346  			},
   347  		)
   348  	}
   349  }
   350  
   351  func (s *DeploySuite) TestPrepareSuccess_Upgrade_PreserveNoHook(c *gc.C) {
   352  	for i, newDeploy := range []newDeploy{
   353  		(operation.Factory).NewUpgrade,
   354  		(operation.Factory).NewRevertUpgrade,
   355  		(operation.Factory).NewResolvedUpgrade,
   356  	} {
   357  		c.Logf("variant %d", i)
   358  		s.testPrepareSuccess(c,
   359  			newDeploy,
   360  			overwriteState,
   361  			operation.State{
   362  				Kind:               operation.Upgrade,
   363  				Step:               operation.Pending,
   364  				CharmURL:           curl("cs:quantal/nyancat-4"),
   365  				Started:            true,
   366  				CollectMetricsTime: 1234567,
   367  				UpdateStatusTime:   1234567,
   368  			},
   369  		)
   370  	}
   371  }
   372  
   373  func (s *DeploySuite) testExecuteConflictError(c *gc.C, newDeploy newDeploy) {
   374  	callbacks := NewDeployCallbacks()
   375  	deployer := &MockDeployer{
   376  		MockNotifyRevert:   &MockNoArgs{},
   377  		MockNotifyResolved: &MockNoArgs{},
   378  		MockStage:          &MockStage{},
   379  		MockDeploy:         &MockNoArgs{err: charm.ErrConflict},
   380  	}
   381  	factory := operation.NewFactory(operation.FactoryParams{
   382  		Deployer:  deployer,
   383  		Callbacks: callbacks,
   384  	})
   385  	charmURL := curl("cs:quantal/nyancat-4")
   386  	op, err := newDeploy(factory, charmURL)
   387  	c.Assert(err, jc.ErrorIsNil)
   388  	_, err = op.Prepare(operation.State{})
   389  	c.Assert(err, jc.ErrorIsNil)
   390  
   391  	newState, err := op.Execute(operation.State{})
   392  	c.Check(newState, gc.IsNil)
   393  	c.Check(err, gc.ErrorMatches, "cannot deploy charm cs:quantal/nyancat-4")
   394  	errURL, ok := operation.DeployConflictCharmURL(err)
   395  	c.Check(ok, jc.IsTrue)
   396  	c.Check(errURL, gc.DeepEquals, charmURL)
   397  	c.Check(deployer.MockDeploy.called, jc.IsTrue)
   398  }
   399  
   400  func (s *DeploySuite) TestExecuteConflictError_Install(c *gc.C) {
   401  	s.testExecuteError(c, (operation.Factory).NewInstall)
   402  }
   403  
   404  func (s *DeploySuite) TestExecuteConflictError_Upgrade(c *gc.C) {
   405  	s.testExecuteError(c, (operation.Factory).NewUpgrade)
   406  }
   407  
   408  func (s *DeploySuite) TestExecuteConflictError_RevertUpgrade(c *gc.C) {
   409  	s.testExecuteError(c, (operation.Factory).NewRevertUpgrade)
   410  }
   411  
   412  func (s *DeploySuite) TestExecuteConflictError_ResolvedUpgrade(c *gc.C) {
   413  	s.testExecuteError(c, (operation.Factory).NewResolvedUpgrade)
   414  }
   415  
   416  func (s *DeploySuite) testExecuteError(c *gc.C, newDeploy newDeploy) {
   417  	callbacks := NewDeployCallbacks()
   418  	deployer := &MockDeployer{
   419  		MockNotifyRevert:   &MockNoArgs{},
   420  		MockNotifyResolved: &MockNoArgs{},
   421  		MockStage:          &MockStage{},
   422  		MockDeploy:         &MockNoArgs{err: errors.New("rasp")},
   423  	}
   424  	factory := operation.NewFactory(operation.FactoryParams{
   425  		Deployer:  deployer,
   426  		Callbacks: callbacks,
   427  	})
   428  	op, err := newDeploy(factory, curl("cs:quantal/nyancat-4"))
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	_, err = op.Prepare(operation.State{})
   431  	c.Assert(err, jc.ErrorIsNil)
   432  
   433  	newState, err := op.Execute(operation.State{})
   434  	c.Check(newState, gc.IsNil)
   435  	c.Check(err, gc.ErrorMatches, "rasp")
   436  	c.Check(deployer.MockDeploy.called, jc.IsTrue)
   437  }
   438  
   439  func (s *DeploySuite) TestExecuteError_Install(c *gc.C) {
   440  	s.testExecuteError(c, (operation.Factory).NewInstall)
   441  }
   442  
   443  func (s *DeploySuite) TestExecuteError_Upgrade(c *gc.C) {
   444  	s.testExecuteError(c, (operation.Factory).NewUpgrade)
   445  }
   446  
   447  func (s *DeploySuite) TestExecuteError_RevertUpgrade(c *gc.C) {
   448  	s.testExecuteError(c, (operation.Factory).NewRevertUpgrade)
   449  }
   450  
   451  func (s *DeploySuite) TestExecuteError_ResolvedUpgrade(c *gc.C) {
   452  	s.testExecuteError(c, (operation.Factory).NewResolvedUpgrade)
   453  }
   454  
   455  func (s *DeploySuite) testExecuteSuccess(
   456  	c *gc.C, newDeploy newDeploy, before, after operation.State,
   457  ) {
   458  	deployer := NewMockDeployer()
   459  	callbacks := NewDeployCallbacks()
   460  	factory := operation.NewFactory(operation.FactoryParams{
   461  		Deployer:  deployer,
   462  		Callbacks: callbacks,
   463  	})
   464  	op, err := newDeploy(factory, curl("cs:quantal/lol-1"))
   465  	c.Assert(err, jc.ErrorIsNil)
   466  
   467  	midState, err := op.Prepare(before)
   468  	c.Assert(err, jc.ErrorIsNil)
   469  	c.Assert(midState, gc.NotNil)
   470  
   471  	newState, err := op.Execute(*midState)
   472  	c.Check(err, jc.ErrorIsNil)
   473  	c.Check(newState, gc.DeepEquals, &after)
   474  	c.Check(deployer.MockDeploy.called, jc.IsTrue)
   475  }
   476  
   477  func (s *DeploySuite) TestExecuteSuccess_Install_BlankSlate(c *gc.C) {
   478  	s.testExecuteSuccess(c,
   479  		(operation.Factory).NewInstall,
   480  		operation.State{},
   481  		operation.State{
   482  			Kind:     operation.Install,
   483  			Step:     operation.Done,
   484  			CharmURL: curl("cs:quantal/lol-1"),
   485  		},
   486  	)
   487  }
   488  
   489  func (s *DeploySuite) TestExecuteSuccess_Install_Queued(c *gc.C) {
   490  	s.testExecuteSuccess(c,
   491  		(operation.Factory).NewInstall,
   492  		operation.State{
   493  			Kind:     operation.Install,
   494  			Step:     operation.Queued,
   495  			CharmURL: curl("cs:quantal/lol-1"),
   496  		},
   497  		operation.State{
   498  			Kind:     operation.Install,
   499  			Step:     operation.Done,
   500  			CharmURL: curl("cs:quantal/lol-1"),
   501  		},
   502  	)
   503  }
   504  
   505  func (s *DeploySuite) TestExecuteSuccess_Upgrade_PreservePendingHook(c *gc.C) {
   506  	for i, newDeploy := range []newDeploy{
   507  		(operation.Factory).NewUpgrade,
   508  		(operation.Factory).NewRevertUpgrade,
   509  		(operation.Factory).NewResolvedUpgrade,
   510  	} {
   511  		c.Logf("variant %d", i)
   512  		s.testExecuteSuccess(c,
   513  			newDeploy,
   514  			operation.State{
   515  				Kind: operation.RunHook,
   516  				Step: operation.Pending,
   517  				Hook: &hook.Info{Kind: hooks.ConfigChanged},
   518  			},
   519  			operation.State{
   520  				Kind:     operation.Upgrade,
   521  				Step:     operation.Done,
   522  				CharmURL: curl("cs:quantal/lol-1"),
   523  				Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   524  			},
   525  		)
   526  	}
   527  }
   528  
   529  func (s *DeploySuite) TestExecuteSuccess_Upgrade_PreserveOriginalPendingHook(c *gc.C) {
   530  	for i, newDeploy := range []newDeploy{
   531  		(operation.Factory).NewUpgrade,
   532  		(operation.Factory).NewRevertUpgrade,
   533  		(operation.Factory).NewResolvedUpgrade,
   534  	} {
   535  		c.Logf("variant %d", i)
   536  		s.testExecuteSuccess(c,
   537  			newDeploy,
   538  			operation.State{
   539  				Kind:     operation.Upgrade,
   540  				Step:     operation.Pending,
   541  				CharmURL: curl("cs:quantal/wild-9"),
   542  				Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   543  			},
   544  			operation.State{
   545  				Kind:     operation.Upgrade,
   546  				Step:     operation.Done,
   547  				CharmURL: curl("cs:quantal/lol-1"),
   548  				Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   549  			},
   550  		)
   551  	}
   552  }
   553  
   554  func (s *DeploySuite) TestExecuteSuccess_Upgrade_PreserveNoHook(c *gc.C) {
   555  	for i, newDeploy := range []newDeploy{
   556  		(operation.Factory).NewUpgrade,
   557  		(operation.Factory).NewRevertUpgrade,
   558  		(operation.Factory).NewResolvedUpgrade,
   559  	} {
   560  		c.Logf("variant %d", i)
   561  		s.testExecuteSuccess(c,
   562  			newDeploy,
   563  			overwriteState,
   564  			operation.State{
   565  				Kind:               operation.Upgrade,
   566  				Step:               operation.Done,
   567  				CharmURL:           curl("cs:quantal/lol-1"),
   568  				Started:            true,
   569  				CollectMetricsTime: 1234567,
   570  				UpdateStatusTime:   1234567,
   571  			},
   572  		)
   573  	}
   574  }
   575  
   576  func (s *DeploySuite) testCommitMetricsError(c *gc.C, newDeploy newDeploy) {
   577  	callbacks := NewDeployCommitCallbacks(errors.New("glukh"))
   578  	factory := operation.NewFactory(operation.FactoryParams{Callbacks: callbacks})
   579  	op, err := newDeploy(factory, curl("cs:quantal/x-0"))
   580  	c.Assert(err, jc.ErrorIsNil)
   581  	newState, err := op.Commit(operation.State{})
   582  	c.Check(err, gc.ErrorMatches, "glukh")
   583  	c.Check(newState, gc.IsNil)
   584  }
   585  
   586  func (s *DeploySuite) TestCommitMetricsError_Install(c *gc.C) {
   587  	s.testCommitMetricsError(c, (operation.Factory).NewInstall)
   588  }
   589  
   590  func (s *DeploySuite) TestCommitMetricsError_Upgrade(c *gc.C) {
   591  	s.testCommitMetricsError(c, (operation.Factory).NewUpgrade)
   592  }
   593  
   594  func (s *DeploySuite) TestCommitMetricsError_RevertUpgrade(c *gc.C) {
   595  	s.testCommitMetricsError(c, (operation.Factory).NewRevertUpgrade)
   596  }
   597  
   598  func (s *DeploySuite) TestCommitMetricsError_ResolvedUpgrade(c *gc.C) {
   599  	s.testCommitMetricsError(c, (operation.Factory).NewResolvedUpgrade)
   600  }
   601  
   602  func (s *DeploySuite) TestCommitQueueInstallHook(c *gc.C) {
   603  	callbacks := NewDeployCommitCallbacks(nil)
   604  	factory := operation.NewFactory(operation.FactoryParams{Callbacks: callbacks})
   605  	op, err := factory.NewInstall(curl("cs:quantal/x-0"))
   606  	c.Assert(err, jc.ErrorIsNil)
   607  	newState, err := op.Commit(operation.State{
   608  		Kind:     operation.Install,
   609  		Step:     operation.Done,
   610  		CharmURL: nil, // doesn't actually matter here
   611  	})
   612  	c.Check(err, jc.ErrorIsNil)
   613  	c.Check(newState, gc.DeepEquals, &operation.State{
   614  		Kind: operation.RunHook,
   615  		Step: operation.Queued,
   616  		Hook: &hook.Info{Kind: hooks.Install},
   617  	})
   618  }
   619  
   620  func (s *DeploySuite) testCommitQueueUpgradeHook(c *gc.C, newDeploy newDeploy) {
   621  	callbacks := NewDeployCommitCallbacks(nil)
   622  	factory := operation.NewFactory(operation.FactoryParams{Callbacks: callbacks})
   623  	op, err := newDeploy(factory, curl("cs:quantal/x-0"))
   624  	c.Assert(err, jc.ErrorIsNil)
   625  	newState, err := op.Commit(operation.State{
   626  		Kind:     operation.Upgrade,
   627  		Step:     operation.Done,
   628  		CharmURL: nil, // doesn't actually matter here
   629  	})
   630  	c.Check(err, jc.ErrorIsNil)
   631  	c.Check(newState, gc.DeepEquals, &operation.State{
   632  		Kind: operation.RunHook,
   633  		Step: operation.Queued,
   634  		Hook: &hook.Info{Kind: hooks.UpgradeCharm},
   635  	})
   636  }
   637  
   638  func (s *DeploySuite) TestCommitQueueUpgradeHook_Upgrade(c *gc.C) {
   639  	s.testCommitQueueUpgradeHook(c, (operation.Factory).NewUpgrade)
   640  }
   641  
   642  func (s *DeploySuite) TestCommitQueueUpgradeHook_RevertUpgrade(c *gc.C) {
   643  	s.testCommitQueueUpgradeHook(c, (operation.Factory).NewRevertUpgrade)
   644  }
   645  
   646  func (s *DeploySuite) TestCommitQueueUpgradeHook_ResolvedUpgrade(c *gc.C) {
   647  	s.testCommitQueueUpgradeHook(c, (operation.Factory).NewResolvedUpgrade)
   648  }
   649  
   650  func (s *DeploySuite) testCommitInterruptedHook(c *gc.C, newDeploy newDeploy) {
   651  	callbacks := NewDeployCommitCallbacks(nil)
   652  	factory := operation.NewFactory(operation.FactoryParams{Callbacks: callbacks})
   653  	op, err := newDeploy(factory, curl("cs:quantal/x-0"))
   654  	c.Assert(err, jc.ErrorIsNil)
   655  	newState, err := op.Commit(operation.State{
   656  		Kind:     operation.Upgrade,
   657  		Step:     operation.Done,
   658  		CharmURL: nil, // doesn't actually matter here
   659  		Hook:     &hook.Info{Kind: hooks.ConfigChanged},
   660  	})
   661  	c.Check(err, jc.ErrorIsNil)
   662  	c.Check(newState, gc.DeepEquals, &operation.State{
   663  		Kind: operation.RunHook,
   664  		Step: operation.Pending,
   665  		Hook: &hook.Info{Kind: hooks.ConfigChanged},
   666  	})
   667  }
   668  
   669  func (s *DeploySuite) TestCommitInterruptedHook_Upgrade(c *gc.C) {
   670  	s.testCommitInterruptedHook(c, (operation.Factory).NewUpgrade)
   671  }
   672  
   673  func (s *DeploySuite) TestCommitInterruptedHook_RevertUpgrade(c *gc.C) {
   674  	s.testCommitInterruptedHook(c, (operation.Factory).NewRevertUpgrade)
   675  }
   676  
   677  func (s *DeploySuite) TestCommitInterruptedHook_ResolvedUpgrade(c *gc.C) {
   678  	s.testCommitInterruptedHook(c, (operation.Factory).NewResolvedUpgrade)
   679  }
   680  
   681  func (s *DeploySuite) testDoesNotNeedGlobalMachineLock(c *gc.C, newDeploy newDeploy) {
   682  	factory := operation.NewFactory(operation.FactoryParams{})
   683  	op, err := newDeploy(factory, curl("cs:quantal/x-0"))
   684  	c.Assert(err, jc.ErrorIsNil)
   685  	c.Assert(op.NeedsGlobalMachineLock(), jc.IsFalse)
   686  }
   687  
   688  func (s *DeploySuite) TestDoesNotNeedGlobalMachineLock_Install(c *gc.C) {
   689  	s.testDoesNotNeedGlobalMachineLock(c, (operation.Factory).NewInstall)
   690  }
   691  
   692  func (s *DeploySuite) TestDoesNotNeedGlobalMachineLock_Upgrade(c *gc.C) {
   693  	s.testDoesNotNeedGlobalMachineLock(c, (operation.Factory).NewUpgrade)
   694  }
   695  
   696  func (s *DeploySuite) TestDoesNotNeedGlobalMachineLock_RevertUpgrade(c *gc.C) {
   697  	s.testDoesNotNeedGlobalMachineLock(c, (operation.Factory).NewRevertUpgrade)
   698  }
   699  
   700  func (s *DeploySuite) TestDoesNotNeedGlobalMachineLock_ResolvedUpgrade(c *gc.C) {
   701  	s.testDoesNotNeedGlobalMachineLock(c, (operation.Factory).NewResolvedUpgrade)
   702  }