github.com/cilium/cilium@v1.16.2/pkg/status/status_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package status 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "strings" 11 "sync/atomic" 12 "testing" 13 "time" 14 15 "github.com/stretchr/testify/require" 16 17 "github.com/cilium/cilium/pkg/lock" 18 "github.com/cilium/cilium/pkg/testutils" 19 ) 20 21 type StatusTestSuite struct { 22 config Config 23 mutex lock.Mutex 24 } 25 26 func setUpTest(_ testing.TB) *StatusTestSuite { 27 s := &StatusTestSuite{} 28 s.mutex.Lock() 29 s.config = Config{ 30 Interval: 10 * time.Millisecond, 31 WarningThreshold: 20 * time.Millisecond, 32 FailureThreshold: 80 * time.Millisecond, 33 } 34 s.mutex.Unlock() 35 36 return s 37 } 38 39 func (s *StatusTestSuite) Config() (c Config) { 40 s.mutex.Lock() 41 c = s.config 42 s.mutex.Unlock() 43 return 44 } 45 46 func TestVariableProbeInterval(t *testing.T) { 47 s := setUpTest(t) 48 49 var runs, ok atomic.Uint64 50 51 p := []Probe{ 52 { 53 Interval: func(failures int) time.Duration { 54 // While failing, retry every millisecond 55 if failures > 0 { 56 return time.Millisecond 57 } 58 59 // Ensure that the regular interval would never retry 60 return time.Minute 61 }, 62 Probe: func(ctx context.Context) (interface{}, error) { 63 // Let 5 runs fail and then succeed 64 if runs.Add(1) < 5 { 65 return nil, fmt.Errorf("still failing") 66 } 67 68 return nil, nil 69 }, 70 OnStatusUpdate: func(status Status) { 71 if status.Data == nil && status.Err == nil { 72 ok.Add(1) 73 } 74 }, 75 }, 76 } 77 78 collector := NewCollector(p, s.Config()) 79 defer collector.Close() 80 81 // wait for 5 probe intervals to occur with 1 millisecond interval 82 // until we reach success 83 require.NoError(t, testutils.WaitUntil(func() bool { 84 return ok.Load() >= 1 85 }, 1*time.Second)) 86 } 87 88 func TestCollectorFailureTimeout(t *testing.T) { 89 s := setUpTest(t) 90 91 var ok atomic.Uint64 92 93 p := []Probe{ 94 { 95 Probe: func(ctx context.Context) (interface{}, error) { 96 time.Sleep(s.Config().FailureThreshold * 2) 97 return nil, nil 98 }, 99 OnStatusUpdate: func(status Status) { 100 if status.StaleWarning && status.Data == nil && status.Err != nil { 101 if strings.Contains(status.Err.Error(), 102 fmt.Sprintf("within %v seconds", s.Config().FailureThreshold.Seconds())) { 103 104 ok.Add(1) 105 } 106 } 107 }, 108 }, 109 } 110 111 collector := NewCollector(p, s.Config()) 112 defer collector.Close() 113 114 // wait for the failure timeout to kick in 115 require.NoError(t, testutils.WaitUntil(func() bool { 116 return ok.Load() >= 1 117 }, 1*time.Second)) 118 require.Len(t, collector.GetStaleProbes(), 1) 119 } 120 121 func TestCollectorSuccess(t *testing.T) { 122 s := setUpTest(t) 123 124 var ok, errs atomic.Uint64 125 err := fmt.Errorf("error") 126 127 p := []Probe{ 128 { 129 Probe: func(ctx context.Context) (interface{}, error) { 130 if ok.Load() > 3 { 131 return nil, err 132 } 133 return "testData", nil 134 }, 135 OnStatusUpdate: func(status Status) { 136 if !errors.Is(status.Err, err) { 137 errs.Add(1) 138 } 139 if !status.StaleWarning && status.Data != nil && status.Err == nil { 140 if s, isString := status.Data.(string); isString && s == "testData" { 141 ok.Add(1) 142 } 143 } 144 }, 145 }, 146 } 147 148 collector := NewCollector(p, s.Config()) 149 defer collector.Close() 150 151 // wait for the probe to succeed 3 times and to return the error 3 times 152 require.NoError(t, testutils.WaitUntil(func() bool { 153 return ok.Load() >= 3 && errs.Load() >= 3 154 }, 1*time.Second)) 155 require.Len(t, collector.GetStaleProbes(), 0) 156 } 157 158 func TestCollectorSuccessAfterTimeout(t *testing.T) { 159 s := setUpTest(t) 160 161 var ok, timeout atomic.Uint64 162 163 p := []Probe{ 164 { 165 Probe: func(ctx context.Context) (interface{}, error) { 166 if timeout.Load() == 0 { 167 time.Sleep(2 * s.Config().FailureThreshold) 168 } 169 return nil, nil 170 }, 171 OnStatusUpdate: func(status Status) { 172 if status.StaleWarning { 173 timeout.Add(1) 174 } else { 175 ok.Add(1) 176 } 177 178 }, 179 }, 180 } 181 182 collector := NewCollector(p, s.Config()) 183 defer collector.Close() 184 185 // wait for the probe to timeout (warning and failure) and then to succeed 186 require.NoError(t, testutils.WaitUntil(func() bool { 187 return timeout.Load() == 1 && ok.Load() > 0 188 }, 1*time.Second)) 189 require.Len(t, collector.GetStaleProbes(), 0) 190 }