github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/resolver/opfactory_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package resolver_test
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/juju/charm/v12/hooks"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/core/model"
    14  	"github.com/juju/juju/testing"
    15  	"github.com/juju/juju/worker/uniter/hook"
    16  	"github.com/juju/juju/worker/uniter/operation"
    17  	"github.com/juju/juju/worker/uniter/remotestate"
    18  	"github.com/juju/juju/worker/uniter/resolver"
    19  )
    20  
    21  type ResolverOpFactorySuite struct {
    22  	testing.BaseSuite
    23  	opFactory *mockOpFactory
    24  }
    25  
    26  var _ = gc.Suite(&ResolverOpFactorySuite{})
    27  
    28  func (s *ResolverOpFactorySuite) SetUpTest(c *gc.C) {
    29  	s.BaseSuite.SetUpTest(c)
    30  	s.opFactory = &mockOpFactory{}
    31  }
    32  
    33  func (s *ResolverOpFactorySuite) TestInitialState(c *gc.C) {
    34  	f := resolver.NewResolverOpFactory(s.opFactory)
    35  	c.Assert(f.LocalState, jc.DeepEquals, &resolver.LocalState{})
    36  	c.Assert(f.RemoteState, jc.DeepEquals, remotestate.Snapshot{})
    37  }
    38  
    39  func (s *ResolverOpFactorySuite) TestUpdateStatusChanged(c *gc.C) {
    40  	s.testUpdateStatusChanged(c, resolver.ResolverOpFactory.NewRunHook)
    41  	s.testUpdateStatusChanged(c, resolver.ResolverOpFactory.NewSkipHook)
    42  }
    43  
    44  func (s *ResolverOpFactorySuite) testUpdateStatusChanged(
    45  	c *gc.C, meth func(resolver.ResolverOpFactory, hook.Info) (operation.Operation, error),
    46  ) {
    47  	f := resolver.NewResolverOpFactory(s.opFactory)
    48  	f.RemoteState.UpdateStatusVersion = 1
    49  
    50  	op, err := f.NewRunHook(hook.Info{Kind: hooks.UpdateStatus})
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	f.RemoteState.UpdateStatusVersion = 2
    53  
    54  	_, err = op.Commit(operation.State{})
    55  	c.Assert(err, jc.ErrorIsNil)
    56  
    57  	// Local state's UpdateStatusVersion should be set to what
    58  	// RemoteState's UpdateStatusVersion was when the operation
    59  	// was constructed.
    60  	c.Assert(f.LocalState.UpdateStatusVersion, gc.Equals, 1)
    61  }
    62  
    63  func (s *ResolverOpFactorySuite) TestConfigChanged(c *gc.C) {
    64  	s.testConfigChanged(c, resolver.ResolverOpFactory.NewRunHook)
    65  	s.testConfigChanged(c, resolver.ResolverOpFactory.NewSkipHook)
    66  }
    67  
    68  func (s *ResolverOpFactorySuite) TestUpgradeSeriesStatusChanged(c *gc.C) {
    69  	f := resolver.NewResolverOpFactory(s.opFactory)
    70  
    71  	// The initial state.
    72  	f.LocalState.UpgradeMachineStatus = model.UpgradeSeriesNotStarted
    73  	f.RemoteState.UpgradeMachineStatus = model.UpgradeSeriesPrepareStarted
    74  
    75  	op, err := f.NewRunHook(hook.Info{Kind: hooks.PreSeriesUpgrade})
    76  	c.Assert(err, jc.ErrorIsNil)
    77  
    78  	_, err = op.Prepare(operation.State{})
    79  	c.Assert(err, jc.ErrorIsNil)
    80  
    81  	c.Assert(f.LocalState.UpgradeMachineStatus, gc.Equals, model.UpgradeSeriesPrepareStarted)
    82  	f.RemoteState.UpgradeMachineStatus = model.UpgradeSeriesPrepareCompleted
    83  
    84  	_, err = op.Commit(operation.State{})
    85  	c.Assert(err, jc.ErrorIsNil)
    86  
    87  	c.Assert(f.LocalState.UpgradeMachineStatus, gc.Equals, model.UpgradeSeriesPrepareCompleted)
    88  }
    89  
    90  func (s *ResolverOpFactorySuite) TestNewHookError(c *gc.C) {
    91  	s.opFactory.SetErrors(
    92  		errors.New("NewRunHook fails"),
    93  		errors.New("NewSkipHook fails"),
    94  	)
    95  	f := resolver.NewResolverOpFactory(s.opFactory)
    96  	_, err := f.NewRunHook(hook.Info{Kind: hooks.ConfigChanged})
    97  	c.Assert(err, gc.ErrorMatches, "NewRunHook fails")
    98  	_, err = f.NewSkipHook(hook.Info{Kind: hooks.ConfigChanged})
    99  	c.Assert(err, gc.ErrorMatches, "NewSkipHook fails")
   100  }
   101  
   102  func (s *ResolverOpFactorySuite) testConfigChanged(
   103  	c *gc.C, meth func(resolver.ResolverOpFactory, hook.Info) (operation.Operation, error),
   104  ) {
   105  	f := resolver.NewResolverOpFactory(s.opFactory)
   106  	f.RemoteState.ConfigHash = "confighash"
   107  	f.RemoteState.TrustHash = "trusthash"
   108  	f.RemoteState.AddressesHash = "addresseshash"
   109  	f.RemoteState.UpdateStatusVersion = 3
   110  
   111  	op, err := f.NewRunHook(hook.Info{Kind: hooks.ConfigChanged})
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	f.RemoteState.ConfigHash = "newhash"
   114  	f.RemoteState.TrustHash = "badhash"
   115  	f.RemoteState.AddressesHash = "differenthash"
   116  	f.RemoteState.UpdateStatusVersion = 4
   117  
   118  	resultState, err := op.Commit(operation.State{})
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(resultState, gc.NotNil)
   121  
   122  	// Local state's UpdateStatusVersion should be set to what
   123  	// RemoteState's UpdateStatusVersion was when the operation
   124  	// was constructed.
   125  	c.Assert(f.LocalState.UpdateStatusVersion, gc.Equals, 3)
   126  	// The hashes need to be set on the result state, because that is
   127  	// written to disk by the executor before the next step is picked.
   128  	c.Assert(resultState.ConfigHash, gc.Equals, "confighash")
   129  	c.Assert(resultState.TrustHash, gc.Equals, "trusthash")
   130  	c.Assert(resultState.AddressesHash, gc.Equals, "addresseshash")
   131  }
   132  
   133  func (s *ResolverOpFactorySuite) TestLeaderSettingsChanged(c *gc.C) {
   134  	s.testLeaderSettingsChanged(c, resolver.ResolverOpFactory.NewRunHook)
   135  	s.testLeaderSettingsChanged(c, resolver.ResolverOpFactory.NewSkipHook)
   136  }
   137  
   138  func (s *ResolverOpFactorySuite) testLeaderSettingsChanged(
   139  	c *gc.C, meth func(resolver.ResolverOpFactory, hook.Info) (operation.Operation, error),
   140  ) {
   141  	f := resolver.NewResolverOpFactory(s.opFactory)
   142  	f.RemoteState.LeaderSettingsVersion = 1
   143  	f.RemoteState.UpdateStatusVersion = 3
   144  
   145  	op, err := meth(f, hook.Info{Kind: hooks.LeaderSettingsChanged})
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	f.RemoteState.LeaderSettingsVersion = 2
   148  	f.RemoteState.UpdateStatusVersion = 4
   149  
   150  	_, err = op.Commit(operation.State{})
   151  	c.Assert(err, jc.ErrorIsNil)
   152  
   153  	// Local state's LeaderSettingsVersion should be set to what
   154  	// RemoteState's LeaderSettingsVersion was when the operation
   155  	// was constructed.
   156  	c.Assert(f.LocalState.LeaderSettingsVersion, gc.Equals, 1)
   157  	c.Assert(f.LocalState.UpdateStatusVersion, gc.Equals, 3)
   158  }
   159  
   160  func (s *ResolverOpFactorySuite) TestUpgrade(c *gc.C) {
   161  	s.testUpgrade(c, resolver.ResolverOpFactory.NewUpgrade)
   162  	s.testUpgrade(c, resolver.ResolverOpFactory.NewRevertUpgrade)
   163  	s.testUpgrade(c, resolver.ResolverOpFactory.NewResolvedUpgrade)
   164  }
   165  
   166  func (s *ResolverOpFactorySuite) testUpgrade(
   167  	c *gc.C, meth func(resolver.ResolverOpFactory, string) (operation.Operation, error),
   168  ) {
   169  	f := resolver.NewResolverOpFactory(s.opFactory)
   170  	f.LocalState.Conflicted = true
   171  	curl := "ch:trusty/mysql"
   172  	op, err := meth(f, curl)
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	_, err = op.Commit(operation.State{})
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	c.Assert(f.LocalState.CharmURL, jc.DeepEquals, curl)
   177  	c.Assert(f.LocalState.Conflicted, jc.IsFalse)
   178  }
   179  
   180  func (s *ResolverOpFactorySuite) TestRemoteInit(c *gc.C) {
   181  	f := resolver.NewResolverOpFactory(s.opFactory)
   182  	f.LocalState.OutdatedRemoteCharm = true
   183  	op, err := f.NewRemoteInit(remotestate.ContainerRunningStatus{})
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	_, err = op.Commit(operation.State{})
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(f.LocalState.OutdatedRemoteCharm, jc.IsFalse)
   188  }
   189  
   190  func (s *ResolverOpFactorySuite) TestSkipRemoteInit(c *gc.C) {
   191  	f := resolver.NewResolverOpFactory(s.opFactory)
   192  	f.LocalState.OutdatedRemoteCharm = true
   193  	op, err := f.NewSkipRemoteInit(false)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	_, err = op.Commit(operation.State{})
   196  	c.Assert(err, jc.ErrorIsNil)
   197  	c.Assert(f.LocalState.OutdatedRemoteCharm, jc.IsTrue)
   198  }
   199  
   200  func (s *ResolverOpFactorySuite) TestNewUpgradeError(c *gc.C) {
   201  	curl := "ch:trusty/mysql"
   202  	s.opFactory.SetErrors(
   203  		errors.New("NewUpgrade fails"),
   204  		errors.New("NewRevertUpgrade fails"),
   205  		errors.New("NewResolvedUpgrade fails"),
   206  	)
   207  	f := resolver.NewResolverOpFactory(s.opFactory)
   208  	_, err := f.NewUpgrade(curl)
   209  	c.Assert(err, gc.ErrorMatches, "NewUpgrade fails")
   210  	_, err = f.NewRevertUpgrade(curl)
   211  	c.Assert(err, gc.ErrorMatches, "NewRevertUpgrade fails")
   212  	_, err = f.NewResolvedUpgrade(curl)
   213  	c.Assert(err, gc.ErrorMatches, "NewResolvedUpgrade fails")
   214  }
   215  
   216  func (s *ResolverOpFactorySuite) TestCommitError(c *gc.C) {
   217  	f := resolver.NewResolverOpFactory(s.opFactory)
   218  
   219  	s.opFactory.op.commit = func(operation.State) (*operation.State, error) {
   220  		return nil, errors.New("commit fails")
   221  	}
   222  	op, err := f.NewUpgrade("ch:trusty/mysql")
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	_, err = op.Commit(operation.State{})
   225  	c.Assert(err, gc.ErrorMatches, "commit fails")
   226  	// Local state should not have been updated. We use the same code
   227  	// internally for all operations, so it suffices to test just the
   228  	// upgrade case.
   229  	c.Assert(f.LocalState.CharmURL, gc.Equals, "")
   230  }
   231  
   232  func (s *ResolverOpFactorySuite) TestActionsCommit(c *gc.C) {
   233  	f := resolver.NewResolverOpFactory(s.opFactory)
   234  	f.RemoteState.ActionsPending = []string{"action 1", "action 2", "action 3"}
   235  	f.LocalState.CompletedActions = map[string]struct{}{}
   236  	op, err := f.NewAction("action 1")
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	_, err = op.Commit(operation.State{})
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Assert(f.LocalState.CompletedActions, gc.DeepEquals, map[string]struct{}{
   241  		"action 1": {},
   242  	})
   243  }
   244  
   245  func (s *ResolverOpFactorySuite) TestActionsTrimming(c *gc.C) {
   246  	f := resolver.NewResolverOpFactory(s.opFactory)
   247  	f.RemoteState.ActionsPending = []string{"c", "d"}
   248  	f.LocalState.CompletedActions = map[string]struct{}{
   249  		"a": {},
   250  		"b": {},
   251  		"c": {},
   252  	}
   253  	op, err := f.NewAction("d")
   254  	c.Assert(err, jc.ErrorIsNil)
   255  	_, err = op.Commit(operation.State{})
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	c.Assert(f.LocalState.CompletedActions, gc.DeepEquals, map[string]struct{}{
   258  		"c": {},
   259  		"d": {},
   260  	})
   261  }
   262  
   263  func (s *ResolverOpFactorySuite) TestFailActionsCommit(c *gc.C) {
   264  	f := resolver.NewResolverOpFactory(s.opFactory)
   265  	f.RemoteState.ActionsPending = []string{"action 1", "action 2", "action 3"}
   266  	f.LocalState.CompletedActions = map[string]struct{}{}
   267  	op, err := f.NewFailAction("action 1")
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	_, err = op.Commit(operation.State{})
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	c.Assert(f.LocalState.CompletedActions, gc.DeepEquals, map[string]struct{}{
   272  		"action 1": {},
   273  	})
   274  }
   275  
   276  func (s *ResolverOpFactorySuite) TestFailActionsTrimming(c *gc.C) {
   277  	f := resolver.NewResolverOpFactory(s.opFactory)
   278  	f.RemoteState.ActionsPending = []string{"c", "d"}
   279  	f.LocalState.CompletedActions = map[string]struct{}{
   280  		"a": {},
   281  		"b": {},
   282  		"c": {},
   283  	}
   284  	op, err := f.NewFailAction("d")
   285  	c.Assert(err, jc.ErrorIsNil)
   286  	_, err = op.Commit(operation.State{})
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	c.Assert(f.LocalState.CompletedActions, gc.DeepEquals, map[string]struct{}{
   289  		"c": {},
   290  		"d": {},
   291  	})
   292  }