From 92129d36bf18f9fe2b6dc47b6080a86f65b4d6f7 Mon Sep 17 00:00:00 2001 From: Neeraj Krishna Gopalakrishna Date: Fri, 2 Jan 2026 15:34:46 +0530 Subject: [PATCH] Add negative validation for imageMinimumGCAge --- pkg/generated/openapi/zz_generated.openapi.go | 2 +- pkg/kubelet/apis/config/types.go | 2 ++ pkg/kubelet/apis/config/validation/validation.go | 3 +++ .../apis/config/validation/validation_test.go | 15 +++++++++++++++ .../src/k8s.io/kubelet/config/v1beta1/types.go | 2 ++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 6f0db21b49d..264f74318bb 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -70225,7 +70225,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen }, "imageMinimumGCAge": { SchemaProps: spec.SchemaProps{ - Description: "imageMinimumGCAge is the minimum age for an unused image before it is garbage collected. Default: \"2m\"", + Description: "imageMinimumGCAge is the minimum age for an unused image before it is garbage collected. The field value must be greater than 0. If unset or 0, defaults to 2m. Default: \"2m\"", Ref: ref(metav1.Duration{}.OpenAPIModelName()), }, }, diff --git a/pkg/kubelet/apis/config/types.go b/pkg/kubelet/apis/config/types.go index 6ab19ac1c66..3dcaafaa6bf 100644 --- a/pkg/kubelet/apis/config/types.go +++ b/pkg/kubelet/apis/config/types.go @@ -219,6 +219,8 @@ type KubeletConfiguration struct { NodeLeaseDurationSeconds int32 // ImageMinimumGCAge is the minimum age for an unused image before it is // garbage collected. + // The field value must be greater than 0. + // If unset or 0, defaults to 2m. ImageMinimumGCAge metav1.Duration // ImageMaximumGCAge is the maximum age an image can be unused before it is garbage collected. // The default of this field is "0s", which disables this field--meaning images won't be garbage diff --git a/pkg/kubelet/apis/config/validation/validation.go b/pkg/kubelet/apis/config/validation/validation.go index 5fc256fc795..02429683a78 100644 --- a/pkg/kubelet/apis/config/validation/validation.go +++ b/pkg/kubelet/apis/config/validation/validation.go @@ -96,6 +96,9 @@ func ValidateKubeletConfiguration(kc *kubeletconfig.KubeletConfiguration, featur if kc.ImageMaximumGCAge.Duration != 0 && !localFeatureGate.Enabled(features.ImageMaximumGCAge) { allErrors = append(allErrors, fmt.Errorf("invalid configuration: ImageMaximumGCAge feature gate is required for Kubelet configuration option imageMaximumGCAge")) } + if kc.ImageMinimumGCAge.Duration < 0 { + allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageMinimumGCAge %v must not be negative", kc.ImageMinimumGCAge.Duration)) + } if kc.ImageMaximumGCAge.Duration < 0 { allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageMaximumGCAge %v must not be negative", kc.ImageMaximumGCAge.Duration)) } diff --git a/pkg/kubelet/apis/config/validation/validation_test.go b/pkg/kubelet/apis/config/validation/validation_test.go index 6b6b80eeb3b..43f8fe28b49 100644 --- a/pkg/kubelet/apis/config/validation/validation_test.go +++ b/pkg/kubelet/apis/config/validation/validation_test.go @@ -48,6 +48,7 @@ var ( HealthzPort: 10248, ImageGCHighThresholdPercent: 85, ImageGCLowThresholdPercent: 80, + ImageMinimumGCAge: metav1.Duration{Duration: 2 * time.Minute}, ImagePullCredentialsVerificationPolicy: "NeverVerifyPreloadedImages", IPTablesDropBit: 15, IPTablesMasqueradeBit: 14, @@ -751,6 +752,20 @@ func TestValidateKubeletConfiguration(t *testing.T) { return conf }, errMsg: "unrecognized feature gate: invalid", + }, { + name: "invalid configuration: invalid ImageMinimumGCAge", + configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { + conf.ImageMinimumGCAge = metav1.Duration{Duration: -1} + return conf + }, + errMsg: "invalid configuration: imageMinimumGCAge -1ns must not be negative", + }, { + name: "valid ImageMinimumGCAge set to default 1ns", + configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { + // Verify that values other than the default are accepted. + conf.ImageMinimumGCAge = metav1.Duration{Duration: 1 * time.Nanosecond} + return conf + }, }, } diff --git a/staging/src/k8s.io/kubelet/config/v1beta1/types.go b/staging/src/k8s.io/kubelet/config/v1beta1/types.go index a7c4e21829c..f7264f77d44 100644 --- a/staging/src/k8s.io/kubelet/config/v1beta1/types.go +++ b/staging/src/k8s.io/kubelet/config/v1beta1/types.go @@ -335,6 +335,8 @@ type KubeletConfiguration struct { NodeLeaseDurationSeconds int32 `json:"nodeLeaseDurationSeconds,omitempty"` // imageMinimumGCAge is the minimum age for an unused image before it is // garbage collected. + // The field value must be greater than 0. + // If unset or 0, defaults to 2m. // Default: "2m" // +optional ImageMinimumGCAge metav1.Duration `json:"imageMinimumGCAge,omitempty"`