...

Source file src/k8s.io/kubernetes/pkg/kubelet/container/testing/fake_runtime.go

Documentation: k8s.io/kubernetes/pkg/kubelet/container/testing

     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  	"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  // FakeRuntime is a fake container runtime for testing.
    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  	// If BlockImagePulls is true, then all PullImage() calls will be blocked until
    67  	// UnblockImagePulls() is called. This is used to simulate image pull latency
    68  	// from container runtime.
    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  // FakeRuntime should implement Runtime.
    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  // UpdatePodCIDR fulfills the cri interface.
   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  // AssertCalls test if the invoked functions are as expected.
   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  // AssertCallCounts checks if a certain call is called for a certain of numbers
   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  	// TODO(random-liu): Add SyncResult for starting and killing containers
   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 { // Container already in the pod.
   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  	// Unlock before waiting for UnblockImagePulls calls, to avoid deadlock.
   332  	f.Unlock()
   333  	select {
   334  	case <-ctx.Done():
   335  	case <-f.imagePullTokenBucket:
   336  	}
   337  	return image.Image, retErr
   338  }
   339  
   340  // UnblockImagePulls unblocks a certain number of image pulls, if BlockImagePulls is true.
   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  // SetContainerFsStats sets the containerFsStats for dependency injection.
   445  func (f *FakeRuntime) SetContainerFsStats(val []*runtimeapi.FilesystemUsage) {
   446  	f.ContainerFsStats = val
   447  }
   448  
   449  // SetImageFsStats sets the ImageFsStats for dependency injection.
   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  // ImageFsInfo returns a ImageFsInfoResponse given the DI injected values of ImageFsStats
   463  // and ContainerFsStats.
   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  	// what to return
   502  	Stdout string
   503  	Err    error
   504  
   505  	// actual values when invoked
   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  	// record invoked values
   514  	f.ContainerID = containerID
   515  	f.Cmd = cmd
   516  
   517  	return []byte(f.Stdout), f.Err
   518  }
   519  

View as plain text