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 }