diff --git a/pkg/apis/node/types.go b/pkg/apis/node/types.go index a10648f384a..7afeb578b33 100644 --- a/pkg/apis/node/types.go +++ b/pkg/apis/node/types.go @@ -18,6 +18,7 @@ package node import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/apis/core" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -45,6 +46,21 @@ type RuntimeClass struct { // The Handler must conform to the DNS Label (RFC 1123) requirements, and is // immutable. Handler string + + // Overhead represents the resource overhead associated with running a pod for a + // given RuntimeClass. For more details, see + // https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md + // This field is alpha-level as of Kubernetes v1.16, and is only honored by servers + // that enable the PodOverhead feature. + // +optional + Overhead *Overhead +} + +// Overhead structure represents the resource overhead associated with running a pod. +type Overhead struct { + // PodFixed represents the fixed resource overhead associated with running a pod. + // +optional + PodFixed core.ResourceList } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/node/validation/validation.go b/pkg/apis/node/validation/validation.go index b89a01ff58d..d329bd7fe80 100644 --- a/pkg/apis/node/validation/validation.go +++ b/pkg/apis/node/validation/validation.go @@ -19,7 +19,11 @@ package validation import ( apivalidation "k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/util/validation/field" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/apis/core" + corevalidation "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/node" + "k8s.io/kubernetes/pkg/features" ) // ValidateRuntimeClass validates the RuntimeClass @@ -30,6 +34,10 @@ func ValidateRuntimeClass(rc *node.RuntimeClass) field.ErrorList { allErrs = append(allErrs, field.Invalid(field.NewPath("handler"), rc.Handler, msg)) } + if rc.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) { + allErrs = append(allErrs, validateOverhead(rc.Overhead, field.NewPath("overhead"))...) + } + return allErrs } @@ -41,3 +49,8 @@ func ValidateRuntimeClassUpdate(new, old *node.RuntimeClass) field.ErrorList { return allErrs } + +func validateOverhead(overhead *node.Overhead, fldPath *field.Path) field.ErrorList { + // reuse the ResourceRequirements validation logic + return corevalidation.ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead.PodFixed}, fldPath) +} diff --git a/pkg/apis/node/validation/validation_test.go b/pkg/apis/node/validation/validation_test.go index fcbdc0e9a1d..188572d5041 100644 --- a/pkg/apis/node/validation/validation_test.go +++ b/pkg/apis/node/validation/validation_test.go @@ -19,8 +19,13 @@ package validation import ( "testing" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/node" + "k8s.io/kubernetes/pkg/features" "github.com/stretchr/testify/assert" ) @@ -126,3 +131,58 @@ func TestValidateRuntimeUpdate(t *testing.T) { }) } } + +func TestValidateOverhead(t *testing.T) { + + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodOverhead, true)() + + successCase := []struct { + Name string + overhead *node.Overhead + }{ + { + Name: "Overhead with valid cpu and memory resources", + overhead: &node.Overhead{ + PodFixed: core.ResourceList{ + core.ResourceName(core.ResourceCPU): resource.MustParse("10"), + core.ResourceName(core.ResourceMemory): resource.MustParse("10G"), + }, + }, + }, + } + + for _, tc := range successCase { + rc := &node.RuntimeClass{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + Handler: "bar", + Overhead: tc.overhead, + } + if errs := ValidateRuntimeClass(rc); len(errs) != 0 { + t.Errorf("%q unexpected error: %v", tc.Name, errs) + } + } + + errorCase := []struct { + Name string + overhead *node.Overhead + }{ + { + Name: "Invalid Resources", + overhead: &node.Overhead{ + PodFixed: core.ResourceList{ + core.ResourceName("my.org"): resource.MustParse("10m"), + }, + }, + }, + } + for _, tc := range errorCase { + rc := &node.RuntimeClass{ + ObjectMeta: metav1.ObjectMeta{Name: "foo"}, + Handler: "bar", + Overhead: tc.overhead, + } + if errs := ValidateRuntimeClass(rc); len(errs) == 0 { + t.Errorf("%q expected error", tc.Name) + } + } +}