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