...

Source file src/k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/kuberuntime

     1  /*
     2  Copyright 2016 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 kuberuntime
    18  
    19  import (
    20  	"context"
    21  	"os"
    22  	"path/filepath"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/golang/mock/gomock"
    27  	"github.com/stretchr/testify/assert"
    28  	v1 "k8s.io/api/core/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    31  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    32  	containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
    33  )
    34  
    35  func TestSandboxGC(t *testing.T) {
    36  	fakeRuntime, _, m, err := createTestRuntimeManager()
    37  	assert.NoError(t, err)
    38  
    39  	podStateProvider := m.containerGC.podStateProvider.(*fakePodStateProvider)
    40  	makeGCSandbox := func(pod *v1.Pod, attempt uint32, state runtimeapi.PodSandboxState, hasRunningContainers, isTerminating bool, createdAt int64) sandboxTemplate {
    41  		return sandboxTemplate{
    42  			pod:         pod,
    43  			state:       state,
    44  			attempt:     attempt,
    45  			createdAt:   createdAt,
    46  			running:     hasRunningContainers,
    47  			terminating: isTerminating,
    48  		}
    49  	}
    50  
    51  	pods := []*v1.Pod{
    52  		makeTestPod("foo1", "new", "1234", []v1.Container{
    53  			makeTestContainer("bar1", "busybox"),
    54  			makeTestContainer("bar2", "busybox"),
    55  		}),
    56  		makeTestPod("foo2", "new", "5678", []v1.Container{
    57  			makeTestContainer("bar3", "busybox"),
    58  		}),
    59  		makeTestPod("deleted", "new", "9012", []v1.Container{
    60  			makeTestContainer("bar4", "busybox"),
    61  		}),
    62  	}
    63  
    64  	for _, test := range []struct {
    65  		description          string              // description of the test case
    66  		sandboxes            []sandboxTemplate   // templates of sandboxes
    67  		containers           []containerTemplate // templates of containers
    68  		remain               []int               // template indexes of remaining sandboxes
    69  		evictTerminatingPods bool
    70  	}{
    71  		{
    72  			description: "notready sandboxes without containers for deleted pods should be garbage collected.",
    73  			sandboxes: []sandboxTemplate{
    74  				makeGCSandbox(pods[2], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, false, false, 0),
    75  			},
    76  			containers:           []containerTemplate{},
    77  			remain:               []int{},
    78  			evictTerminatingPods: false,
    79  		},
    80  		{
    81  			description: "ready sandboxes without containers for deleted pods should not be garbage collected.",
    82  			sandboxes: []sandboxTemplate{
    83  				makeGCSandbox(pods[2], 0, runtimeapi.PodSandboxState_SANDBOX_READY, false, false, 0),
    84  			},
    85  			containers:           []containerTemplate{},
    86  			remain:               []int{0},
    87  			evictTerminatingPods: false,
    88  		},
    89  		{
    90  			description: "sandboxes for existing pods should not be garbage collected.",
    91  			sandboxes: []sandboxTemplate{
    92  				makeGCSandbox(pods[0], 0, runtimeapi.PodSandboxState_SANDBOX_READY, true, false, 0),
    93  				makeGCSandbox(pods[1], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 0),
    94  			},
    95  			containers:           []containerTemplate{},
    96  			remain:               []int{0, 1},
    97  			evictTerminatingPods: false,
    98  		},
    99  		{
   100  			description: "older exited sandboxes without containers for existing pods should be garbage collected if there are more than one exited sandboxes.",
   101  			sandboxes: []sandboxTemplate{
   102  				makeGCSandbox(pods[0], 1, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 1),
   103  				makeGCSandbox(pods[0], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 0),
   104  			},
   105  			containers:           []containerTemplate{},
   106  			remain:               []int{0},
   107  			evictTerminatingPods: false,
   108  		},
   109  		{
   110  			description: "older exited sandboxes with containers for existing pods should not be garbage collected even if there are more than one exited sandboxes.",
   111  			sandboxes: []sandboxTemplate{
   112  				makeGCSandbox(pods[0], 1, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 1),
   113  				makeGCSandbox(pods[0], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 0),
   114  			},
   115  			containers: []containerTemplate{
   116  				{pod: pods[0], container: &pods[0].Spec.Containers[0], sandboxAttempt: 0, state: runtimeapi.ContainerState_CONTAINER_EXITED},
   117  			},
   118  			remain:               []int{0, 1},
   119  			evictTerminatingPods: false,
   120  		},
   121  		{
   122  			description: "non-running sandboxes for existing pods should be garbage collected if evictTerminatingPods is set.",
   123  			sandboxes: []sandboxTemplate{
   124  				makeGCSandbox(pods[0], 0, runtimeapi.PodSandboxState_SANDBOX_READY, true, true, 0),
   125  				makeGCSandbox(pods[1], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, true, 0),
   126  			},
   127  			containers:           []containerTemplate{},
   128  			remain:               []int{0},
   129  			evictTerminatingPods: true,
   130  		},
   131  		{
   132  			description: "sandbox with containers should not be garbage collected.",
   133  			sandboxes: []sandboxTemplate{
   134  				makeGCSandbox(pods[0], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, false, false, 0),
   135  			},
   136  			containers: []containerTemplate{
   137  				{pod: pods[0], container: &pods[0].Spec.Containers[0], state: runtimeapi.ContainerState_CONTAINER_EXITED},
   138  			},
   139  			remain:               []int{0},
   140  			evictTerminatingPods: false,
   141  		},
   142  		{
   143  			description: "multiple sandboxes should be handled properly.",
   144  			sandboxes: []sandboxTemplate{
   145  				// running sandbox.
   146  				makeGCSandbox(pods[0], 1, runtimeapi.PodSandboxState_SANDBOX_READY, true, false, 1),
   147  				// exited sandbox without containers.
   148  				makeGCSandbox(pods[0], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 0),
   149  				// exited sandbox with containers.
   150  				makeGCSandbox(pods[1], 1, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 1),
   151  				// exited sandbox without containers.
   152  				makeGCSandbox(pods[1], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, true, false, 0),
   153  				// exited sandbox without containers for deleted pods.
   154  				makeGCSandbox(pods[2], 0, runtimeapi.PodSandboxState_SANDBOX_NOTREADY, false, true, 0),
   155  			},
   156  			containers: []containerTemplate{
   157  				{pod: pods[1], container: &pods[1].Spec.Containers[0], sandboxAttempt: 1, state: runtimeapi.ContainerState_CONTAINER_EXITED},
   158  			},
   159  			remain:               []int{0, 2},
   160  			evictTerminatingPods: false,
   161  		},
   162  	} {
   163  		t.Run(test.description, func(t *testing.T) {
   164  			ctx := context.Background()
   165  			podStateProvider.removed = make(map[types.UID]struct{})
   166  			podStateProvider.terminated = make(map[types.UID]struct{})
   167  			fakeSandboxes := makeFakePodSandboxes(t, m, test.sandboxes)
   168  			fakeContainers := makeFakeContainers(t, m, test.containers)
   169  			for _, s := range test.sandboxes {
   170  				if !s.running && s.pod.Name == "deleted" {
   171  					podStateProvider.removed[s.pod.UID] = struct{}{}
   172  				}
   173  				if s.terminating {
   174  					podStateProvider.terminated[s.pod.UID] = struct{}{}
   175  				}
   176  			}
   177  			fakeRuntime.SetFakeSandboxes(fakeSandboxes)
   178  			fakeRuntime.SetFakeContainers(fakeContainers)
   179  
   180  			err := m.containerGC.evictSandboxes(ctx, test.evictTerminatingPods)
   181  			assert.NoError(t, err)
   182  			realRemain, err := fakeRuntime.ListPodSandbox(ctx, nil)
   183  			assert.NoError(t, err)
   184  			assert.Len(t, realRemain, len(test.remain))
   185  			for _, remain := range test.remain {
   186  				resp, err := fakeRuntime.PodSandboxStatus(ctx, fakeSandboxes[remain].Id, false)
   187  				assert.NoError(t, err)
   188  				assert.Equal(t, &fakeSandboxes[remain].PodSandboxStatus, resp.Status)
   189  			}
   190  		})
   191  	}
   192  }
   193  
   194  func makeGCContainer(podName, containerName string, attempt int, createdAt int64, state runtimeapi.ContainerState) containerTemplate {
   195  	container := makeTestContainer(containerName, "test-image")
   196  	pod := makeTestPod(podName, "test-ns", podName, []v1.Container{container})
   197  	return containerTemplate{
   198  		pod:       pod,
   199  		container: &container,
   200  		attempt:   attempt,
   201  		createdAt: createdAt,
   202  		state:     state,
   203  	}
   204  }
   205  
   206  func TestContainerGC(t *testing.T) {
   207  	fakeRuntime, _, m, err := createTestRuntimeManager()
   208  	assert.NoError(t, err)
   209  
   210  	podStateProvider := m.containerGC.podStateProvider.(*fakePodStateProvider)
   211  	defaultGCPolicy := kubecontainer.GCPolicy{MinAge: time.Hour, MaxPerPodContainer: 2, MaxContainers: 6}
   212  
   213  	for _, test := range []struct {
   214  		description          string                  // description of the test case
   215  		containers           []containerTemplate     // templates of containers
   216  		policy               *kubecontainer.GCPolicy // container gc policy
   217  		remain               []int                   // template indexes of remaining containers
   218  		evictTerminatingPods bool
   219  		allSourcesReady      bool
   220  	}{
   221  		{
   222  			description: "all containers should be removed when max container limit is 0",
   223  			containers: []containerTemplate{
   224  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   225  			},
   226  			policy:               &kubecontainer.GCPolicy{MinAge: time.Minute, MaxPerPodContainer: 1, MaxContainers: 0},
   227  			remain:               []int{},
   228  			evictTerminatingPods: false,
   229  			allSourcesReady:      true,
   230  		},
   231  		{
   232  			description: "max containers should be complied when no max per pod container limit is set",
   233  			containers: []containerTemplate{
   234  				makeGCContainer("foo", "bar", 4, 4, runtimeapi.ContainerState_CONTAINER_EXITED),
   235  				makeGCContainer("foo", "bar", 3, 3, runtimeapi.ContainerState_CONTAINER_EXITED),
   236  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   237  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   238  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   239  			},
   240  			policy:               &kubecontainer.GCPolicy{MinAge: time.Minute, MaxPerPodContainer: -1, MaxContainers: 4},
   241  			remain:               []int{0, 1, 2, 3},
   242  			evictTerminatingPods: false,
   243  			allSourcesReady:      true,
   244  		},
   245  		{
   246  			description: "no containers should be removed if both max container and per pod container limits are not set",
   247  			containers: []containerTemplate{
   248  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   249  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   250  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   251  			},
   252  			policy:               &kubecontainer.GCPolicy{MinAge: time.Minute, MaxPerPodContainer: -1, MaxContainers: -1},
   253  			remain:               []int{0, 1, 2},
   254  			evictTerminatingPods: false,
   255  			allSourcesReady:      true,
   256  		},
   257  		{
   258  			description: "recently started containers should not be removed",
   259  			containers: []containerTemplate{
   260  				makeGCContainer("foo", "bar", 2, time.Now().UnixNano(), runtimeapi.ContainerState_CONTAINER_EXITED),
   261  				makeGCContainer("foo", "bar", 1, time.Now().UnixNano(), runtimeapi.ContainerState_CONTAINER_EXITED),
   262  				makeGCContainer("foo", "bar", 0, time.Now().UnixNano(), runtimeapi.ContainerState_CONTAINER_EXITED),
   263  			},
   264  			remain:               []int{0, 1, 2},
   265  			evictTerminatingPods: false,
   266  			allSourcesReady:      true,
   267  		},
   268  		{
   269  			description: "oldest containers should be removed when per pod container limit exceeded",
   270  			containers: []containerTemplate{
   271  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   272  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   273  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   274  			},
   275  			remain:               []int{0, 1},
   276  			evictTerminatingPods: false,
   277  			allSourcesReady:      true,
   278  		},
   279  		{
   280  			description: "running containers should not be removed",
   281  			containers: []containerTemplate{
   282  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   283  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   284  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_RUNNING),
   285  			},
   286  			remain:               []int{0, 1, 2},
   287  			evictTerminatingPods: false,
   288  			allSourcesReady:      true,
   289  		},
   290  		{
   291  			description: "no containers should be removed when limits are not exceeded",
   292  			containers: []containerTemplate{
   293  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   294  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   295  			},
   296  			remain:               []int{0, 1},
   297  			evictTerminatingPods: false,
   298  			allSourcesReady:      true,
   299  		},
   300  		{
   301  			description: "max container count should apply per (UID, container) pair",
   302  			containers: []containerTemplate{
   303  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   304  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   305  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   306  				makeGCContainer("foo1", "baz", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   307  				makeGCContainer("foo1", "baz", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   308  				makeGCContainer("foo1", "baz", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   309  				makeGCContainer("foo2", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   310  				makeGCContainer("foo2", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   311  				makeGCContainer("foo2", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   312  			},
   313  			remain:               []int{0, 1, 3, 4, 6, 7},
   314  			evictTerminatingPods: false,
   315  			allSourcesReady:      true,
   316  		},
   317  		{
   318  			description: "max limit should apply and try to keep from every pod",
   319  			containers: []containerTemplate{
   320  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   321  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   322  				makeGCContainer("foo1", "bar1", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   323  				makeGCContainer("foo1", "bar1", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   324  				makeGCContainer("foo2", "bar2", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   325  				makeGCContainer("foo2", "bar2", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   326  				makeGCContainer("foo3", "bar3", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   327  				makeGCContainer("foo3", "bar3", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   328  				makeGCContainer("foo4", "bar4", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   329  				makeGCContainer("foo4", "bar4", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   330  			},
   331  			remain:               []int{0, 2, 4, 6, 8},
   332  			evictTerminatingPods: false,
   333  			allSourcesReady:      true,
   334  		},
   335  		{
   336  			description: "oldest pods should be removed if limit exceeded",
   337  			containers: []containerTemplate{
   338  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   339  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   340  				makeGCContainer("foo1", "bar1", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   341  				makeGCContainer("foo1", "bar1", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   342  				makeGCContainer("foo2", "bar2", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   343  				makeGCContainer("foo3", "bar3", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   344  				makeGCContainer("foo4", "bar4", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   345  				makeGCContainer("foo5", "bar5", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   346  				makeGCContainer("foo6", "bar6", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   347  				makeGCContainer("foo7", "bar7", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   348  			},
   349  			remain:               []int{0, 2, 4, 6, 8, 9},
   350  			evictTerminatingPods: false,
   351  			allSourcesReady:      true,
   352  		},
   353  		{
   354  			description: "all non-running containers should be removed when evictTerminatingPods is set",
   355  			containers: []containerTemplate{
   356  				makeGCContainer("foo", "bar", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   357  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   358  				makeGCContainer("foo1", "bar1", 2, 2, runtimeapi.ContainerState_CONTAINER_EXITED),
   359  				makeGCContainer("foo1", "bar1", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   360  				makeGCContainer("running", "bar2", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   361  				makeGCContainer("foo3", "bar3", 0, 0, runtimeapi.ContainerState_CONTAINER_RUNNING),
   362  			},
   363  			remain:               []int{4, 5},
   364  			evictTerminatingPods: true,
   365  			allSourcesReady:      true,
   366  		},
   367  		{
   368  			description: "containers for deleted pods should be removed",
   369  			containers: []containerTemplate{
   370  				makeGCContainer("foo", "bar", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   371  				makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   372  				// deleted pods still respect MinAge.
   373  				makeGCContainer("deleted", "bar1", 2, time.Now().UnixNano(), runtimeapi.ContainerState_CONTAINER_EXITED),
   374  				makeGCContainer("deleted", "bar1", 1, 1, runtimeapi.ContainerState_CONTAINER_EXITED),
   375  				makeGCContainer("deleted", "bar1", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   376  			},
   377  			remain:               []int{0, 1, 2},
   378  			evictTerminatingPods: false,
   379  			allSourcesReady:      true,
   380  		},
   381  		{
   382  			description: "containers for deleted pods may not be removed if allSourcesReady is set false ",
   383  			containers: []containerTemplate{
   384  				makeGCContainer("deleted", "bar1", 0, 0, runtimeapi.ContainerState_CONTAINER_EXITED),
   385  			},
   386  			remain:               []int{0},
   387  			evictTerminatingPods: true,
   388  			allSourcesReady:      false,
   389  		},
   390  	} {
   391  		t.Run(test.description, func(t *testing.T) {
   392  			ctx := context.Background()
   393  			podStateProvider.removed = make(map[types.UID]struct{})
   394  			podStateProvider.terminated = make(map[types.UID]struct{})
   395  			fakeContainers := makeFakeContainers(t, m, test.containers)
   396  			for _, s := range test.containers {
   397  				if s.pod.Name == "deleted" {
   398  					podStateProvider.removed[s.pod.UID] = struct{}{}
   399  				}
   400  				if s.pod.Name != "running" {
   401  					podStateProvider.terminated[s.pod.UID] = struct{}{}
   402  				}
   403  			}
   404  			fakeRuntime.SetFakeContainers(fakeContainers)
   405  
   406  			if test.policy == nil {
   407  				test.policy = &defaultGCPolicy
   408  			}
   409  			err := m.containerGC.evictContainers(ctx, *test.policy, test.allSourcesReady, test.evictTerminatingPods)
   410  			assert.NoError(t, err)
   411  			realRemain, err := fakeRuntime.ListContainers(ctx, nil)
   412  			assert.NoError(t, err)
   413  			assert.Len(t, realRemain, len(test.remain))
   414  			for _, remain := range test.remain {
   415  				resp, err := fakeRuntime.ContainerStatus(ctx, fakeContainers[remain].Id, false)
   416  				assert.NoError(t, err)
   417  				assert.Equal(t, &fakeContainers[remain].ContainerStatus, resp.Status)
   418  			}
   419  		})
   420  	}
   421  }
   422  
   423  // Notice that legacy container symlink is not tested since it may be deprecated soon.
   424  func TestPodLogDirectoryGC(t *testing.T) {
   425  	ctx := context.Background()
   426  	_, _, m, err := createTestRuntimeManager()
   427  	assert.NoError(t, err)
   428  	fakeOS := m.osInterface.(*containertest.FakeOS)
   429  	podStateProvider := m.containerGC.podStateProvider.(*fakePodStateProvider)
   430  
   431  	// pod log directories without corresponding pods should be removed.
   432  	files := []string{"123", "456", "789", "012", "name_namespace_321", "name_namespace_654"}
   433  	podLogsDirectory := "/var/log/pods"
   434  	removed := []string{
   435  		filepath.Join(podLogsDirectory, "789"),
   436  		filepath.Join(podLogsDirectory, "012"),
   437  		filepath.Join(podLogsDirectory, "name_namespace_654"),
   438  	}
   439  	podStateProvider.removed["012"] = struct{}{}
   440  	podStateProvider.removed["789"] = struct{}{}
   441  	podStateProvider.removed["654"] = struct{}{}
   442  
   443  	ctrl := gomock.NewController(t)
   444  	defer ctrl.Finish()
   445  
   446  	fakeOS.ReadDirFn = func(string) ([]os.DirEntry, error) {
   447  		var dirEntries []os.DirEntry
   448  		for _, file := range files {
   449  			mockDE := containertest.NewMockDirEntry(ctrl)
   450  			mockDE.EXPECT().Name().Return(file)
   451  			dirEntries = append(dirEntries, mockDE)
   452  		}
   453  		return dirEntries, nil
   454  	}
   455  
   456  	// allSourcesReady == true, pod log directories without corresponding pod should be removed.
   457  	err = m.containerGC.evictPodLogsDirectories(ctx, true)
   458  	assert.NoError(t, err)
   459  	assert.Equal(t, removed, fakeOS.Removes)
   460  
   461  	// allSourcesReady == false, pod log directories should not be removed.
   462  	fakeOS.Removes = []string{}
   463  	err = m.containerGC.evictPodLogsDirectories(ctx, false)
   464  	assert.NoError(t, err)
   465  	assert.Empty(t, fakeOS.Removes)
   466  }
   467  
   468  func TestUnknownStateContainerGC(t *testing.T) {
   469  	ctx := context.Background()
   470  	fakeRuntime, _, m, err := createTestRuntimeManager()
   471  	assert.NoError(t, err)
   472  
   473  	// podStateProvider := m.containerGC.podStateProvider.(*fakePodStateProvider)
   474  	defaultGCPolicy := kubecontainer.GCPolicy{MinAge: time.Hour, MaxPerPodContainer: 0, MaxContainers: 0}
   475  
   476  	fakeContainers := makeFakeContainers(t, m, []containerTemplate{
   477  		makeGCContainer("foo", "bar", 0, 0, runtimeapi.ContainerState_CONTAINER_UNKNOWN),
   478  	})
   479  	fakeRuntime.SetFakeContainers(fakeContainers)
   480  
   481  	err = m.containerGC.evictContainers(ctx, defaultGCPolicy, true, false)
   482  	assert.NoError(t, err)
   483  
   484  	assert.Contains(t, fakeRuntime.GetCalls(), "StopContainer", "RemoveContainer",
   485  		"container in unknown state should be stopped before being removed")
   486  
   487  	remain, err := fakeRuntime.ListContainers(ctx, nil)
   488  	assert.NoError(t, err)
   489  	assert.Empty(t, remain)
   490  }
   491  

View as plain text