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