github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/operation/executor_test.go (about) 1 // Copyright 2014 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/charm/v12/hooks" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "go.uber.org/mock/gomock" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/yaml.v2" 17 18 "github.com/juju/juju/rpc/params" 19 "github.com/juju/juju/worker/uniter/hook" 20 "github.com/juju/juju/worker/uniter/operation" 21 "github.com/juju/juju/worker/uniter/operation/mocks" 22 "github.com/juju/juju/worker/uniter/remotestate" 23 ) 24 25 type NewExecutorSuite struct { 26 testing.IsolationSuite 27 28 mockStateRW *mocks.MockUnitStateReadWriter 29 } 30 31 var _ = gc.Suite(&NewExecutorSuite{}) 32 33 func failAcquireLock(_, _ string) (func(), error) { 34 return nil, errors.New("wat") 35 } 36 37 func (s *NewExecutorSuite) SetUpTest(c *gc.C) { 38 s.IsolationSuite.SetUpTest(c) 39 } 40 41 func (s *NewExecutorSuite) setupMocks(c *gc.C) *gomock.Controller { 42 ctlr := gomock.NewController(c) 43 s.mockStateRW = mocks.NewMockUnitStateReadWriter(ctlr) 44 return ctlr 45 } 46 47 func (s *NewExecutorSuite) expectState(c *gc.C, st operation.State) { 48 data, err := yaml.Marshal(st) 49 c.Assert(err, jc.ErrorIsNil) 50 stStr := string(data) 51 52 mExp := s.mockStateRW.EXPECT() 53 mExp.State().Return(params.UnitStateResult{UniterState: stStr}, nil) 54 } 55 56 func (s *NewExecutorSuite) expectStateNil() { 57 mExp := s.mockStateRW.EXPECT() 58 mExp.State().Return(params.UnitStateResult{}, nil) 59 } 60 61 func (s *NewExecutorSuite) TestNewExecutorInvalidStateRead(c *gc.C) { 62 defer s.setupMocks(c).Finish() 63 initialState := operation.State{Step: operation.Queued} 64 s.expectState(c, initialState) 65 cfg := operation.ExecutorConfig{ 66 StateReadWriter: s.mockStateRW, 67 InitialState: initialState, 68 AcquireLock: failAcquireLock, 69 Logger: loggo.GetLogger("test"), 70 } 71 executor, err := operation.NewExecutor("test", cfg) 72 c.Assert(executor, gc.IsNil) 73 c.Assert(err, gc.ErrorMatches, `validation of uniter state: invalid operation state: .*`) 74 } 75 76 func (s *NewExecutorSuite) TestNewExecutorNoInitialState(c *gc.C) { 77 defer s.setupMocks(c).Finish() 78 s.expectStateNil() 79 initialState := operation.State{Step: operation.Queued} 80 cfg := operation.ExecutorConfig{ 81 StateReadWriter: s.mockStateRW, 82 InitialState: initialState, 83 AcquireLock: failAcquireLock, 84 Logger: loggo.GetLogger("test"), 85 } 86 executor, err := operation.NewExecutor("test", cfg) 87 c.Assert(err, jc.ErrorIsNil) 88 c.Assert(executor.State(), gc.DeepEquals, initialState) 89 } 90 91 func (s *NewExecutorSuite) TestNewExecutorValidFile(c *gc.C) { 92 // note: this content matches valid persistent state as of 1.21; we expect 93 // that "hook" will have to become "last-hook" to enable action execution 94 // during hook error states. If you do this, please leave at least one test 95 // with this form of the yaml in place. 96 defer s.setupMocks(c).Finish() 97 s.mockStateRW.EXPECT().State().Return(params.UnitStateResult{UniterState: "started: true\nop: continue\nopstep: pending\n"}, nil) 98 cfg := operation.ExecutorConfig{ 99 StateReadWriter: s.mockStateRW, 100 InitialState: operation.State{Step: operation.Queued}, 101 AcquireLock: failAcquireLock, 102 Logger: loggo.GetLogger("test"), 103 } 104 executor, err := operation.NewExecutor("test", cfg) 105 c.Assert(err, jc.ErrorIsNil) 106 c.Assert(executor.State(), gc.DeepEquals, operation.State{ 107 Kind: operation.Continue, 108 Step: operation.Pending, 109 Started: true, 110 }) 111 } 112 113 type ExecutorSuite struct { 114 testing.IsolationSuite 115 mockStateRW *mocks.MockUnitStateReadWriter 116 } 117 118 var _ = gc.Suite(&ExecutorSuite{}) 119 120 func (s *ExecutorSuite) setupMocks(c *gc.C) *gomock.Controller { 121 ctlr := gomock.NewController(c) 122 s.mockStateRW = mocks.NewMockUnitStateReadWriter(ctlr) 123 return ctlr 124 } 125 126 func (s *ExecutorSuite) expectSetState(c *gc.C, st operation.State) { 127 data, err := yaml.Marshal(st) 128 c.Assert(err, jc.ErrorIsNil) 129 strUniterState := string(data) 130 131 mExp := s.mockStateRW.EXPECT() 132 mExp.SetState(unitStateMatcher{c: c, expected: strUniterState}).Return(nil) 133 } 134 135 func (s *ExecutorSuite) expectState(c *gc.C, st operation.State) { 136 data, err := yaml.Marshal(st) 137 c.Assert(err, jc.ErrorIsNil) 138 strState := string(data) 139 140 mExp := s.mockStateRW.EXPECT() 141 mExp.State().Return(params.UnitStateResult{UniterState: strState}, nil) 142 } 143 144 func (s *ExecutorSuite) expectConfigChangedPendingOp(c *gc.C) operation.State { 145 op := operation.State{ 146 Kind: operation.RunHook, 147 Step: operation.Pending, 148 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 149 } 150 s.expectSetState(c, op) 151 return op 152 } 153 154 func (s *ExecutorSuite) expectConfigChangedDoneOp(c *gc.C) operation.State { 155 op := operation.State{ 156 Kind: operation.RunHook, 157 Step: operation.Done, 158 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 159 } 160 s.expectSetState(c, op) 161 return op 162 } 163 164 func (s *ExecutorSuite) expectStartQueuedOp(c *gc.C) operation.State { 165 op := operation.State{ 166 Kind: operation.RunHook, 167 Step: operation.Queued, 168 Hook: &hook.Info{Kind: hooks.Start}, 169 } 170 s.expectSetState(c, op) 171 return op 172 } 173 174 func (s *ExecutorSuite) expectStartPendingOp(c *gc.C) operation.State { 175 op := operation.State{ 176 Kind: operation.RunHook, 177 Step: operation.Pending, 178 Hook: &hook.Info{Kind: hooks.Start}, 179 } 180 s.expectSetState(c, op) 181 return op 182 } 183 184 func (s *ExecutorSuite) newExecutor(c *gc.C, st *operation.State) operation.Executor { 185 // ensure s.setupMocks called first. 186 c.Assert(s.mockStateRW, gc.NotNil) 187 188 s.expectState(c, *st) 189 cfg := operation.ExecutorConfig{ 190 StateReadWriter: s.mockStateRW, 191 InitialState: operation.State{Step: operation.Queued}, 192 AcquireLock: failAcquireLock, 193 Logger: loggo.GetLogger("test"), 194 } 195 executor, err := operation.NewExecutor("test", cfg) 196 c.Assert(err, jc.ErrorIsNil) 197 return executor 198 } 199 200 func justInstalledState() operation.State { 201 return operation.State{ 202 Kind: operation.Continue, 203 Step: operation.Pending, 204 } 205 } 206 207 func (s *ExecutorSuite) TestSucceedNoStateChanges(c *gc.C) { 208 defer s.setupMocks(c).Finish() 209 initialState := justInstalledState() 210 executor := s.newExecutor(c, &initialState) 211 212 prepare := newStep(nil, nil) 213 execute := newStep(nil, nil) 214 commit := newStep(nil, nil) 215 op := &mockOperation{ 216 prepare: prepare, 217 execute: execute, 218 commit: commit, 219 } 220 221 err := executor.Run(op, nil) 222 c.Assert(err, jc.ErrorIsNil) 223 224 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 225 c.Assert(execute.gotState, gc.DeepEquals, initialState) 226 c.Assert(commit.gotState, gc.DeepEquals, initialState) 227 c.Assert(executor.State(), gc.DeepEquals, initialState) 228 } 229 230 func (s *ExecutorSuite) TestSucceedWithStateChanges(c *gc.C) { 231 defer s.setupMocks(c).Finish() 232 233 prepareOp := s.expectConfigChangedPendingOp(c) 234 executeOp := s.expectConfigChangedDoneOp(c) 235 commitOp := s.expectStartQueuedOp(c) 236 237 initialState := justInstalledState() 238 executor := s.newExecutor(c, &initialState) 239 240 prepare := newStep(&prepareOp, nil) 241 execute := newStep(&executeOp, nil) 242 commit := newStep(&commitOp, nil) 243 op := &mockOperation{ 244 prepare: prepare, 245 execute: execute, 246 commit: commit, 247 } 248 249 err := executor.Run(op, nil) 250 c.Assert(err, jc.ErrorIsNil) 251 252 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 253 c.Assert(execute.gotState, gc.DeepEquals, *prepare.newState) 254 c.Assert(commit.gotState, gc.DeepEquals, *execute.newState) 255 c.Assert(executor.State(), gc.DeepEquals, *commit.newState) 256 } 257 258 func (s *ExecutorSuite) TestSucceedWithRemoteStateChanges(c *gc.C) { 259 defer s.setupMocks(c).Finish() 260 261 initialState := justInstalledState() 262 executor := s.newExecutor(c, &initialState) 263 264 remoteStateUpdated := make(chan struct{}, 1) 265 prepare := newStep(nil, nil) 266 execute := mockStepFunc(func(state operation.State) (*operation.State, error) { 267 select { 268 case <-remoteStateUpdated: 269 return nil, nil 270 case <-time.After(testing.ShortWait): 271 c.Fatal("remote state wasn't updated") 272 return nil, nil 273 } 274 }) 275 commit := newStep(nil, nil) 276 op := &mockOperation{ 277 prepare: prepare, 278 execute: execute, 279 commit: commit, 280 remoteStateFunc: func(snapshot remotestate.Snapshot) { 281 c.Assert(snapshot, gc.DeepEquals, remotestate.Snapshot{ 282 ConfigHash: "test", 283 }) 284 remoteStateUpdated <- struct{}{} 285 }, 286 } 287 288 rs := make(chan remotestate.Snapshot, 1) 289 rs <- remotestate.Snapshot{ 290 ConfigHash: "test", 291 } 292 err := executor.Run(op, rs) 293 c.Assert(err, jc.ErrorIsNil) 294 } 295 296 func (s *ExecutorSuite) TestErrSkipExecute(c *gc.C) { 297 defer s.setupMocks(c).Finish() 298 299 prepareOp := s.expectConfigChangedPendingOp(c) 300 commitOp := s.expectStartQueuedOp(c) 301 302 initialState := justInstalledState() 303 executor := s.newExecutor(c, &initialState) 304 305 prepare := newStep(&prepareOp, operation.ErrSkipExecute) 306 commit := newStep(&commitOp, nil) 307 op := &mockOperation{ 308 prepare: prepare, 309 commit: commit, 310 } 311 312 err := executor.Run(op, nil) 313 c.Assert(err, jc.ErrorIsNil) 314 315 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 316 c.Assert(commit.gotState, gc.DeepEquals, *prepare.newState) 317 c.Assert(executor.State(), gc.DeepEquals, *commit.newState) 318 } 319 320 func (s *ExecutorSuite) TestValidateStateChange(c *gc.C) { 321 defer s.setupMocks(c).Finish() 322 323 initialState := justInstalledState() 324 executor := s.newExecutor(c, &initialState) 325 326 prepare := newStep(&operation.State{ 327 Kind: operation.RunHook, 328 Step: operation.Pending, 329 }, nil) 330 op := &mockOperation{ 331 prepare: prepare, 332 } 333 334 err := executor.Run(op, nil) 335 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation" for test: invalid operation state: missing hook info with Kind RunHook`) 336 c.Assert(errors.Cause(err), gc.ErrorMatches, "missing hook info with Kind RunHook") 337 c.Assert(executor.State(), gc.DeepEquals, initialState) 338 } 339 340 func (s *ExecutorSuite) TestFailPrepareNoStateChange(c *gc.C) { 341 defer s.setupMocks(c).Finish() 342 343 initialState := justInstalledState() 344 executor := s.newExecutor(c, &initialState) 345 346 prepare := newStep(nil, errors.New("pow")) 347 op := &mockOperation{ 348 prepare: prepare, 349 } 350 351 err := executor.Run(op, nil) 352 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation" for test: pow`) 353 c.Assert(errors.Cause(err), gc.ErrorMatches, "pow") 354 355 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 356 c.Assert(executor.State(), gc.DeepEquals, initialState) 357 } 358 359 func (s *ExecutorSuite) TestFailPrepareWithStateChange(c *gc.C) { 360 defer s.setupMocks(c).Finish() 361 prepareOp := s.expectStartPendingOp(c) 362 363 initialState := justInstalledState() 364 executor := s.newExecutor(c, &initialState) 365 366 prepare := newStep(&prepareOp, errors.New("blam")) 367 op := &mockOperation{ 368 prepare: prepare, 369 } 370 371 err := executor.Run(op, nil) 372 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation" for test: blam`) 373 c.Assert(errors.Cause(err), gc.ErrorMatches, "blam") 374 375 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 376 c.Assert(executor.State(), gc.DeepEquals, *prepare.newState) 377 } 378 379 func (s *ExecutorSuite) TestFailExecuteNoStateChange(c *gc.C) { 380 defer s.setupMocks(c).Finish() 381 382 initialState := justInstalledState() 383 executor := s.newExecutor(c, &initialState) 384 385 prepare := newStep(nil, nil) 386 execute := newStep(nil, errors.New("splat")) 387 op := &mockOperation{ 388 prepare: prepare, 389 execute: execute, 390 } 391 392 err := executor.Run(op, nil) 393 c.Assert(err, gc.ErrorMatches, `executing operation "mock operation" for test: splat`) 394 c.Assert(errors.Cause(err), gc.ErrorMatches, "splat") 395 396 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 397 c.Assert(executor.State(), gc.DeepEquals, initialState) 398 } 399 400 func (s *ExecutorSuite) TestFailExecuteWithStateChange(c *gc.C) { 401 defer s.setupMocks(c).Finish() 402 executeOp := s.expectStartPendingOp(c) 403 404 initialState := justInstalledState() 405 executor := s.newExecutor(c, &initialState) 406 407 prepare := newStep(nil, nil) 408 execute := newStep(&executeOp, errors.New("kerblooie")) 409 op := &mockOperation{ 410 prepare: prepare, 411 execute: execute, 412 } 413 414 err := executor.Run(op, nil) 415 c.Assert(err, gc.ErrorMatches, `executing operation "mock operation" for test: kerblooie`) 416 c.Assert(errors.Cause(err), gc.ErrorMatches, "kerblooie") 417 418 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 419 c.Assert(executor.State(), gc.DeepEquals, *execute.newState) 420 } 421 422 func (s *ExecutorSuite) TestFailCommitNoStateChange(c *gc.C) { 423 defer s.setupMocks(c).Finish() 424 425 initialState := justInstalledState() 426 executor := s.newExecutor(c, &initialState) 427 428 prepare := newStep(nil, nil) 429 execute := newStep(nil, nil) 430 commit := newStep(nil, errors.New("whack")) 431 op := &mockOperation{ 432 prepare: prepare, 433 execute: execute, 434 commit: commit, 435 } 436 437 err := executor.Run(op, nil) 438 c.Assert(err, gc.ErrorMatches, `committing operation "mock operation" for test: whack`) 439 c.Assert(errors.Cause(err), gc.ErrorMatches, "whack") 440 441 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 442 c.Assert(executor.State(), gc.DeepEquals, initialState) 443 } 444 445 func (s *ExecutorSuite) TestFailCommitWithStateChange(c *gc.C) { 446 defer s.setupMocks(c).Finish() 447 448 initialState := justInstalledState() 449 commitOp := s.expectStartPendingOp(c) 450 451 executor := s.newExecutor(c, &initialState) 452 prepare := newStep(nil, nil) 453 execute := newStep(nil, nil) 454 commit := newStep(&commitOp, errors.New("take that you bandit")) 455 op := &mockOperation{ 456 prepare: prepare, 457 execute: execute, 458 commit: commit, 459 } 460 461 err := executor.Run(op, nil) 462 c.Assert(err, gc.ErrorMatches, `committing operation "mock operation" for test: take that you bandit`) 463 c.Assert(errors.Cause(err), gc.ErrorMatches, "take that you bandit") 464 465 c.Assert(prepare.gotState, gc.DeepEquals, initialState) 466 c.Assert(executor.State(), gc.DeepEquals, *commit.newState) 467 } 468 469 func (s *ExecutorSuite) initLockTest(c *gc.C, lockFunc func(string, string) (func(), error)) operation.Executor { 470 initialState := justInstalledState() 471 err := operation.NewStateOps(s.mockStateRW).Write(&initialState) 472 c.Assert(err, jc.ErrorIsNil) 473 cfg := operation.ExecutorConfig{ 474 StateReadWriter: s.mockStateRW, 475 InitialState: operation.State{Step: operation.Queued}, 476 AcquireLock: lockFunc, 477 Logger: loggo.GetLogger("test"), 478 } 479 executor, err := operation.NewExecutor("test", cfg) 480 c.Assert(err, jc.ErrorIsNil) 481 482 return executor 483 } 484 485 func (s *ExecutorSuite) TestLockSucceedsStepsCalled(c *gc.C) { 486 op := &mockOperation{ 487 needsLock: true, 488 prepare: newStep(nil, nil), 489 execute: newStep(nil, nil), 490 commit: newStep(nil, nil), 491 } 492 493 mockLock := &mockLockFunc{op: op} 494 lockFunc := mockLock.newSucceedingLock() 495 executor := s.initLockTest(c, lockFunc) 496 497 err := executor.Run(op, nil) 498 c.Assert(err, jc.ErrorIsNil) 499 500 c.Assert(mockLock.calledLock, jc.IsTrue) 501 c.Assert(mockLock.calledUnlock, jc.IsTrue) 502 c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue) 503 504 expectedStepsOnUnlock := []bool{true, true, true} 505 c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock) 506 } 507 508 func (s *ExecutorSuite) TestLockFailsOpsStepsNotCalled(c *gc.C) { 509 prepare := newStep(nil, nil) 510 execute := newStep(nil, nil) 511 commit := newStep(nil, nil) 512 op := &mockOperation{ 513 needsLock: true, 514 prepare: prepare, 515 execute: execute, 516 commit: commit, 517 } 518 519 mockLock := &mockLockFunc{op: op} 520 lockFunc := mockLock.newFailingLock() 521 executor := s.initLockTest(c, lockFunc) 522 523 err := executor.Run(op, nil) 524 c.Assert(err, gc.ErrorMatches, "could not acquire lock: wat") 525 526 c.Assert(mockLock.calledLock, jc.IsFalse) 527 c.Assert(mockLock.calledUnlock, jc.IsFalse) 528 c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue) 529 530 c.Assert(prepare.called, jc.IsFalse) 531 c.Assert(execute.called, jc.IsFalse) 532 c.Assert(commit.called, jc.IsFalse) 533 } 534 535 func (s *ExecutorSuite) testLockUnlocksOnError(c *gc.C, op *mockOperation) (error, *mockLockFunc) { 536 mockLock := &mockLockFunc{op: op} 537 lockFunc := mockLock.newSucceedingLock() 538 executor := s.initLockTest(c, lockFunc) 539 540 err := executor.Run(op, nil) 541 542 c.Assert(mockLock.calledLock, jc.IsTrue) 543 c.Assert(mockLock.calledUnlock, jc.IsTrue) 544 c.Assert(mockLock.noStepsCalledOnLock, jc.IsTrue) 545 546 return err, mockLock 547 } 548 549 func (s *ExecutorSuite) TestLockUnlocksOnError_Prepare(c *gc.C) { 550 op := &mockOperation{ 551 needsLock: true, 552 prepare: newStep(nil, errors.New("kerblooie")), 553 execute: newStep(nil, nil), 554 commit: newStep(nil, nil), 555 } 556 557 err, mockLock := s.testLockUnlocksOnError(c, op) 558 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": kerblooie`) 559 c.Assert(errors.Cause(err), gc.ErrorMatches, "kerblooie") 560 561 expectedStepsOnUnlock := []bool{true, false, false} 562 c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock) 563 } 564 565 func (s *ExecutorSuite) TestLockUnlocksOnError_Execute(c *gc.C) { 566 op := &mockOperation{ 567 needsLock: true, 568 prepare: newStep(nil, nil), 569 execute: newStep(nil, errors.New("you asked for it")), 570 commit: newStep(nil, nil), 571 } 572 573 err, mockLock := s.testLockUnlocksOnError(c, op) 574 c.Assert(err, gc.ErrorMatches, `executing operation "mock operation": you asked for it`) 575 c.Assert(errors.Cause(err), gc.ErrorMatches, "you asked for it") 576 577 expectedStepsOnUnlock := []bool{true, true, false} 578 c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock) 579 } 580 581 func (s *ExecutorSuite) TestLockUnlocksOnError_Commit(c *gc.C) { 582 op := &mockOperation{ 583 needsLock: true, 584 prepare: newStep(nil, nil), 585 execute: newStep(nil, nil), 586 commit: newStep(nil, errors.New("well, shit")), 587 } 588 589 err, mockLock := s.testLockUnlocksOnError(c, op) 590 c.Assert(err, gc.ErrorMatches, `committing operation "mock operation": well, shit`) 591 c.Assert(errors.Cause(err), gc.ErrorMatches, "well, shit") 592 593 expectedStepsOnUnlock := []bool{true, true, true} 594 c.Assert(mockLock.stepsCalledOnUnlock, gc.DeepEquals, expectedStepsOnUnlock) 595 } 596 597 type mockLockFunc struct { 598 noStepsCalledOnLock bool 599 stepsCalledOnUnlock []bool 600 calledLock bool 601 calledUnlock bool 602 op *mockOperation 603 } 604 605 func (mock *mockLockFunc) newFailingLock() func(string, string) (func(), error) { 606 return func(string, string) (func(), error) { 607 mock.noStepsCalledOnLock = mock.op.prepare.(*mockStep).called == false && 608 mock.op.commit.(*mockStep).called == false 609 return nil, errors.New("wat") 610 } 611 } 612 613 func (mock *mockLockFunc) newSucceedingLock() func(string, string) (func(), error) { 614 return func(string, string) (func(), error) { 615 mock.calledLock = true 616 // Ensure that when we lock no operation has been called 617 mock.noStepsCalledOnLock = mock.op.prepare.(*mockStep).called == false && 618 mock.op.commit.(*mockStep).called == false 619 return func() { 620 // Record steps called when unlocking 621 mock.stepsCalledOnUnlock = []bool{mock.op.prepare.(*mockStep).called, 622 mock.op.execute.(*mockStep).called, 623 mock.op.commit.(*mockStep).called} 624 mock.calledUnlock = true 625 }, nil 626 } 627 } 628 629 type mockStepInterface interface { 630 Run(state operation.State) (*operation.State, error) 631 } 632 633 type mockStepFunc func(state operation.State) (*operation.State, error) 634 635 func (m mockStepFunc) Run(state operation.State) (*operation.State, error) { 636 return m(state) 637 } 638 639 type mockStep struct { 640 gotState operation.State 641 newState *operation.State 642 err error 643 called bool 644 } 645 646 func newStep(newState *operation.State, err error) *mockStep { 647 return &mockStep{newState: newState, err: err} 648 } 649 650 func (step *mockStep) Run(state operation.State) (*operation.State, error) { 651 step.called = true 652 step.gotState = state 653 return step.newState, step.err 654 } 655 656 type mockOperation struct { 657 needsLock bool 658 prepare mockStepInterface 659 execute mockStepInterface 660 commit mockStepInterface 661 remoteStateFunc func(snapshot remotestate.Snapshot) 662 } 663 664 func (op *mockOperation) String() string { 665 return "mock operation" 666 } 667 668 func (op *mockOperation) NeedsGlobalMachineLock() bool { 669 return op.needsLock 670 } 671 672 func (m *mockOperation) ExecutionGroup() string { 673 return "" 674 } 675 676 func (op *mockOperation) Prepare(state operation.State) (*operation.State, error) { 677 return op.prepare.Run(state) 678 } 679 680 func (op *mockOperation) Execute(state operation.State) (*operation.State, error) { 681 return op.execute.Run(state) 682 } 683 684 func (op *mockOperation) Commit(state operation.State) (*operation.State, error) { 685 return op.commit.Run(state) 686 } 687 688 func (op *mockOperation) RemoteStateChanged(snapshot remotestate.Snapshot) { 689 if op.remoteStateFunc != nil { 690 op.remoteStateFunc(snapshot) 691 } 692 }