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