diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index b85c555896b..0db0de8023c 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -6424,6 +6424,7 @@ func ValidateNode(node *core.Node) field.ErrorList { // All status fields are optional and can be updated later. // That said, if specified, we need to ensure they are valid. allErrs = append(allErrs, ValidateNodeResources(node)...) + allErrs = append(allErrs, validateNodeSwapStatus(node.Status.NodeInfo.Swap, fldPath.Child("nodeSwapStatus"))...) // validate PodCIDRS only if we need to if len(node.Spec.PodCIDRs) > 0 { @@ -8769,3 +8770,22 @@ func IsValidIPForLegacyField(fldPath *field.Path, value string, validOldIPs []st func IsValidCIDRForLegacyField(fldPath *field.Path, value string, validOldCIDRs []string) field.ErrorList { return validation.IsValidCIDRForLegacyField(fldPath, value, utilfeature.DefaultFeatureGate.Enabled(features.StrictIPCIDRValidation), validOldCIDRs) } + +func validateNodeSwapStatus(nodeSwapStatus *core.NodeSwapStatus, fldPath *field.Path) field.ErrorList { + allErrors := field.ErrorList{} + + if nodeSwapStatus == nil { + return allErrors + } + + if nodeSwapStatus.Capacity != nil { + capacityFld := fldPath.Child("capacity") + + errs := ValidatePositiveField(*nodeSwapStatus.Capacity, capacityFld) + if len(errs) > 0 { + allErrors = append(allErrors, errs...) + } + } + + return allErrors +} diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index a6e3135e898..a744fee414d 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -26758,3 +26758,64 @@ func TestValidatePodResize(t *testing.T) { }) } } + +func TestValidateNodeSwapStatus(t *testing.T) { + makeNode := func(nodeSwapStatus *core.NodeSwapStatus) core.Node { + node := makeNode("test-node", nil) + node.Status.NodeInfo.Swap = nodeSwapStatus + + return node + } + makeSwapStatus := func(capacity int64) *core.NodeSwapStatus { + return &core.NodeSwapStatus{ + Capacity: ptr.To(capacity), + } + } + + testCases := []struct { + name string + expectError bool + node core.Node + }{ + { + name: "node with nil nodeSwapStatus", + expectError: false, + node: makeNode(nil), + }, + { + name: "node with nil nodeSwapStatus.Capacity", + expectError: false, + node: makeNode(&core.NodeSwapStatus{}), + }, + { + name: "node with positive capacity", + expectError: false, + node: makeNode(makeSwapStatus(123456)), + }, + { + name: "node with zero capacity should be invalid (nodeSwapStatus should be nil)", + expectError: true, + node: makeNode(makeSwapStatus(0)), + }, + { + name: "node with negative capacity should be invalid", + expectError: true, + node: makeNode(makeSwapStatus(-123456)), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + errs := ValidateNode(&tc.node) + + if len(errs) == 0 && tc.expectError { + t.Errorf("expected failure for %s, but there were none", tc.name) + return + } + if len(errs) != 0 && !tc.expectError { + t.Errorf("expected success for %s, but there were errors: %v", tc.name, errs) + return + } + }) + } +}