...

Source file src/k8s.io/kubernetes/test/e2e_node/image_gc_test.go

Documentation: k8s.io/kubernetes/test/e2e_node

     1  /*
     2  Copyright 2024 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 e2enode
    18  
    19  import (
    20  	"context"
    21  	"time"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	internalapi "k8s.io/cri-api/pkg/apis"
    26  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    27  	kubefeatures "k8s.io/kubernetes/pkg/features"
    28  	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
    29  	"k8s.io/kubernetes/test/e2e/framework"
    30  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    31  	admissionapi "k8s.io/pod-security-admission/api"
    32  
    33  	"github.com/onsi/ginkgo/v2"
    34  	"github.com/onsi/gomega"
    35  )
    36  
    37  const (
    38  	// Kubelet GC's on a frequency of once every 5 minutes
    39  	// Add a little leeway to give it time.
    40  	checkGCUntil time.Duration = 6 * time.Minute
    41  	checkGCFreq  time.Duration = 30 * time.Second
    42  )
    43  
    44  var _ = SIGDescribe("ImageGarbageCollect", framework.WithSerial(), framework.WithNodeFeature("GarbageCollect"), func() {
    45  	f := framework.NewDefaultFramework("image-garbage-collect-test")
    46  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    47  	var is internalapi.ImageManagerService
    48  	ginkgo.BeforeEach(func() {
    49  		var err error
    50  		_, is, err = getCRIClient()
    51  		framework.ExpectNoError(err)
    52  	})
    53  	ginkgo.AfterEach(func() {
    54  		framework.ExpectNoError(PrePullAllImages())
    55  	})
    56  	ginkgo.Context("when ImageMaximumGCAge is set", func() {
    57  		tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
    58  			initialConfig.ImageMaximumGCAge = metav1.Duration{Duration: time.Duration(time.Minute * 1)}
    59  			initialConfig.ImageMinimumGCAge = metav1.Duration{Duration: time.Duration(time.Second * 1)}
    60  			if initialConfig.FeatureGates == nil {
    61  				initialConfig.FeatureGates = make(map[string]bool)
    62  			}
    63  			initialConfig.FeatureGates[string(kubefeatures.ImageMaximumGCAge)] = true
    64  		})
    65  		ginkgo.It("should GC unused images", func(ctx context.Context) {
    66  			pod := innocentPod()
    67  			e2epod.NewPodClient(f).CreateBatch(ctx, []*v1.Pod{pod})
    68  
    69  			_, err := is.PullImage(context.Background(), &runtimeapi.ImageSpec{Image: agnhostImage}, nil, nil)
    70  			framework.ExpectNoError(err)
    71  
    72  			allImages, err := is.ListImages(context.Background(), &runtimeapi.ImageFilter{})
    73  			framework.ExpectNoError(err)
    74  
    75  			e2epod.NewPodClient(f).DeleteSync(ctx, pod.ObjectMeta.Name, metav1.DeleteOptions{}, e2epod.DefaultPodDeletionTimeout)
    76  
    77  			// Even though the image gc max timing is less, we are bound by the kubelet's
    78  			// ImageGCPeriod, which is hardcoded to 5 minutes.
    79  			gomega.Eventually(ctx, func() int {
    80  				gcdImageList, err := is.ListImages(context.Background(), &runtimeapi.ImageFilter{})
    81  				framework.ExpectNoError(err)
    82  				return len(gcdImageList)
    83  			}, checkGCUntil, checkGCFreq).Should(gomega.BeNumerically("<", len(allImages)))
    84  		})
    85  		ginkgo.It("should not GC unused images prematurely", func(ctx context.Context) {
    86  			pod := innocentPod()
    87  			e2epod.NewPodClient(f).CreateBatch(ctx, []*v1.Pod{pod})
    88  
    89  			_, err := is.PullImage(context.Background(), &runtimeapi.ImageSpec{Image: agnhostImage}, nil, nil)
    90  			framework.ExpectNoError(err)
    91  
    92  			allImages, err := is.ListImages(context.Background(), &runtimeapi.ImageFilter{})
    93  			framework.ExpectNoError(err)
    94  
    95  			e2epod.NewPodClient(f).DeleteSync(ctx, pod.ObjectMeta.Name, metav1.DeleteOptions{}, e2epod.DefaultPodDeletionTimeout)
    96  
    97  			restartKubelet(true)
    98  			waitForKubeletToStart(ctx, f)
    99  
   100  			// Wait until the maxAge of the image after the kubelet is restarted to ensure it doesn't
   101  			// GC too early.
   102  			gomega.Consistently(ctx, func() int {
   103  				gcdImageList, err := is.ListImages(context.Background(), &runtimeapi.ImageFilter{})
   104  				framework.ExpectNoError(err)
   105  				return len(gcdImageList)
   106  			}, 50*time.Second, 10*time.Second).Should(gomega.Equal(len(allImages)))
   107  
   108  			// Even though the image gc max timing is less, we are bound by the kubelet's
   109  			// ImageGCPeriod, which is hardcoded to 5 minutes.
   110  			gomega.Eventually(ctx, func() int {
   111  				gcdImageList, err := is.ListImages(context.Background(), &runtimeapi.ImageFilter{})
   112  				framework.ExpectNoError(err)
   113  				return len(gcdImageList)
   114  			}, checkGCUntil, checkGCFreq).Should(gomega.BeNumerically("<", len(allImages)))
   115  		})
   116  	})
   117  })
   118  

View as plain text