1
16
17 package testing
18
19 import (
20 "context"
21 "io"
22 "net/url"
23 "reflect"
24 "sync"
25 "time"
26
27 v1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/types"
29 "k8s.io/client-go/util/flowcontrol"
30 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
31 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
32 "k8s.io/kubernetes/pkg/volume"
33 )
34
35 type TB interface {
36 Errorf(format string, args ...any)
37 }
38
39 type FakePod struct {
40 Pod *kubecontainer.Pod
41 NetnsPath string
42 }
43
44
45 type FakeRuntime struct {
46 sync.Mutex
47 CalledFunctions []string
48 PodList []*FakePod
49 AllPodList []*FakePod
50 ImageList []kubecontainer.Image
51 ImageFsStats []*runtimeapi.FilesystemUsage
52 ContainerFsStats []*runtimeapi.FilesystemUsage
53 APIPodStatus v1.PodStatus
54 PodStatus kubecontainer.PodStatus
55 StartedPods []string
56 KilledPods []string
57 StartedContainers []string
58 KilledContainers []string
59 RuntimeStatus *kubecontainer.RuntimeStatus
60 VersionInfo string
61 APIVersionInfo string
62 RuntimeType string
63 Err error
64 InspectErr error
65 StatusErr error
66
67
68
69 BlockImagePulls bool
70 imagePullTokenBucket chan bool
71 T TB
72 }
73
74 const FakeHost = "localhost:12345"
75
76 type FakeStreamingRuntime struct {
77 *FakeRuntime
78 }
79
80 var _ kubecontainer.StreamingRuntime = &FakeStreamingRuntime{}
81
82
83 var _ kubecontainer.Runtime = &FakeRuntime{}
84
85 type FakeVersion struct {
86 Version string
87 }
88
89 func (fv *FakeVersion) String() string {
90 return fv.Version
91 }
92
93 func (fv *FakeVersion) Compare(other string) (int, error) {
94 result := 0
95 if fv.Version > other {
96 result = 1
97 } else if fv.Version < other {
98 result = -1
99 }
100 return result, nil
101 }
102
103 type podsGetter interface {
104 GetPods(context.Context, bool) ([]*kubecontainer.Pod, error)
105 }
106
107 type FakeRuntimeCache struct {
108 getter podsGetter
109 }
110
111 func NewFakeRuntimeCache(getter podsGetter) kubecontainer.RuntimeCache {
112 return &FakeRuntimeCache{getter}
113 }
114
115 func (f *FakeRuntimeCache) GetPods(ctx context.Context) ([]*kubecontainer.Pod, error) {
116 return f.getter.GetPods(ctx, false)
117 }
118
119 func (f *FakeRuntimeCache) ForceUpdateIfOlder(context.Context, time.Time) error {
120 return nil
121 }
122
123
124 func (f *FakeRuntime) UpdatePodCIDR(_ context.Context, c string) error {
125 return nil
126 }
127
128 func (f *FakeRuntime) assertList(expect []string, test []string) bool {
129 if !reflect.DeepEqual(expect, test) {
130 f.T.Errorf("AssertList: expected %#v, got %#v", expect, test)
131 return false
132 }
133 return true
134 }
135
136
137 func (f *FakeRuntime) AssertCalls(calls []string) bool {
138 f.Lock()
139 defer f.Unlock()
140 return f.assertList(calls, f.CalledFunctions)
141 }
142
143
144 func (f *FakeRuntime) AssertCallCounts(funcName string, expectedCount int) bool {
145 f.Lock()
146 defer f.Unlock()
147 actualCount := 0
148 for _, c := range f.CalledFunctions {
149 if funcName == c {
150 actualCount += 1
151 }
152 }
153 if expectedCount != actualCount {
154 f.T.Errorf("AssertCallCounts: expected %s to be called %d times, but was actually called %d times.", funcName, expectedCount, actualCount)
155 return false
156 }
157 return true
158 }
159
160 func (f *FakeRuntime) AssertStartedPods(pods []string) bool {
161 f.Lock()
162 defer f.Unlock()
163 return f.assertList(pods, f.StartedPods)
164 }
165
166 func (f *FakeRuntime) AssertKilledPods(pods []string) bool {
167 f.Lock()
168 defer f.Unlock()
169 return f.assertList(pods, f.KilledPods)
170 }
171
172 func (f *FakeRuntime) AssertStartedContainers(containers []string) bool {
173 f.Lock()
174 defer f.Unlock()
175 return f.assertList(containers, f.StartedContainers)
176 }
177
178 func (f *FakeRuntime) AssertKilledContainers(containers []string) bool {
179 f.Lock()
180 defer f.Unlock()
181 return f.assertList(containers, f.KilledContainers)
182 }
183
184 func (f *FakeRuntime) Type() string {
185 return f.RuntimeType
186 }
187
188 func (f *FakeRuntime) Version(_ context.Context) (kubecontainer.Version, error) {
189 f.Lock()
190 defer f.Unlock()
191
192 f.CalledFunctions = append(f.CalledFunctions, "Version")
193 return &FakeVersion{Version: f.VersionInfo}, f.Err
194 }
195
196 func (f *FakeRuntime) APIVersion() (kubecontainer.Version, error) {
197 f.Lock()
198 defer f.Unlock()
199
200 f.CalledFunctions = append(f.CalledFunctions, "APIVersion")
201 return &FakeVersion{Version: f.APIVersionInfo}, f.Err
202 }
203
204 func (f *FakeRuntime) Status(_ context.Context) (*kubecontainer.RuntimeStatus, error) {
205 f.Lock()
206 defer f.Unlock()
207
208 f.CalledFunctions = append(f.CalledFunctions, "Status")
209 return f.RuntimeStatus, f.StatusErr
210 }
211
212 func (f *FakeRuntime) GetPods(_ context.Context, all bool) ([]*kubecontainer.Pod, error) {
213 f.Lock()
214 defer f.Unlock()
215
216 var pods []*kubecontainer.Pod
217
218 f.CalledFunctions = append(f.CalledFunctions, "GetPods")
219 if all {
220 for _, fakePod := range f.AllPodList {
221 pods = append(pods, fakePod.Pod)
222 }
223 } else {
224 for _, fakePod := range f.PodList {
225 pods = append(pods, fakePod.Pod)
226 }
227 }
228 return pods, f.Err
229 }
230
231 func (f *FakeRuntime) SyncPod(_ context.Context, pod *v1.Pod, _ *kubecontainer.PodStatus, _ []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
232 f.Lock()
233 defer f.Unlock()
234
235 f.CalledFunctions = append(f.CalledFunctions, "SyncPod")
236 f.StartedPods = append(f.StartedPods, string(pod.UID))
237 for _, c := range pod.Spec.Containers {
238 f.StartedContainers = append(f.StartedContainers, c.Name)
239 }
240
241 if f.Err != nil {
242 result.Fail(f.Err)
243 }
244 return
245 }
246
247 func (f *FakeRuntime) KillPod(_ context.Context, pod *v1.Pod, runningPod kubecontainer.Pod, gracePeriodOverride *int64) error {
248 f.Lock()
249 defer f.Unlock()
250
251 f.CalledFunctions = append(f.CalledFunctions, "KillPod")
252 f.KilledPods = append(f.KilledPods, string(runningPod.ID))
253 for _, c := range runningPod.Containers {
254 f.KilledContainers = append(f.KilledContainers, c.Name)
255 }
256 return f.Err
257 }
258
259 func (f *FakeRuntime) RunContainerInPod(container v1.Container, pod *v1.Pod, volumeMap map[string]volume.VolumePlugin) error {
260 f.Lock()
261 defer f.Unlock()
262
263 f.CalledFunctions = append(f.CalledFunctions, "RunContainerInPod")
264 f.StartedContainers = append(f.StartedContainers, container.Name)
265
266 pod.Spec.Containers = append(pod.Spec.Containers, container)
267 for _, c := range pod.Spec.Containers {
268 if c.Name == container.Name {
269 return f.Err
270 }
271 }
272 pod.Spec.Containers = append(pod.Spec.Containers, container)
273 return f.Err
274 }
275
276 func (f *FakeRuntime) KillContainerInPod(container v1.Container, pod *v1.Pod) error {
277 f.Lock()
278 defer f.Unlock()
279
280 f.CalledFunctions = append(f.CalledFunctions, "KillContainerInPod")
281 f.KilledContainers = append(f.KilledContainers, container.Name)
282 return f.Err
283 }
284
285 func (f *FakeRuntime) GeneratePodStatus(event *runtimeapi.ContainerEventResponse) (*kubecontainer.PodStatus, error) {
286 f.Lock()
287 defer f.Unlock()
288
289 f.CalledFunctions = append(f.CalledFunctions, "GeneratePodStatus")
290 status := f.PodStatus
291 return &status, f.Err
292 }
293
294 func (f *FakeRuntime) GetPodStatus(_ context.Context, uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error) {
295 f.Lock()
296 defer f.Unlock()
297
298 f.CalledFunctions = append(f.CalledFunctions, "GetPodStatus")
299 status := f.PodStatus
300 return &status, f.Err
301 }
302
303 func (f *FakeRuntime) GetContainerLogs(_ context.Context, pod *v1.Pod, containerID kubecontainer.ContainerID, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) (err error) {
304 f.Lock()
305 defer f.Unlock()
306
307 f.CalledFunctions = append(f.CalledFunctions, "GetContainerLogs")
308 return f.Err
309 }
310
311 func (f *FakeRuntime) PullImage(ctx context.Context, image kubecontainer.ImageSpec, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
312 f.Lock()
313 f.CalledFunctions = append(f.CalledFunctions, "PullImage")
314 if f.Err == nil {
315 i := kubecontainer.Image{
316 ID: image.Image,
317 Spec: image,
318 }
319 f.ImageList = append(f.ImageList, i)
320 }
321
322 if !f.BlockImagePulls {
323 f.Unlock()
324 return image.Image, f.Err
325 }
326
327 retErr := f.Err
328 if f.imagePullTokenBucket == nil {
329 f.imagePullTokenBucket = make(chan bool, 1)
330 }
331
332 f.Unlock()
333 select {
334 case <-ctx.Done():
335 case <-f.imagePullTokenBucket:
336 }
337 return image.Image, retErr
338 }
339
340
341 func (f *FakeRuntime) UnblockImagePulls(count int) {
342 if f.imagePullTokenBucket != nil {
343 for i := 0; i < count; i++ {
344 select {
345 case f.imagePullTokenBucket <- true:
346 default:
347 }
348 }
349 }
350 }
351
352 func (f *FakeRuntime) GetImageRef(_ context.Context, image kubecontainer.ImageSpec) (string, error) {
353 f.Lock()
354 defer f.Unlock()
355
356 f.CalledFunctions = append(f.CalledFunctions, "GetImageRef")
357 for _, i := range f.ImageList {
358 if i.ID == image.Image {
359 return i.ID, nil
360 }
361 }
362 return "", f.InspectErr
363 }
364
365 func (f *FakeRuntime) GetImageSize(_ context.Context, image kubecontainer.ImageSpec) (uint64, error) {
366 f.Lock()
367 defer f.Unlock()
368
369 f.CalledFunctions = append(f.CalledFunctions, "GetImageSize")
370 return 0, f.Err
371 }
372
373 func (f *FakeRuntime) ListImages(_ context.Context) ([]kubecontainer.Image, error) {
374 f.Lock()
375 defer f.Unlock()
376
377 f.CalledFunctions = append(f.CalledFunctions, "ListImages")
378 return snapshot(f.ImageList), f.Err
379 }
380
381 func snapshot(imageList []kubecontainer.Image) []kubecontainer.Image {
382 result := make([]kubecontainer.Image, len(imageList))
383 copy(result, imageList)
384 return result
385 }
386
387 func (f *FakeRuntime) RemoveImage(_ context.Context, image kubecontainer.ImageSpec) error {
388 f.Lock()
389 defer f.Unlock()
390
391 f.CalledFunctions = append(f.CalledFunctions, "RemoveImage")
392 index := 0
393 for i := range f.ImageList {
394 if f.ImageList[i].ID == image.Image {
395 index = i
396 break
397 }
398 }
399 f.ImageList = append(f.ImageList[:index], f.ImageList[index+1:]...)
400
401 return f.Err
402 }
403
404 func (f *FakeRuntime) GarbageCollect(_ context.Context, gcPolicy kubecontainer.GCPolicy, ready bool, evictNonDeletedPods bool) error {
405 f.Lock()
406 defer f.Unlock()
407
408 f.CalledFunctions = append(f.CalledFunctions, "GarbageCollect")
409 return f.Err
410 }
411
412 func (f *FakeRuntime) DeleteContainer(_ context.Context, containerID kubecontainer.ContainerID) error {
413 f.Lock()
414 defer f.Unlock()
415
416 f.CalledFunctions = append(f.CalledFunctions, "DeleteContainer")
417 return f.Err
418 }
419
420 func (f *FakeRuntime) CheckpointContainer(_ context.Context, options *runtimeapi.CheckpointContainerRequest) error {
421 f.Lock()
422 defer f.Unlock()
423
424 f.CalledFunctions = append(f.CalledFunctions, "CheckpointContainer")
425 return f.Err
426 }
427
428 func (f *FakeRuntime) ListMetricDescriptors(_ context.Context) ([]*runtimeapi.MetricDescriptor, error) {
429 f.Lock()
430 defer f.Unlock()
431
432 f.CalledFunctions = append(f.CalledFunctions, "ListMetricDescriptors")
433 return nil, f.Err
434 }
435
436 func (f *FakeRuntime) ListPodSandboxMetrics(_ context.Context) ([]*runtimeapi.PodSandboxMetrics, error) {
437 f.Lock()
438 defer f.Unlock()
439
440 f.CalledFunctions = append(f.CalledFunctions, "ListPodSandboxMetrics")
441 return nil, f.Err
442 }
443
444
445 func (f *FakeRuntime) SetContainerFsStats(val []*runtimeapi.FilesystemUsage) {
446 f.ContainerFsStats = val
447 }
448
449
450 func (f *FakeRuntime) SetImageFsStats(val []*runtimeapi.FilesystemUsage) {
451 f.ImageFsStats = val
452 }
453
454 func (f *FakeRuntime) ImageStats(_ context.Context) (*kubecontainer.ImageStats, error) {
455 f.Lock()
456 defer f.Unlock()
457
458 f.CalledFunctions = append(f.CalledFunctions, "ImageStats")
459 return nil, f.Err
460 }
461
462
463
464 func (f *FakeRuntime) ImageFsInfo(_ context.Context) (*runtimeapi.ImageFsInfoResponse, error) {
465 f.Lock()
466 defer f.Unlock()
467
468 f.CalledFunctions = append(f.CalledFunctions, "ImageFsInfo")
469 resp := &runtimeapi.ImageFsInfoResponse{
470 ImageFilesystems: f.ImageFsStats,
471 ContainerFilesystems: f.ContainerFsStats,
472 }
473 return resp, f.Err
474 }
475
476 func (f *FakeStreamingRuntime) GetExec(_ context.Context, id kubecontainer.ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error) {
477 f.Lock()
478 defer f.Unlock()
479
480 f.CalledFunctions = append(f.CalledFunctions, "GetExec")
481 return &url.URL{Host: FakeHost}, f.Err
482 }
483
484 func (f *FakeStreamingRuntime) GetAttach(_ context.Context, id kubecontainer.ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error) {
485 f.Lock()
486 defer f.Unlock()
487
488 f.CalledFunctions = append(f.CalledFunctions, "GetAttach")
489 return &url.URL{Host: FakeHost}, f.Err
490 }
491
492 func (f *FakeStreamingRuntime) GetPortForward(_ context.Context, podName, podNamespace string, podUID types.UID, ports []int32) (*url.URL, error) {
493 f.Lock()
494 defer f.Unlock()
495
496 f.CalledFunctions = append(f.CalledFunctions, "GetPortForward")
497 return &url.URL{Host: FakeHost}, f.Err
498 }
499
500 type FakeContainerCommandRunner struct {
501
502 Stdout string
503 Err error
504
505
506 ContainerID kubecontainer.ContainerID
507 Cmd []string
508 }
509
510 var _ kubecontainer.CommandRunner = &FakeContainerCommandRunner{}
511
512 func (f *FakeContainerCommandRunner) RunInContainer(_ context.Context, containerID kubecontainer.ContainerID, cmd []string, timeout time.Duration) ([]byte, error) {
513
514 f.ContainerID = containerID
515 f.Cmd = cmd
516
517 return []byte(f.Stdout), f.Err
518 }
519
View as plain text