Revert "Separate SeparateDiskTests from eviction"

This commit is contained in:
Kevin Hannon 2025-03-21 12:29:30 -04:00 committed by GitHub
parent 71eb04295a
commit a237f429fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 340 deletions

View File

@ -451,10 +451,6 @@ var (
// TODO: remove when SELinuxMount feature gate is enabled by default.
SELinuxMountReadWriteOncePodOnly = framework.WithFeature(framework.ValidFeatures.Add("SELinuxMountReadWriteOncePodOnly"))
// SeparateDiskTest (SIG-node, used for testing separate container runtime filesystem)
// The tests need separate disk settings on nodes and separate filesystems in storage.conf
SeparateDisk = framework.WithFeature(framework.ValidFeatures.Add("SeparateDisk"))
// Owner: sig-network
// Marks tests of KEP-1880 that require the `MultiCIDRServiceAllocator` feature gate
// and the networking.k8s.io/v1alpha1 API.

View File

@ -20,7 +20,6 @@ import (
"context"
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
@ -69,80 +68,27 @@ const (
noStarvedResource = v1.ResourceName("none")
)
type EvictionTestConfig struct {
Signal string
PressureTimeout time.Duration
ExpectedNodeCondition v1.NodeConditionType
ExpectedStarvedResource v1.ResourceName
IsHardEviction bool // true for hard eviction, false for soft eviction
ResourceGetter func(summary *kubeletstatsv1alpha1.Summary) uint64 // Gets available resources (bytes, inodes, etc.)
ResourceThreshold uint64 // Consumed resources that trigger eviction
ThresholdPercentage string // either uint64 or percentage
EvictionGracePeriod string // Used for soft eviction
MetricsLogger func(ctx context.Context)
}
func testRunner(f *framework.Framework, config EvictionTestConfig, specs []podEvictSpec) {
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
ginkgo.Context(fmt.Sprintf(testContextFmt, config.ExpectedNodeCondition), func() {
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
summary := eventuallyGetSummary(ctx)
available := config.ResourceGetter(summary)
if config.ThresholdPercentage == "" && available <= config.ResourceThreshold {
e2eskipper.Skipf("Too few resources free on the host for the eviction test to run")
}
var thresholdValue string
if config.ThresholdPercentage != "" {
thresholdValue = config.ThresholdPercentage
} else {
thresholdValue = fmt.Sprintf("%d", available-config.ResourceThreshold)
}
if config.IsHardEviction {
initialConfig.EvictionHard = map[string]string{config.Signal: thresholdValue}
} else {
initialConfig.EvictionSoft = map[string]string{config.Signal: thresholdValue}
initialConfig.EvictionSoftGracePeriod = map[string]string{config.Signal: config.EvictionGracePeriod}
initialConfig.EvictionMaxPodGracePeriod = 30
}
// Add any special overrides for specific tests
initialConfig.EvictionMinimumReclaim = map[string]string{}
// Ensure that pods are not evicted because of the eviction-hard threshold
// setting a threshold to 0% disables; non-empty map overrides default value (necessary due to omitempty)
if !config.IsHardEviction {
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalMemoryAvailable): "0%"}
}
})
runEvictionTest(f, config.PressureTimeout, config.ExpectedNodeCondition,
config.ExpectedStarvedResource, config.MetricsLogger, specs)
})
}
// InodeEviction tests that the node responds to node disk pressure by evicting only responsible pods.
// Node disk pressure is induced by consuming all inodes on the node.
var _ = SIGDescribe("InodeEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.Eviction, func() {
testRunner(
framework.NewDefaultFramework("inode-eviction-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalNodeFsInodesFree),
PressureTimeout: 15 * time.Minute,
ExpectedNodeCondition: v1.NodeDiskPressure,
ExpectedStarvedResource: resourceInodes,
IsHardEviction: true,
ResourceThreshold: uint64(200000), // Inodes consumed
MetricsLogger: logInodeMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
return *summary.Node.Fs.InodesFree
},
},
[]podEvictSpec{
f := framework.NewDefaultFramework("inode-eviction-test")
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
expectedNodeCondition := v1.NodeDiskPressure
expectedStarvedResource := resourceInodes
pressureTimeout := 15 * time.Minute
inodesConsumed := uint64(200000)
ginkgo.Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() {
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
// Set the eviction threshold to inodesFree - inodesConsumed, so that using inodesConsumed causes an eviction.
summary := eventuallyGetSummary(ctx)
inodesFree := *summary.Node.Fs.InodesFree
if inodesFree <= inodesConsumed {
e2eskipper.Skipf("Too few inodes free on the host for the InodeEviction test to run")
}
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalNodeFsInodesFree): fmt.Sprintf("%d", inodesFree-inodesConsumed)}
initialConfig.EvictionMinimumReclaim = map[string]string{}
})
runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logInodeMetrics, []podEvictSpec{
{
evictionPriority: 1,
// TODO(#127864): Container runtime may not immediate free up the resources after the pod eviction,
@ -154,6 +100,7 @@ var _ = SIGDescribe("InodeEviction", framework.WithSlow(), framework.WithSerial(
pod: innocentPod(),
},
})
})
})
// ImageGCNoEviction tests that the eviction manager is able to prevent eviction
@ -280,32 +227,41 @@ var _ = SIGDescribe("LocalStorageEviction", framework.WithSlow(), framework.With
// Disk pressure is induced by running pods which consume disk space, which exceed the soft eviction threshold.
// Note: This test's purpose is to test Soft Evictions. Local storage was chosen since it is the least costly to run.
var _ = SIGDescribe("LocalStorageSoftEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.Eviction, func() {
diskConsumed := resource.MustParse("4Gi")
testRunner(
framework.NewDefaultFramework("localstorage-eviction-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalNodeFsAvailable),
PressureTimeout: 10 * time.Minute,
ExpectedNodeCondition: v1.NodeDiskPressure,
ExpectedStarvedResource: v1.ResourceEphemeralStorage,
ResourceThreshold: uint64(diskConsumed.Value()), // local storage
IsHardEviction: false,
EvictionGracePeriod: "1m",
MetricsLogger: logDiskMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
return *summary.Node.Fs.AvailableBytes
},
},
[]podEvictSpec{
f := framework.NewDefaultFramework("localstorage-eviction-test")
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
pressureTimeout := 10 * time.Minute
expectedNodeCondition := v1.NodeDiskPressure
expectedStarvedResource := v1.ResourceEphemeralStorage
ginkgo.Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() {
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
diskConsumed := resource.MustParse("4Gi")
summary := eventuallyGetSummary(ctx)
availableBytes := *(summary.Node.Fs.AvailableBytes)
if availableBytes <= uint64(diskConsumed.Value()) {
e2eskipper.Skipf("Too little disk free on the host for the LocalStorageSoftEviction test to run")
}
initialConfig.EvictionSoft = map[string]string{string(evictionapi.SignalNodeFsAvailable): fmt.Sprintf("%d", availableBytes-uint64(diskConsumed.Value()))}
initialConfig.EvictionSoftGracePeriod = map[string]string{string(evictionapi.SignalNodeFsAvailable): "1m"}
// Defer to the pod default grace period
initialConfig.EvictionMaxPodGracePeriod = 30
initialConfig.EvictionMinimumReclaim = map[string]string{}
// Ensure that pods are not evicted because of the eviction-hard threshold
// setting a threshold to 0% disables; non-empty map overrides default value (necessary due to omitempty)
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalMemoryAvailable): "0%"}
})
runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logDiskMetrics, []podEvictSpec{
{
evictionPriority: 1,
pod: diskConsumingPod("container-disk-hog", lotsOfDisk, &v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, v1.ResourceRequirements{}),
// TODO(#127864): Container runtime may not immediate free up the resources after the pod eviction,
// causing the test to fail. We provision an emptyDir volume to avoid relying on the runtime behavior.
pod: diskConsumingPod("container-disk-hog", lotsOfDisk, &v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, v1.ResourceRequirements{}),
},
{
evictionPriority: 0,
pod: innocentPod(),
},
})
})
})
var _ = SIGDescribe("LocalStorageSoftEvictionNotOverwriteTerminationGracePeriodSeconds", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.Eviction, func() {
@ -348,28 +304,20 @@ var _ = SIGDescribe("LocalStorageSoftEvictionNotOverwriteTerminationGracePeriodS
// LocalStorageCapacityIsolationEviction tests that container and volume local storage limits are enforced through evictions
var _ = SIGDescribe("LocalStorageCapacityIsolationEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.LocalStorageCapacityIsolationQuota, feature.Eviction, func() {
sizeLimit := resource.MustParse("40Mi")
useOverLimit := 41 /* Mb */
useUnderLimit := 39 /* Mb */
containerLimit := v1.ResourceList{v1.ResourceEphemeralStorage: sizeLimit}
f := framework.NewDefaultFramework("localstorage-eviction-test")
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
evictionTestTimeout := 10 * time.Minute
ginkgo.Context(fmt.Sprintf(testContextFmt, "evictions due to pod local storage violations"), func() {
tempSetCurrentKubeletConfig(f, func(ctx context.Context, initialConfig *kubeletconfig.KubeletConfiguration) {
// setting a threshold to 0% disables; non-empty map overrides default value (necessary due to omitempty)
initialConfig.EvictionHard = map[string]string{string(evictionapi.SignalMemoryAvailable): "0%"}
})
sizeLimit := resource.MustParse("100Mi")
useOverLimit := 101 /* Mb */
useUnderLimit := 99 /* Mb */
containerLimit := v1.ResourceList{v1.ResourceEphemeralStorage: sizeLimit}
testRunner(
framework.NewDefaultFramework("localstorage-eviction-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalMemoryAvailable),
PressureTimeout: 10 * time.Minute,
ExpectedNodeCondition: noPressure,
ExpectedStarvedResource: noStarvedResource,
IsHardEviction: true,
ThresholdPercentage: "0%", // Disabling this threshold to focus on pod-level limits
MetricsLogger: logDiskMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
// We're not using node-level resource checks for this test
// Just need a non-zero value to pass the resource check
return 1024 * 1024 * 1024 // 1 GB (arbitrary non-zero value)
},
},
[]podEvictSpec{
runEvictionTest(f, evictionTestTimeout, noPressure, noStarvedResource, logDiskMetrics, []podEvictSpec{
{
evictionPriority: 1, // This pod should be evicted because emptyDir (default storage type) usage violation
pod: diskConsumingPod("emptydir-disk-sizelimit", useOverLimit, &v1.VolumeSource{
@ -402,6 +350,7 @@ var _ = SIGDescribe("LocalStorageCapacityIsolationEviction", framework.WithSlow(
pod: diskConsumingPod("container-disk-below-sizelimit", useUnderLimit, nil, v1.ResourceRequirements{Limits: containerLimit}),
},
})
})
})
// PriorityMemoryEvictionOrdering tests that the node responds to node memory pressure by evicting pods.
@ -640,19 +589,6 @@ func runEvictionTest(f *framework.Framework, pressureTimeout time.Duration, expe
// Nodes do not immediately report local storage capacity
// Sleep so that pods requesting local storage do not fail to schedule
time.Sleep(30 * time.Second)
// Check for Pressure
ginkgo.By("make sure node has no pressure before starting")
gomega.Eventually(ctx, func(ctx context.Context) error {
if expectedNodeCondition == noPressure || !hasNodeCondition(ctx, f, expectedNodeCondition) {
return nil
}
return fmt.Errorf("NodeCondition: %s encountered", expectedNodeCondition)
}, pressureDisappearTimeout, evictionPollInterval).Should(gomega.Succeed())
// prepull images only if its image-gc-eviction-test
if regexp.MustCompile(`(?i)image-gc.*`).MatchString(f.BaseName) {
gomega.Expect(PrePullAllImages(ctx)).Should(gomega.Succeed())
}
ginkgo.By("setting up pods to be used by tests")
pods := []*v1.Pod{}
for _, spec := range testSpecs {
@ -720,23 +656,10 @@ func runEvictionTest(f *framework.Framework, pressureTimeout time.Duration, expe
}, postTestConditionMonitoringPeriod, evictionPollInterval).Should(gomega.Succeed())
ginkgo.By("checking for correctly formatted eviction events")
gomega.Eventually(ctx, func(ctx context.Context) error {
return verifyEvictionEvents(ctx, f, testSpecs, expectedStarvedResource)
}, postTestConditionMonitoringPeriod, evictionPollInterval).Should(gomega.Succeed())
verifyEvictionEvents(ctx, f, testSpecs, expectedStarvedResource)
})
ginkgo.AfterEach(func(ctx context.Context) {
prePullImagesIfNeccecary := func() {
if framework.TestContext.PrepullImages {
// The disk eviction test may cause the prepulled images to be evicted,
// prepull those images again to ensure this test not affect following tests.
err := PrePullAllImages(ctx)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}
}
// Run prePull using a defer to make sure it is executed even when the assertions below fails
defer prePullImagesIfNeccecary()
ginkgo.By("deleting pods")
for _, spec := range testSpecs {
ginkgo.By(fmt.Sprintf("deleting pod: %s", spec.pod.Name))
@ -887,7 +810,7 @@ func verifyPodConditions(ctx context.Context, f *framework.Framework, testSpecs
}
}
func verifyEvictionEvents(ctx context.Context, f *framework.Framework, testSpecs []podEvictSpec, expectedStarvedResource v1.ResourceName) error {
func verifyEvictionEvents(ctx context.Context, f *framework.Framework, testSpecs []podEvictSpec, expectedStarvedResource v1.ResourceName) {
for _, spec := range testSpecs {
pod := spec.pod
if spec.evictionPriority != 0 {
@ -901,22 +824,24 @@ func verifyEvictionEvents(ctx context.Context, f *framework.Framework, testSpecs
framework.ExpectNoError(err, "getting events")
gomega.Expect(podEvictEvents.Items).To(gomega.HaveLen(1), "Expected to find 1 eviction event for pod %s, got %d", pod.Name, len(podEvictEvents.Items))
event := podEvictEvents.Items[0]
if expectedStarvedResource != noStarvedResource {
// Check the eviction.StarvedResourceKey
starved, found := event.Annotations[eviction.StarvedResourceKey]
if !found {
return fmt.Errorf("Expected to find an annotation on the eviction event for pod %s containing the starved resource %s, but it was not found",
framework.Failf("Expected to find an annotation on the eviction event for pod %s containing the starved resource %s, but it was not found",
pod.Name, expectedStarvedResource)
}
starvedResource := v1.ResourceName(starved)
gomega.Expect(starvedResource).To(gomega.Equal(expectedStarvedResource), "Expected to the starved_resource annotation on pod %s to contain %s, but got %s instead",
pod.Name, expectedStarvedResource, starvedResource)
// We only check these keys for memory, because ephemeral storage evictions may be due to volume usage, in which case these values are not present
if expectedStarvedResource == v1.ResourceMemory {
// Check the eviction.OffendingContainersKey
offendersString, found := event.Annotations[eviction.OffendingContainersKey]
if !found {
return fmt.Errorf("Expected to find an annotation on the eviction event for pod %s containing the offending containers, but it was not found",
framework.Failf("Expected to find an annotation on the eviction event for pod %s containing the offending containers, but it was not found",
pod.Name)
}
offendingContainers := strings.Split(offendersString, ",")
@ -928,7 +853,7 @@ func verifyEvictionEvents(ctx context.Context, f *framework.Framework, testSpecs
// Check the eviction.OffendingContainersUsageKey
offendingUsageString, found := event.Annotations[eviction.OffendingContainersUsageKey]
if !found {
return fmt.Errorf("Expected to find an annotation on the eviction event for pod %s containing the offending containers' usage, but it was not found",
framework.Failf("Expected to find an annotation on the eviction event for pod %s containing the offending containers' usage, but it was not found",
pod.Name)
}
offendingContainersUsage := strings.Split(offendingUsageString, ",")
@ -943,7 +868,6 @@ func verifyEvictionEvents(ctx context.Context, f *framework.Framework, testSpecs
}
}
}
return nil
}
// Returns TRUE if the node has the node condition, FALSE otherwise

View File

@ -1,193 +0,0 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2enode
import (
"context"
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
kubeletstatsv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
"k8s.io/kubernetes/test/e2e/feature"
"k8s.io/kubernetes/test/e2e/framework"
"github.com/onsi/gomega"
)
// Eviction Policy is described here:
// https://github.com/kubernetes/design-proposals-archive/blob/main/node/kubelet-eviction.md
// Stats is best effort and we evict based on stats being successful
// Container runtime filesystem should display different stats for imagefs and nodefs
var _ = SIGDescribe("Summary", feature.SeparateDisk, func() {
f := framework.NewDefaultFramework("summary-test")
f.It("should display different stats for imagefs and nodefs", func(ctx context.Context) {
summary := eventuallyGetSummary(ctx)
// Available and Capacity are the most useful to tell difference
gomega.Expect(summary.Node.Fs.AvailableBytes).ToNot(gomega.Equal(summary.Node.Runtime.ImageFs.AvailableBytes))
gomega.Expect(summary.Node.Fs.CapacityBytes).ToNot(gomega.Equal(summary.Node.Runtime.ImageFs.CapacityBytes))
})
})
// Node disk pressure is induced by consuming all inodes on the Writeable Layer (imageFS).
var _ = SIGDescribe("InodeEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.SeparateDisk, func() {
testRunner(
framework.NewDefaultFramework("inode-eviction-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalImageFsInodesFree),
PressureTimeout: 15 * time.Minute,
ExpectedNodeCondition: v1.NodeDiskPressure,
ExpectedStarvedResource: resourceInodes,
ResourceThreshold: uint64(200000), // Inodes consumed
IsHardEviction: true,
MetricsLogger: logInodeMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
return *(summary.Node.Runtime.ImageFs.InodesFree)
},
},
[]podEvictSpec{
{
evictionPriority: 1,
pod: inodeConsumingPod("container-inode-hog", lotsOfFiles, nil),
},
{
evictionPriority: 0,
pod: innocentPod(),
},
})
})
// LocalStorageEviction tests that the node responds to node disk pressure by evicting only responsible pods
// Disk pressure is induced by running pods which consume disk space, which exceed the soft eviction threshold.
// Note: This test's purpose is to test Soft Evictions. Local storage was chosen since it is the least costly to run.
var _ = SIGDescribe("LocalStorageSoftEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.SeparateDisk, func() {
diskConsumed := resource.MustParse("4Gi")
testRunner(
framework.NewDefaultFramework("local-storage-imagefs-soft-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalImageFsAvailable),
PressureTimeout: 10 * time.Minute,
ExpectedNodeCondition: v1.NodeDiskPressure,
ExpectedStarvedResource: v1.ResourceEphemeralStorage,
ResourceThreshold: uint64(diskConsumed.Value()), // local storage
IsHardEviction: false,
EvictionGracePeriod: "1m",
MetricsLogger: logDiskMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
return *summary.Node.Runtime.ImageFs.AvailableBytes
},
},
[]podEvictSpec{
{
evictionPriority: 1,
pod: diskConsumingPod("best-effort-disk", lotsOfDisk, nil, v1.ResourceRequirements{}),
},
{
evictionPriority: 0,
pod: innocentPod(),
},
})
})
// LocalStorageCapacityIsolationEviction tests that container and volume local storage limits are enforced through evictions
// removed localstoragecapacityisolation feature gate here as its not a feature gate anymore
var _ = SIGDescribe("LocalStorageCapacityIsolationEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.SeparateDisk, func() {
sizeLimit := resource.MustParse("40Mi")
useOverLimit := 41 /* Mb */
useUnderLimit := 39 /* Mb */
containerLimit := v1.ResourceList{v1.ResourceEphemeralStorage: sizeLimit}
testRunner(
framework.NewDefaultFramework("localstorage-eviction-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalMemoryAvailable),
PressureTimeout: 10 * time.Minute,
ExpectedNodeCondition: noPressure,
ExpectedStarvedResource: noStarvedResource,
IsHardEviction: true,
ThresholdPercentage: "0%", // Disabling this threshold to focus on pod-level limits
MetricsLogger: logDiskMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
// We're not using node-level resource checks for this test
// Just need a non-zero value to pass the resource check
return 1024 * 1024 * 1024 // 1 GB (arbitrary non-zero value)
},
},
[]podEvictSpec{
{
evictionPriority: 1, // This pod should be evicted because emptyDir (default storage type) usage violation
pod: diskConsumingPod("emptydir-disk-sizelimit", useOverLimit, &v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{SizeLimit: &sizeLimit},
}, v1.ResourceRequirements{}),
},
{
evictionPriority: 1, // This pod should cross the container limit by writing to its writable layer.
pod: diskConsumingPod("container-disk-limit", useOverLimit, nil, v1.ResourceRequirements{Limits: containerLimit}),
},
{
evictionPriority: 1, // This pod should hit the container limit by writing to an emptydir
pod: diskConsumingPod("container-emptydir-disk-limit", useOverLimit, &v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
v1.ResourceRequirements{Limits: containerLimit}),
},
{
evictionPriority: 0, // This pod should not be evicted because MemoryBackedVolumes cannot use more space than is allocated to them since SizeMemoryBackedVolumes was enabled
pod: diskConsumingPod("emptydir-memory-sizelimit", useOverLimit, &v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{Medium: "Memory", SizeLimit: &sizeLimit},
}, v1.ResourceRequirements{}),
},
{
evictionPriority: 0, // This pod should not be evicted because it uses less than its limit
pod: diskConsumingPod("emptydir-disk-below-sizelimit", useUnderLimit, &v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{SizeLimit: &sizeLimit},
}, v1.ResourceRequirements{}),
},
{
evictionPriority: 0, // This pod should not be evicted because it uses less than its limit
pod: diskConsumingPod("container-disk-below-sizelimit", useUnderLimit, nil, v1.ResourceRequirements{Limits: containerLimit}),
},
})
})
// ImageStorageVolumeEviction tests that the node responds to node disk pressure by evicting pods.
// Volumes write to the node filesystem so we are testing eviction on nodefs even if it
// exceeds imagefs limits.
var _ = SIGDescribe("ImageStorageVolumeEviction", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.SeparateDisk, func() {
testRunner(
framework.NewDefaultFramework("exceed-nodefs-test"),
EvictionTestConfig{
Signal: string(evictionapi.SignalNodeFsAvailable),
PressureTimeout: 15 * time.Minute,
ExpectedNodeCondition: v1.NodeDiskPressure,
ExpectedStarvedResource: v1.ResourceEphemeralStorage,
IsHardEviction: true,
ThresholdPercentage: "50%", // Use percentage instead of absolute threshold
MetricsLogger: logDiskMetrics,
ResourceGetter: func(summary *kubeletstatsv1alpha1.Summary) uint64 {
return *summary.Node.Fs.AvailableBytes
},
},
[]podEvictSpec{
{
evictionPriority: 1, // This pod should exceed disk capacity on nodefs since writing to a volume
pod: diskConsumingPod("container-emptydir-disk-limit", 16000, &v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
v1.ResourceRequirements{}),
},
})
})