statemachine.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. Copyright 2023 The Rook Authors. All rights reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package multus
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. meta "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. )
  20. type validationState interface {
  21. Run(ctx context.Context, vsm *validationStateMachine) (suggestions []string, err error)
  22. }
  23. type validationStateMachine struct {
  24. vt *ValidationTest
  25. state validationState
  26. timer *time.Timer
  27. stateWasChanged bool
  28. resourceOwnerRefs []meta.OwnerReference
  29. testResults *ValidationTestResults
  30. lastSuggestions []string
  31. lastErr error
  32. done bool
  33. }
  34. func (vsm *validationStateMachine) SetNextState(nextState validationState) {
  35. vsm.stateWasChanged = true
  36. vsm.state = nextState
  37. }
  38. func (vsm *validationStateMachine) Exit() {
  39. vsm.done = true
  40. }
  41. func (vsm *validationStateMachine) Run(ctx context.Context) (*ValidationTestResults, error) {
  42. vsm.timer = time.NewTimer(vsm.vt.ResourceTimeout)
  43. defer func() { vsm.timer.Stop() }()
  44. for {
  45. select {
  46. case <-ctx.Done():
  47. return vsm.exitContextCanceled(ctx)
  48. case <-vsm.timer.C:
  49. vsm.testResults.addSuggestions(vsm.lastSuggestions...)
  50. return vsm.testResults, fmt.Errorf("multus validation test timed out: %w", vsm.lastErr)
  51. default:
  52. // give each state the full resource timeout to run successfully
  53. if vsm.stateWasChanged {
  54. vsm.resetTimer()
  55. vsm.stateWasChanged = false
  56. }
  57. // run the state
  58. suggestions, err := vsm.state.Run(ctx, vsm)
  59. // if the context was canceled, the error message won't be as useful as gathered from
  60. // the last context, so exit before updating the latest suggestions and error
  61. if ctx.Err() != nil {
  62. return vsm.exitContextCanceled(ctx)
  63. }
  64. // record the latest suggestions and error
  65. vsm.lastErr = err
  66. vsm.lastSuggestions = suggestions
  67. // exit the state machine, error or not
  68. if vsm.done {
  69. vsm.testResults.addSuggestions(vsm.lastSuggestions...)
  70. if vsm.lastErr != nil {
  71. return vsm.testResults, fmt.Errorf("multus validation test failed: %w", vsm.lastErr)
  72. }
  73. return vsm.testResults, nil
  74. }
  75. if err != nil {
  76. vsm.vt.Logger.Infof("continuing: %s", err)
  77. }
  78. time.Sleep(2 * time.Second)
  79. }
  80. }
  81. }
  82. func (vsm *validationStateMachine) resetTimer() {
  83. if !vsm.timer.Stop() {
  84. <-vsm.timer.C
  85. }
  86. vsm.timer.Reset(vsm.vt.ResourceTimeout)
  87. }
  88. func (vsm *validationStateMachine) exitContextCanceled(ctx context.Context) (*ValidationTestResults, error) {
  89. vsm.testResults.addSuggestions(vsm.lastSuggestions...)
  90. return vsm.testResults, fmt.Errorf("context canceled before multus validation test could complete: %s: %w", ctx.Err().Error(), vsm.lastErr)
  91. }