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  }