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 }