mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Downward API hugepages
This commit is contained in:
parent
26f09b77a8
commit
45bd6cb186
@ -12,9 +12,13 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/api/pod",
|
importpath = "k8s.io/kubernetes/pkg/api/pod",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
|
"//pkg/apis/core/helper:go_default_library",
|
||||||
|
"//pkg/apis/core/v1/helper:go_default_library",
|
||||||
|
"//pkg/apis/core/validation:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -21,8 +21,13 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||||
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
|
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,6 +288,91 @@ func UpdatePodCondition(status *api.PodStatus, condition *api.PodCondition) bool
|
|||||||
return !isEqual
|
return !isEqual
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usesHugePagesInProjectedVolume returns true if hugepages are used in downward api for volume
|
||||||
|
func usesHugePagesInProjectedVolume(podSpec *api.PodSpec) bool {
|
||||||
|
// determine if any container is using hugepages in downward api volume
|
||||||
|
for _, volumeSource := range podSpec.Volumes {
|
||||||
|
if volumeSource.DownwardAPI != nil {
|
||||||
|
for _, item := range volumeSource.DownwardAPI.Items {
|
||||||
|
if item.ResourceFieldRef != nil {
|
||||||
|
if strings.HasPrefix(item.ResourceFieldRef.Resource, "requests.hugepages-") || strings.HasPrefix(item.ResourceFieldRef.Resource, "limits.hugepages-") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// usesHugePagesInProjectedEnv returns true if hugepages are used in downward api for volume
|
||||||
|
func usesHugePagesInProjectedEnv(item api.Container) bool {
|
||||||
|
for _, env := range item.Env {
|
||||||
|
if env.ValueFrom != nil {
|
||||||
|
if env.ValueFrom.ResourceFieldRef != nil {
|
||||||
|
if strings.HasPrefix(env.ValueFrom.ResourceFieldRef.Resource, "requests.hugepages-") || strings.HasPrefix(env.ValueFrom.ResourceFieldRef.Resource, "limits.hugepages-") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// usesMultipleHugePageResources returns true if the pod spec uses more than
|
||||||
|
// one size of hugepage
|
||||||
|
func usesMultipleHugePageResources(podSpec *api.PodSpec) bool {
|
||||||
|
hugePageResources := sets.NewString()
|
||||||
|
resourceSet := helper.ToPodResourcesSet(podSpec)
|
||||||
|
for resourceStr := range resourceSet {
|
||||||
|
if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
|
||||||
|
hugePageResources.Insert(resourceStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(hugePageResources) > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValidationOptionsFromPodSpec returns validation options based on pod specs
|
||||||
|
func GetValidationOptionsFromPodSpec(podSpec, oldPodSpec *api.PodSpec) apivalidation.PodValidationOptions {
|
||||||
|
// default pod validation options based on feature gate
|
||||||
|
opts := validation.PodValidationOptions{
|
||||||
|
// Allow multiple huge pages on pod create if feature is enabled
|
||||||
|
AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
|
||||||
|
// Allow pod spec to use hugepages in downward API if feature is enabled
|
||||||
|
AllowDownwardAPIHugePages: utilfeature.DefaultFeatureGate.Enabled(features.DownwardAPIHugePages),
|
||||||
|
}
|
||||||
|
// if we are not doing an update operation, just return with default options
|
||||||
|
if oldPodSpec == nil {
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
// if old spec used multiple huge page sizes, we must allow it
|
||||||
|
opts.AllowMultipleHugePageResources = opts.AllowMultipleHugePageResources || usesMultipleHugePageResources(oldPodSpec)
|
||||||
|
// if old spec used hugepages in downward api, we must allow it
|
||||||
|
opts.AllowDownwardAPIHugePages = opts.AllowDownwardAPIHugePages || usesHugePagesInProjectedVolume(oldPodSpec)
|
||||||
|
// determine if any container is using hugepages in env var
|
||||||
|
if !opts.AllowDownwardAPIHugePages {
|
||||||
|
VisitContainers(oldPodSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
|
||||||
|
opts.AllowDownwardAPIHugePages = opts.AllowDownwardAPIHugePages || usesHugePagesInProjectedEnv(*c)
|
||||||
|
return !opts.AllowDownwardAPIHugePages
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValidationOptionsFromPodTemplate will return pod validation options for specified template.
|
||||||
|
func GetValidationOptionsFromPodTemplate(podTemplate, oldPodTemplate *api.PodTemplateSpec) apivalidation.PodValidationOptions {
|
||||||
|
var newPodSpec, oldPodSpec *api.PodSpec
|
||||||
|
// we have to be careful about nil pointers here
|
||||||
|
// replication controller in particular is prone to passing nil
|
||||||
|
if podTemplate != nil {
|
||||||
|
newPodSpec = &podTemplate.Spec
|
||||||
|
}
|
||||||
|
if oldPodTemplate != nil {
|
||||||
|
oldPodSpec = &oldPodTemplate.Spec
|
||||||
|
}
|
||||||
|
return GetValidationOptionsFromPodSpec(newPodSpec, oldPodSpec)
|
||||||
|
}
|
||||||
|
|
||||||
// DropDisabledTemplateFields removes disabled fields from the pod template metadata and spec.
|
// DropDisabledTemplateFields removes disabled fields from the pod template metadata and spec.
|
||||||
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a PodTemplateSpec
|
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a PodTemplateSpec
|
||||||
func DropDisabledTemplateFields(podTemplate, oldPodTemplate *api.PodTemplateSpec) {
|
func DropDisabledTemplateFields(podTemplate, oldPodTemplate *api.PodTemplateSpec) {
|
||||||
|
@ -98,6 +98,7 @@ go_test(
|
|||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/legacyscheme:go_default_library",
|
"//pkg/api/legacyscheme:go_default_library",
|
||||||
|
"//pkg/api/pod:go_default_library",
|
||||||
"//pkg/api/testing/compat:go_default_library",
|
"//pkg/api/testing/compat:go_default_library",
|
||||||
"//pkg/apis/apps:go_default_library",
|
"//pkg/apis/apps:go_default_library",
|
||||||
"//pkg/apis/apps/v1:go_default_library",
|
"//pkg/apis/apps/v1:go_default_library",
|
||||||
|
@ -19,14 +19,14 @@ package testing
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||||
"k8s.io/kubernetes/pkg/api/testing/compat"
|
"k8s.io/kubernetes/pkg/api/testing/compat"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
|
||||||
|
|
||||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompatibility_v1_PodSecurityContext(t *testing.T) {
|
func TestCompatibility_v1_PodSecurityContext(t *testing.T) {
|
||||||
@ -159,7 +159,8 @@ func TestCompatibility_v1_PodSecurityContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validator := func(obj runtime.Object) field.ErrorList {
|
validator := func(obj runtime.Object) field.ErrorList {
|
||||||
return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), &(obj.(*api.Pod).ObjectMeta), field.NewPath("spec"))
|
opts := podutil.GetValidationOptionsFromPodSpec(&(obj.(*api.Pod).Spec), nil)
|
||||||
|
return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), &(obj.(*api.Pod).ObjectMeta), field.NewPath("spec"), opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
@ -20,8 +20,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
@ -193,7 +194,20 @@ func ExtractContainerResourceValue(fs *v1.ResourceFieldSelector, container *v1.C
|
|||||||
case "requests.ephemeral-storage":
|
case "requests.ephemeral-storage":
|
||||||
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
||||||
}
|
}
|
||||||
|
// handle extended standard resources with dynamic names
|
||||||
|
// example: requests.hugepages-<pageSize> or limits.hugepages-<pageSize>
|
||||||
|
if strings.HasPrefix(fs.Resource, "requests.") {
|
||||||
|
resourceName := v1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
|
||||||
|
if IsHugePageResourceName(resourceName) {
|
||||||
|
return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(fs.Resource, "limits.") {
|
||||||
|
resourceName := v1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
|
||||||
|
if IsHugePageResourceName(resourceName) {
|
||||||
|
return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
|
return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +225,13 @@ func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Q
|
|||||||
return strconv.FormatInt(m, 10), nil
|
return strconv.FormatInt(m, 10), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertResourceHugePagesToString converts hugepages value to the format of divisor and returns
|
||||||
|
// ceiling of the value.
|
||||||
|
func convertResourceHugePagesToString(hugePages *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||||
|
m := int64(math.Ceil(float64(hugePages.Value()) / float64(divisor.Value())))
|
||||||
|
return strconv.FormatInt(m, 10), nil
|
||||||
|
}
|
||||||
|
|
||||||
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
||||||
// ceiling of the value.
|
// ceiling of the value.
|
||||||
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||||
@ -240,6 +261,8 @@ func MergeContainerResourceLimits(container *v1.Container,
|
|||||||
if container.Resources.Limits == nil {
|
if container.Resources.Limits == nil {
|
||||||
container.Resources.Limits = make(v1.ResourceList)
|
container.Resources.Limits = make(v1.ResourceList)
|
||||||
}
|
}
|
||||||
|
// NOTE: we exclude hugepages-* resources because hugepages are never overcommitted.
|
||||||
|
// This means that the container always has a limit specified.
|
||||||
for _, resource := range []v1.ResourceName{v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage} {
|
for _, resource := range []v1.ResourceName{v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage} {
|
||||||
if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
|
if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
|
||||||
if cap, exists := allocatable[resource]; exists {
|
if cap, exists := allocatable[resource]; exists {
|
||||||
@ -248,3 +271,9 @@ func MergeContainerResourceLimits(container *v1.Container,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsHugePageResourceName returns true if the resource name has the huge page
|
||||||
|
// resource prefix.
|
||||||
|
func IsHugePageResourceName(name v1.ResourceName) bool {
|
||||||
|
return strings.HasPrefix(string(name), v1.ResourceHugePagesPrefix)
|
||||||
|
}
|
||||||
|
@ -32,6 +32,7 @@ go_test(
|
|||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/apps:go_default_library",
|
"//pkg/apis/apps:go_default_library",
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
|
"//pkg/apis/core/validation:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
@ -241,17 +241,17 @@ func ValidateControllerRevisionUpdate(newHistory, oldHistory *apps.ControllerRev
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateDaemonSet tests if required fields in the DaemonSet are set.
|
// ValidateDaemonSet tests if required fields in the DaemonSet are set.
|
||||||
func ValidateDaemonSet(ds *apps.DaemonSet) field.ErrorList {
|
func ValidateDaemonSet(ds *apps.DaemonSet, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&ds.ObjectMeta, true, ValidateDaemonSetName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&ds.ObjectMeta, true, ValidateDaemonSetName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set.
|
// ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set.
|
||||||
func ValidateDaemonSetUpdate(ds, oldDS *apps.DaemonSet) field.ErrorList {
|
func ValidateDaemonSetUpdate(ds, oldDS *apps.DaemonSet, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateDaemonSetSpecUpdate(&ds.Spec, &oldDS.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateDaemonSetSpecUpdate(&ds.Spec, &oldDS.Spec, field.NewPath("spec"))...)
|
||||||
allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ func ValidateDaemonSetStatusUpdate(ds, oldDS *apps.DaemonSet) field.ErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set.
|
// ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set.
|
||||||
func ValidateDaemonSetSpec(spec *apps.DaemonSetSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateDaemonSetSpec(spec *apps.DaemonSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
|
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
|
||||||
@ -320,7 +320,7 @@ func ValidateDaemonSetSpec(spec *apps.DaemonSetSpec, fldPath *field.Path) field.
|
|||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for daemonset"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is invalid for daemonset"))
|
||||||
}
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...)
|
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"), opts)...)
|
||||||
// Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid.
|
// Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid.
|
||||||
allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, fldPath.Child("template", "spec", "volumes"))...)
|
allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, fldPath.Child("template", "spec", "volumes"))...)
|
||||||
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
|
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
|
||||||
@ -474,7 +474,7 @@ func ValidateRollback(rollback *apps.RollbackConfig, fldPath *field.Path) field.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateDeploymentSpec validates given deployment spec.
|
// ValidateDeploymentSpec validates given deployment spec.
|
||||||
func ValidateDeploymentSpec(spec *apps.DeploymentSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateDeploymentSpec(spec *apps.DeploymentSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
||||||
|
|
||||||
@ -491,7 +491,7 @@ func ValidateDeploymentSpec(spec *apps.DeploymentSpec, fldPath *field.Path) fiel
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
|
||||||
} else {
|
} else {
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...)
|
allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"), opts)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
|
allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
|
||||||
@ -541,9 +541,9 @@ func ValidateDeploymentStatus(status *apps.DeploymentStatus, fldPath *field.Path
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateDeploymentUpdate tests if an update to a Deployment is valid.
|
// ValidateDeploymentUpdate tests if an update to a Deployment is valid.
|
||||||
func ValidateDeploymentUpdate(update, old *apps.Deployment) field.ErrorList {
|
func ValidateDeploymentUpdate(update, old *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,9 +564,9 @@ func ValidateDeploymentStatusUpdate(update, old *apps.Deployment) field.ErrorLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateDeployment validates a given Deployment.
|
// ValidateDeployment validates a given Deployment.
|
||||||
func ValidateDeployment(obj *apps.Deployment) field.ErrorList {
|
func ValidateDeployment(obj *apps.Deployment, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,17 +587,17 @@ func ValidateDeploymentRollback(obj *apps.DeploymentRollback) field.ErrorList {
|
|||||||
var ValidateReplicaSetName = apimachineryvalidation.NameIsDNSSubdomain
|
var ValidateReplicaSetName = apimachineryvalidation.NameIsDNSSubdomain
|
||||||
|
|
||||||
// ValidateReplicaSet tests if required fields in the ReplicaSet are set.
|
// ValidateReplicaSet tests if required fields in the ReplicaSet are set.
|
||||||
func ValidateReplicaSet(rs *apps.ReplicaSet) field.ErrorList {
|
func ValidateReplicaSet(rs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set.
|
// ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set.
|
||||||
func ValidateReplicaSetUpdate(rs, oldRs *apps.ReplicaSet) field.ErrorList {
|
func ValidateReplicaSetUpdate(rs, oldRs *apps.ReplicaSet, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
|
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
|
||||||
allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,7 +634,7 @@ func ValidateReplicaSetStatus(status apps.ReplicaSetStatus, fldPath *field.Path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set.
|
// ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set.
|
||||||
func ValidateReplicaSetSpec(spec *apps.ReplicaSetSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateReplicaSetSpec(spec *apps.ReplicaSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
||||||
@ -653,13 +653,13 @@ func ValidateReplicaSetSpec(spec *apps.ReplicaSetSpec, fldPath *field.Path) fiel
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector"))
|
||||||
} else {
|
} else {
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...)
|
allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"), opts)...)
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePodTemplateSpecForReplicaSet validates the given template and ensures that it is in accordance with the desired selector and replicas.
|
// ValidatePodTemplateSpecForReplicaSet validates the given template and ensures that it is in accordance with the desired selector and replicas.
|
||||||
func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path) field.ErrorList {
|
func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
if template == nil {
|
if template == nil {
|
||||||
allErrs = append(allErrs, field.Required(fldPath, ""))
|
allErrs = append(allErrs, field.Required(fldPath, ""))
|
||||||
@ -671,7 +671,7 @@ func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selecto
|
|||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...)
|
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath, opts)...)
|
||||||
if replicas > 1 {
|
if replicas > 1 {
|
||||||
allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...)
|
allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
|
corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1552,7 +1553,7 @@ func TestValidateDaemonSetUpdate(t *testing.T) {
|
|||||||
if len(successCase.old.ObjectMeta.ResourceVersion) == 0 || len(successCase.update.ObjectMeta.ResourceVersion) == 0 {
|
if len(successCase.old.ObjectMeta.ResourceVersion) == 0 || len(successCase.update.ObjectMeta.ResourceVersion) == 0 {
|
||||||
t.Errorf("%q has incorrect test setup with no resource version set", testName)
|
t.Errorf("%q has incorrect test setup with no resource version set", testName)
|
||||||
}
|
}
|
||||||
if errs := ValidateDaemonSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
|
if errs := ValidateDaemonSetUpdate(&successCase.update, &successCase.old, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("%q expected no error, but got: %v", testName, errs)
|
t.Errorf("%q expected no error, but got: %v", testName, errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1770,7 +1771,7 @@ func TestValidateDaemonSetUpdate(t *testing.T) {
|
|||||||
t.Errorf("%q has incorrect test setup with no resource version set", testName)
|
t.Errorf("%q has incorrect test setup with no resource version set", testName)
|
||||||
}
|
}
|
||||||
// Run the tests
|
// Run the tests
|
||||||
if errs := ValidateDaemonSetUpdate(&errorCase.update, &errorCase.old); len(errs) != errorCase.expectedErrNum {
|
if errs := ValidateDaemonSetUpdate(&errorCase.update, &errorCase.old, corevalidation.PodValidationOptions{}); len(errs) != errorCase.expectedErrNum {
|
||||||
t.Errorf("%q expected %d errors, but got %d error: %v", testName, errorCase.expectedErrNum, len(errs), errs)
|
t.Errorf("%q expected %d errors, but got %d error: %v", testName, errorCase.expectedErrNum, len(errs), errs)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("(PASS) %q got errors %v", testName, errs)
|
t.Logf("(PASS) %q got errors %v", testName, errs)
|
||||||
@ -1829,7 +1830,7 @@ func TestValidateDaemonSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := ValidateDaemonSet(&successCase); len(errs) != 0 {
|
if errs := ValidateDaemonSet(&successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1973,7 +1974,7 @@ func TestValidateDaemonSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateDaemonSet(&v)
|
errs := ValidateDaemonSet(&v, corevalidation.PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
}
|
}
|
||||||
@ -2049,7 +2050,7 @@ func TestValidateDeployment(t *testing.T) {
|
|||||||
validDeployment(),
|
validDeployment(),
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := ValidateDeployment(successCase); len(errs) != 0 {
|
if errs := ValidateDeployment(successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2142,7 +2143,7 @@ func TestValidateDeployment(t *testing.T) {
|
|||||||
errorCases["ephemeral containers not allowed"] = invalidEphemeralContainersDeployment
|
errorCases["ephemeral containers not allowed"] = invalidEphemeralContainersDeployment
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateDeployment(v)
|
errs := ValidateDeployment(v, corevalidation.PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("[%s] expected failure", k)
|
t.Errorf("[%s] expected failure", k)
|
||||||
} else if !strings.Contains(errs[0].Error(), k) {
|
} else if !strings.Contains(errs[0].Error(), k) {
|
||||||
@ -2671,7 +2672,7 @@ func TestValidateReplicaSetUpdate(t *testing.T) {
|
|||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||||
if errs := ValidateReplicaSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
|
if errs := ValidateReplicaSetUpdate(&successCase.update, &successCase.old, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2746,7 +2747,7 @@ func TestValidateReplicaSetUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for testName, errorCase := range errorCases {
|
for testName, errorCase := range errorCases {
|
||||||
if errs := ValidateReplicaSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
|
if errs := ValidateReplicaSetUpdate(&errorCase.update, &errorCase.old, corevalidation.PodValidationOptions{}); len(errs) == 0 {
|
||||||
t.Errorf("expected failure: %s", testName)
|
t.Errorf("expected failure: %s", testName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2816,7 +2817,7 @@ func TestValidateReplicaSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := ValidateReplicaSet(&successCase); len(errs) != 0 {
|
if errs := ValidateReplicaSet(&successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2948,7 +2949,7 @@ func TestValidateReplicaSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateReplicaSet(&v)
|
errs := ValidateReplicaSet(&v, corevalidation.PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ go_test(
|
|||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/batch:go_default_library",
|
"//pkg/apis/batch:go_default_library",
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
|
"//pkg/apis/core/validation:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
@ -76,17 +76,17 @@ func ValidateGeneratedSelector(obj *batch.Job) field.ErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateJob validates a Job and returns an ErrorList with any errors.
|
// ValidateJob validates a Job and returns an ErrorList with any errors.
|
||||||
func ValidateJob(job *batch.Job) field.ErrorList {
|
func ValidateJob(job *batch.Job, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
// Jobs and rcs have the same name validation
|
// Jobs and rcs have the same name validation
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateGeneratedSelector(job)...)
|
allErrs = append(allErrs, ValidateGeneratedSelector(job)...)
|
||||||
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateJobSpec validates a JobSpec and returns an ErrorList with any errors.
|
// ValidateJobSpec validates a JobSpec and returns an ErrorList with any errors.
|
||||||
func ValidateJobSpec(spec *batch.JobSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateJobSpec(spec *batch.JobSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := validateJobSpec(spec, fldPath)
|
allErrs := validateJobSpec(spec, fldPath, opts)
|
||||||
|
|
||||||
if spec.Selector == nil {
|
if spec.Selector == nil {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
|
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
|
||||||
@ -104,7 +104,7 @@ func ValidateJobSpec(spec *batch.JobSpec, fldPath *field.Path) field.ErrorList {
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateJobSpec(spec *batch.JobSpec, fldPath *field.Path) field.ErrorList {
|
func validateJobSpec(spec *batch.JobSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if spec.Parallelism != nil {
|
if spec.Parallelism != nil {
|
||||||
@ -123,7 +123,7 @@ func validateJobSpec(spec *batch.JobSpec, fldPath *field.Path) field.ErrorList {
|
|||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.TTLSecondsAfterFinished), fldPath.Child("ttlSecondsAfterFinished"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.TTLSecondsAfterFinished), fldPath.Child("ttlSecondsAfterFinished"))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...)
|
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"), opts)...)
|
||||||
if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure &&
|
if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure &&
|
||||||
spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
|
spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
|
||||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"),
|
allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"),
|
||||||
@ -142,9 +142,9 @@ func ValidateJobStatus(status *batch.JobStatus, fldPath *field.Path) field.Error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateJobUpdate validates an update to a Job and returns an ErrorList with any errors.
|
// ValidateJobUpdate validates an update to a Job and returns an ErrorList with any errors.
|
||||||
func ValidateJobUpdate(job, oldJob *batch.Job) field.ErrorList {
|
func ValidateJobUpdate(job, oldJob *batch.Job, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateJobSpecUpdate(job.Spec, oldJob.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateJobSpecUpdate(job.Spec, oldJob.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +156,9 @@ func ValidateJobUpdateStatus(job, oldJob *batch.Job) field.ErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateJobSpecUpdate validates an update to a JobSpec and returns an ErrorList with any errors.
|
// ValidateJobSpecUpdate validates an update to a JobSpec and returns an ErrorList with any errors.
|
||||||
func ValidateJobSpecUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateJobSpecUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, ValidateJobSpec(&spec, fldPath)...)
|
allErrs = append(allErrs, ValidateJobSpec(&spec, fldPath, opts)...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Completions, oldSpec.Completions, fldPath.Child("completions"))...)
|
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Completions, oldSpec.Completions, fldPath.Child("completions"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Selector, oldSpec.Selector, fldPath.Child("selector"))...)
|
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Selector, oldSpec.Selector, fldPath.Child("selector"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Template, oldSpec.Template, fldPath.Child("template"))...)
|
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Template, oldSpec.Template, fldPath.Child("template"))...)
|
||||||
@ -173,10 +173,10 @@ func ValidateJobStatusUpdate(status, oldStatus batch.JobStatus) field.ErrorList
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateCronJob validates a CronJob and returns an ErrorList with any errors.
|
// ValidateCronJob validates a CronJob and returns an ErrorList with any errors.
|
||||||
func ValidateCronJob(cronJob *batch.CronJob) field.ErrorList {
|
func ValidateCronJob(cronJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
// CronJobs and rcs have the same name validation
|
// CronJobs and rcs have the same name validation
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateCronJobSpec(&cronJob.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateCronJobSpec(&cronJob.Spec, field.NewPath("spec"), opts)...)
|
||||||
if len(cronJob.ObjectMeta.Name) > apimachineryvalidation.DNS1035LabelMaxLength-11 {
|
if len(cronJob.ObjectMeta.Name) > apimachineryvalidation.DNS1035LabelMaxLength-11 {
|
||||||
// The cronjob controller appends a 11-character suffix to the cronjob (`-$TIMESTAMP`) when
|
// The cronjob controller appends a 11-character suffix to the cronjob (`-$TIMESTAMP`) when
|
||||||
// creating a job. The job name length limit is 63 characters.
|
// creating a job. The job name length limit is 63 characters.
|
||||||
@ -188,16 +188,16 @@ func ValidateCronJob(cronJob *batch.CronJob) field.ErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateCronJobUpdate validates an update to a CronJob and returns an ErrorList with any errors.
|
// ValidateCronJobUpdate validates an update to a CronJob and returns an ErrorList with any errors.
|
||||||
func ValidateCronJobUpdate(job, oldJob *batch.CronJob) field.ErrorList {
|
func ValidateCronJobUpdate(job, oldJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateCronJobSpec(&job.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateCronJobSpec(&job.Spec, field.NewPath("spec"), opts)...)
|
||||||
// skip the 52-character name validation limit on update validation
|
// skip the 52-character name validation limit on update validation
|
||||||
// to allow old cronjobs with names > 52 chars to be updated/deleted
|
// to allow old cronjobs with names > 52 chars to be updated/deleted
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateCronJobSpec validates a CronJobSpec and returns an ErrorList with any errors.
|
// ValidateCronJobSpec validates a CronJobSpec and returns an ErrorList with any errors.
|
||||||
func ValidateCronJobSpec(spec *batch.CronJobSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateCronJobSpec(spec *batch.CronJobSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(spec.Schedule) == 0 {
|
if len(spec.Schedule) == 0 {
|
||||||
@ -209,7 +209,7 @@ func ValidateCronJobSpec(spec *batch.CronJobSpec, fldPath *field.Path) field.Err
|
|||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.StartingDeadlineSeconds), fldPath.Child("startingDeadlineSeconds"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.StartingDeadlineSeconds), fldPath.Child("startingDeadlineSeconds"))...)
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, validateConcurrencyPolicy(&spec.ConcurrencyPolicy, fldPath.Child("concurrencyPolicy"))...)
|
allErrs = append(allErrs, validateConcurrencyPolicy(&spec.ConcurrencyPolicy, fldPath.Child("concurrencyPolicy"))...)
|
||||||
allErrs = append(allErrs, ValidateJobTemplateSpec(&spec.JobTemplate, fldPath.Child("jobTemplate"))...)
|
allErrs = append(allErrs, ValidateJobTemplateSpec(&spec.JobTemplate, fldPath.Child("jobTemplate"), opts)...)
|
||||||
|
|
||||||
if spec.SuccessfulJobsHistoryLimit != nil {
|
if spec.SuccessfulJobsHistoryLimit != nil {
|
||||||
// zero is a valid SuccessfulJobsHistoryLimit
|
// zero is a valid SuccessfulJobsHistoryLimit
|
||||||
@ -248,16 +248,16 @@ func validateScheduleFormat(schedule string, fldPath *field.Path) field.ErrorLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateJobTemplate validates a JobTemplate and returns an ErrorList with any errors.
|
// ValidateJobTemplate validates a JobTemplate and returns an ErrorList with any errors.
|
||||||
func ValidateJobTemplate(job *batch.JobTemplate) field.ErrorList {
|
func ValidateJobTemplate(job *batch.JobTemplate, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
// this method should be identical to ValidateJob
|
// this method should be identical to ValidateJob
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateJobTemplateSpec(&job.Template, field.NewPath("template"))...)
|
allErrs = append(allErrs, ValidateJobTemplateSpec(&job.Template, field.NewPath("template"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateJobTemplateSpec validates a JobTemplateSpec and returns an ErrorList with any errors.
|
// ValidateJobTemplateSpec validates a JobTemplateSpec and returns an ErrorList with any errors.
|
||||||
func ValidateJobTemplateSpec(spec *batch.JobTemplateSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateJobTemplateSpec(spec *batch.JobTemplateSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
|
||||||
allErrs := validateJobSpec(&spec.Spec, fldPath.Child("spec"))
|
allErrs := validateJobSpec(&spec.Spec, fldPath.Child("spec"), opts)
|
||||||
|
|
||||||
// jobtemplate will always have the selector automatically generated
|
// jobtemplate will always have the selector automatically generated
|
||||||
if spec.Spec.Selector != nil {
|
if spec.Spec.Selector != nil {
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
|
corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
if errs := ValidateJob(&v); len(errs) != 0 {
|
if errs := ValidateJob(&v, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success for %s: %v", k, errs)
|
t.Errorf("expected success for %s: %v", k, errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,7 +239,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateJob(&v)
|
errs := ValidateJob(&v, corevalidation.PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
} else {
|
} else {
|
||||||
@ -368,7 +369,7 @@ func TestValidateCronJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
if errs := ValidateCronJob(&v); len(errs) != 0 {
|
if errs := ValidateCronJob(&v, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success for %s: %v", k, errs)
|
t.Errorf("expected success for %s: %v", k, errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +377,7 @@ func TestValidateCronJob(t *testing.T) {
|
|||||||
// copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update
|
// copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update
|
||||||
v = *v.DeepCopy()
|
v = *v.DeepCopy()
|
||||||
v.ResourceVersion = "1"
|
v.ResourceVersion = "1"
|
||||||
if errs := ValidateCronJobUpdate(&v, &v); len(errs) != 0 {
|
if errs := ValidateCronJobUpdate(&v, &v, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success for %s: %v", k, errs)
|
t.Errorf("expected success for %s: %v", k, errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -629,7 +630,7 @@ func TestValidateCronJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateCronJob(&v)
|
errs := ValidateCronJob(&v, corevalidation.PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
} else {
|
} else {
|
||||||
@ -644,7 +645,7 @@ func TestValidateCronJob(t *testing.T) {
|
|||||||
// copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update
|
// copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update
|
||||||
v = *v.DeepCopy()
|
v = *v.DeepCopy()
|
||||||
v.ResourceVersion = "1"
|
v.ResourceVersion = "1"
|
||||||
errs = ValidateCronJobUpdate(&v, &v)
|
errs = ValidateCronJobUpdate(&v, &v, corevalidation.PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
if k == "metadata.name: must be no more than 52 characters" {
|
if k == "metadata.name: must be no more than 52 characters" {
|
||||||
continue
|
continue
|
||||||
|
@ -497,3 +497,38 @@ func PersistentVolumeClaimHasClass(claim *core.PersistentVolumeClaim) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toResourceNames(resources core.ResourceList) []core.ResourceName {
|
||||||
|
result := []core.ResourceName{}
|
||||||
|
for resourceName := range resources {
|
||||||
|
result = append(result, resourceName)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSet(resourceNames []core.ResourceName) sets.String {
|
||||||
|
result := sets.NewString()
|
||||||
|
for _, resourceName := range resourceNames {
|
||||||
|
result.Insert(string(resourceName))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// toContainerResourcesSet returns a set of resources names in container resource requirements
|
||||||
|
func toContainerResourcesSet(ctr *core.Container) sets.String {
|
||||||
|
resourceNames := toResourceNames(ctr.Resources.Requests)
|
||||||
|
resourceNames = append(resourceNames, toResourceNames(ctr.Resources.Limits)...)
|
||||||
|
return toSet(resourceNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPodResourcesSet returns a set of resource names in all containers in a pod.
|
||||||
|
func ToPodResourcesSet(podSpec *core.PodSpec) sets.String {
|
||||||
|
result := sets.NewString()
|
||||||
|
for i := range podSpec.InitContainers {
|
||||||
|
result = result.Union(toContainerResourcesSet(&podSpec.InitContainers[i]))
|
||||||
|
}
|
||||||
|
for i := range podSpec.Containers {
|
||||||
|
result = result.Union(toContainerResourcesSet(&podSpec.Containers[i]))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -26,40 +26,33 @@ func (rn ResourceName) String() string {
|
|||||||
|
|
||||||
// CPU returns the CPU limit if specified.
|
// CPU returns the CPU limit if specified.
|
||||||
func (rl *ResourceList) CPU() *resource.Quantity {
|
func (rl *ResourceList) CPU() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceCPU]; ok {
|
return rl.Name(ResourceCPU, resource.DecimalSI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{Format: resource.DecimalSI}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory returns the Memory limit if specified.
|
// Memory returns the Memory limit if specified.
|
||||||
func (rl *ResourceList) Memory() *resource.Quantity {
|
func (rl *ResourceList) Memory() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceMemory]; ok {
|
return rl.Name(ResourceMemory, resource.BinarySI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{Format: resource.BinarySI}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Storage returns the Storage limit if specified.
|
// Storage returns the Storage limit if specified.
|
||||||
func (rl *ResourceList) Storage() *resource.Quantity {
|
func (rl *ResourceList) Storage() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceStorage]; ok {
|
return rl.Name(ResourceStorage, resource.BinarySI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{Format: resource.BinarySI}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pods returns the list of pods
|
// Pods returns the list of pods
|
||||||
func (rl *ResourceList) Pods() *resource.Quantity {
|
func (rl *ResourceList) Pods() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourcePods]; ok {
|
return rl.Name(ResourcePods, resource.DecimalSI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageEphemeral returns the list of ephemeral storage volumes, if any
|
// StorageEphemeral returns the list of ephemeral storage volumes, if any
|
||||||
func (rl *ResourceList) StorageEphemeral() *resource.Quantity {
|
func (rl *ResourceList) StorageEphemeral() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceEphemeralStorage]; ok {
|
return rl.Name(ResourceEphemeralStorage, resource.BinarySI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the resource with name if specified, otherwise it returns a nil quantity with default format.
|
||||||
|
func (rl *ResourceList) Name(name ResourceName, defaultFormat resource.Format) *resource.Quantity {
|
||||||
|
if val, ok := (*rl)[name]; ok {
|
||||||
return &val
|
return &val
|
||||||
}
|
}
|
||||||
return &resource.Quantity{}
|
return &resource.Quantity{Format: defaultFormat}
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *fiel
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateVolumes(volumes []core.Volume, podMeta *metav1.ObjectMeta, fldPath *field.Path) (map[string]core.VolumeSource, field.ErrorList) {
|
func ValidateVolumes(volumes []core.Volume, podMeta *metav1.ObjectMeta, fldPath *field.Path, opts PodValidationOptions) (map[string]core.VolumeSource, field.ErrorList) {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
allNames := sets.String{}
|
allNames := sets.String{}
|
||||||
@ -369,7 +369,7 @@ func ValidateVolumes(volumes []core.Volume, podMeta *metav1.ObjectMeta, fldPath
|
|||||||
for i, vol := range volumes {
|
for i, vol := range volumes {
|
||||||
idxPath := fldPath.Index(i)
|
idxPath := fldPath.Index(i)
|
||||||
namePath := idxPath.Child("name")
|
namePath := idxPath.Child("name")
|
||||||
el := validateVolumeSource(&vol.VolumeSource, idxPath, vol.Name, podMeta)
|
el := validateVolumeSource(&vol.VolumeSource, idxPath, vol.Name, podMeta, opts)
|
||||||
if len(vol.Name) == 0 {
|
if len(vol.Name) == 0 {
|
||||||
el = append(el, field.Required(namePath, ""))
|
el = append(el, field.Required(namePath, ""))
|
||||||
} else {
|
} else {
|
||||||
@ -446,7 +446,7 @@ func devicePathAlreadyExists(devicePath string, mounts map[string]string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volName string, podMeta *metav1.ObjectMeta) field.ErrorList {
|
func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volName string, podMeta *metav1.ObjectMeta, opts PodValidationOptions) field.ErrorList {
|
||||||
numVolumes := 0
|
numVolumes := 0
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
if source.EmptyDir != nil {
|
if source.EmptyDir != nil {
|
||||||
@ -576,7 +576,7 @@ func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volNam
|
|||||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("downwarAPI"), "may not specify more than 1 volume type"))
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("downwarAPI"), "may not specify more than 1 volume type"))
|
||||||
} else {
|
} else {
|
||||||
numVolumes++
|
numVolumes++
|
||||||
allErrs = append(allErrs, validateDownwardAPIVolumeSource(source.DownwardAPI, fldPath.Child("downwardAPI"))...)
|
allErrs = append(allErrs, validateDownwardAPIVolumeSource(source.DownwardAPI, fldPath.Child("downwardAPI"), opts)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if source.FC != nil {
|
if source.FC != nil {
|
||||||
@ -658,7 +658,7 @@ func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volNam
|
|||||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("projected"), "may not specify more than 1 volume type"))
|
allErrs = append(allErrs, field.Forbidden(fldPath.Child("projected"), "may not specify more than 1 volume type"))
|
||||||
} else {
|
} else {
|
||||||
numVolumes++
|
numVolumes++
|
||||||
allErrs = append(allErrs, validateProjectedVolumeSource(source.Projected, fldPath.Child("projected"))...)
|
allErrs = append(allErrs, validateProjectedVolumeSource(source.Projected, fldPath.Child("projected"), opts)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if source.ScaleIO != nil {
|
if source.ScaleIO != nil {
|
||||||
@ -1007,9 +1007,8 @@ var validVolumeDownwardAPIFieldPathExpressions = sets.NewString(
|
|||||||
"metadata.annotations",
|
"metadata.annotations",
|
||||||
"metadata.uid")
|
"metadata.uid")
|
||||||
|
|
||||||
func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *field.Path) field.ErrorList {
|
func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(file.Path) == 0 {
|
if len(file.Path) == 0 {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||||
}
|
}
|
||||||
@ -1020,7 +1019,11 @@ func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *fi
|
|||||||
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
|
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
|
||||||
}
|
}
|
||||||
} else if file.ResourceFieldRef != nil {
|
} else if file.ResourceFieldRef != nil {
|
||||||
allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
|
localValidContainerResourceFieldPathPrefixes := validContainerResourceFieldPathPrefixes
|
||||||
|
if opts.AllowDownwardAPIHugePages {
|
||||||
|
localValidContainerResourceFieldPathPrefixes = validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages
|
||||||
|
}
|
||||||
|
allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, &localValidContainerResourceFieldPathPrefixes, fldPath.Child("resourceFieldRef"), true)...)
|
||||||
} else {
|
} else {
|
||||||
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
|
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
|
||||||
}
|
}
|
||||||
@ -1031,7 +1034,7 @@ func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *fi
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateDownwardAPIVolumeSource(downwardAPIVolume *core.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
|
func validateDownwardAPIVolumeSource(downwardAPIVolume *core.DownwardAPIVolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
downwardAPIMode := downwardAPIVolume.DefaultMode
|
downwardAPIMode := downwardAPIVolume.DefaultMode
|
||||||
@ -1040,12 +1043,12 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *core.DownwardAPIVolumeSo
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range downwardAPIVolume.Items {
|
for _, file := range downwardAPIVolume.Items {
|
||||||
allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, fldPath)...)
|
allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, fldPath, opts)...)
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateProjectionSources(projection *core.ProjectedVolumeSource, projectionMode *int32, fldPath *field.Path) field.ErrorList {
|
func validateProjectionSources(projection *core.ProjectedVolumeSource, projectionMode *int32, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allPaths := sets.String{}
|
allPaths := sets.String{}
|
||||||
|
|
||||||
@ -1093,7 +1096,7 @@ func validateProjectionSources(projection *core.ProjectedVolumeSource, projectio
|
|||||||
if projPath := srcPath.Child("downwardAPI"); source.DownwardAPI != nil {
|
if projPath := srcPath.Child("downwardAPI"); source.DownwardAPI != nil {
|
||||||
numSources++
|
numSources++
|
||||||
for _, file := range source.DownwardAPI.Items {
|
for _, file := range source.DownwardAPI.Items {
|
||||||
allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, projPath)...)
|
allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, projPath, opts)...)
|
||||||
if len(file.Path) > 0 {
|
if len(file.Path) > 0 {
|
||||||
curPath := file.Path
|
curPath := file.Path
|
||||||
if !allPaths.Has(curPath) {
|
if !allPaths.Has(curPath) {
|
||||||
@ -1123,7 +1126,7 @@ func validateProjectionSources(projection *core.ProjectedVolumeSource, projectio
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateProjectedVolumeSource(projection *core.ProjectedVolumeSource, fldPath *field.Path) field.ErrorList {
|
func validateProjectedVolumeSource(projection *core.ProjectedVolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
projectionMode := projection.DefaultMode
|
projectionMode := projection.DefaultMode
|
||||||
@ -1131,7 +1134,7 @@ func validateProjectedVolumeSource(projection *core.ProjectedVolumeSource, fldPa
|
|||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, fileModeErrorMsg))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, fileModeErrorMsg))
|
||||||
}
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, validateProjectionSources(projection, projectionMode, fldPath)...)
|
allErrs = append(allErrs, validateProjectionSources(projection, projectionMode, fldPath, opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2138,7 +2141,7 @@ func validateContainerPorts(ports []core.ContainerPort, fldPath *field.Path) fie
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateEnv validates env vars
|
// ValidateEnv validates env vars
|
||||||
func ValidateEnv(vars []core.EnvVar, fldPath *field.Path) field.ErrorList {
|
func ValidateEnv(vars []core.EnvVar, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
for i, ev := range vars {
|
for i, ev := range vars {
|
||||||
@ -2150,7 +2153,7 @@ func ValidateEnv(vars []core.EnvVar, fldPath *field.Path) field.ErrorList {
|
|||||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg))
|
allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"))...)
|
allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"), opts)...)
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -2166,9 +2169,17 @@ var validEnvDownwardAPIFieldPathExpressions = sets.NewString(
|
|||||||
// status.podIPs is populated even if IPv6DualStack feature gate
|
// status.podIPs is populated even if IPv6DualStack feature gate
|
||||||
// is not enabled. This will work for single stack and dual stack.
|
// is not enabled. This will work for single stack and dual stack.
|
||||||
"status.podIPs")
|
"status.podIPs")
|
||||||
|
|
||||||
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "limits.ephemeral-storage", "requests.cpu", "requests.memory", "requests.ephemeral-storage")
|
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "limits.ephemeral-storage", "requests.cpu", "requests.memory", "requests.ephemeral-storage")
|
||||||
|
|
||||||
func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path) field.ErrorList {
|
// NOTE: this is only valid with DownwardAPIHugePages enabled
|
||||||
|
var validContainerResourceFieldPathPrefixes = sets.NewString()
|
||||||
|
var validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages = sets.NewString(hugepagesRequestsPrefixDownwardAPI, hugepagesLimitsPrefixDownwardAPI)
|
||||||
|
|
||||||
|
const hugepagesRequestsPrefixDownwardAPI string = `requests.hugepages-`
|
||||||
|
const hugepagesLimitsPrefixDownwardAPI string = `limits.hugepages-`
|
||||||
|
|
||||||
|
func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if ev.ValueFrom == nil {
|
if ev.ValueFrom == nil {
|
||||||
@ -2183,7 +2194,11 @@ func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path) field.ErrorLis
|
|||||||
}
|
}
|
||||||
if ev.ValueFrom.ResourceFieldRef != nil {
|
if ev.ValueFrom.ResourceFieldRef != nil {
|
||||||
numSources++
|
numSources++
|
||||||
allErrs = append(allErrs, validateContainerResourceFieldSelector(ev.ValueFrom.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), false)...)
|
localValidContainerResourceFieldPathPrefixes := validContainerResourceFieldPathPrefixes
|
||||||
|
if opts.AllowDownwardAPIHugePages {
|
||||||
|
localValidContainerResourceFieldPathPrefixes = validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages
|
||||||
|
}
|
||||||
|
allErrs = append(allErrs, validateContainerResourceFieldSelector(ev.ValueFrom.ResourceFieldRef, &validContainerResourceFieldPathExpressions, &localValidContainerResourceFieldPathPrefixes, fldPath.Child("resourceFieldRef"), false)...)
|
||||||
}
|
}
|
||||||
if ev.ValueFrom.ConfigMapKeyRef != nil {
|
if ev.ValueFrom.ConfigMapKeyRef != nil {
|
||||||
numSources++
|
numSources++
|
||||||
@ -2246,7 +2261,7 @@ func validateObjectFieldSelector(fs *core.ObjectFieldSelector, expressions *sets
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateContainerResourceFieldSelector(fs *core.ResourceFieldSelector, expressions *sets.String, fldPath *field.Path, volume bool) field.ErrorList {
|
func validateContainerResourceFieldSelector(fs *core.ResourceFieldSelector, expressions *sets.String, prefixes *sets.String, fldPath *field.Path, volume bool) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if volume && len(fs.ContainerName) == 0 {
|
if volume && len(fs.ContainerName) == 0 {
|
||||||
@ -2254,8 +2269,19 @@ func validateContainerResourceFieldSelector(fs *core.ResourceFieldSelector, expr
|
|||||||
} else if len(fs.Resource) == 0 {
|
} else if len(fs.Resource) == 0 {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
|
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
|
||||||
} else if !expressions.Has(fs.Resource) {
|
} else if !expressions.Has(fs.Resource) {
|
||||||
|
// check if the prefix is present
|
||||||
|
foundPrefix := false
|
||||||
|
if prefixes != nil {
|
||||||
|
for _, prefix := range prefixes.List() {
|
||||||
|
if strings.HasPrefix(fs.Resource, prefix) {
|
||||||
|
foundPrefix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundPrefix {
|
||||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, expressions.List()))
|
allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, expressions.List()))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...)
|
allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -2315,6 +2341,7 @@ func validateSecretEnvSource(secretSource *core.SecretEnvSource, fldPath *field.
|
|||||||
|
|
||||||
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
|
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
|
||||||
var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
||||||
|
var validContainerResourceDivisorForHugePages = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
||||||
var validContainerResourceDivisorForEphemeralStorage = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
var validContainerResourceDivisorForEphemeralStorage = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
||||||
|
|
||||||
func validateContainerResourceDivisor(rName string, divisor resource.Quantity, fldPath *field.Path) field.ErrorList {
|
func validateContainerResourceDivisor(rName string, divisor resource.Quantity, fldPath *field.Path) field.ErrorList {
|
||||||
@ -2337,6 +2364,11 @@ func validateContainerResourceDivisor(rName string, divisor resource.Quantity, f
|
|||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the local ephemeral storage resource"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the local ephemeral storage resource"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(rName, hugepagesRequestsPrefixDownwardAPI) || strings.HasPrefix(rName, hugepagesLimitsPrefixDownwardAPI) {
|
||||||
|
if !validContainerResourceDivisorForHugePages.Has(divisor.String()) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the hugepages resource"))
|
||||||
|
}
|
||||||
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2674,7 +2706,7 @@ func validatePullPolicy(policy core.PullPolicy, fldPath *field.Path) field.Error
|
|||||||
return allErrors
|
return allErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateEphemeralContainers(ephemeralContainers []core.EphemeralContainer, containers, initContainers []core.Container, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
|
func validateEphemeralContainers(ephemeralContainers []core.EphemeralContainer, containers, initContainers []core.Container, volumes map[string]core.VolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(ephemeralContainers) == 0 {
|
if len(ephemeralContainers) == 0 {
|
||||||
@ -2706,7 +2738,7 @@ func validateEphemeralContainers(ephemeralContainers []core.EphemeralContainer,
|
|||||||
// of ephemeralContainers[0].spec.name)
|
// of ephemeralContainers[0].spec.name)
|
||||||
// TODO(verb): factor a validateContainer() out of validateContainers() to be used here
|
// TODO(verb): factor a validateContainer() out of validateContainers() to be used here
|
||||||
c := core.Container(ec.EphemeralContainerCommon)
|
c := core.Container(ec.EphemeralContainerCommon)
|
||||||
allErrs = append(allErrs, validateContainers([]core.Container{c}, false, volumes, idxPath)...)
|
allErrs = append(allErrs, validateContainers([]core.Container{c}, false, volumes, idxPath, opts)...)
|
||||||
// EphemeralContainers don't require the backwards-compatibility distinction between pod/podTemplate validation
|
// EphemeralContainers don't require the backwards-compatibility distinction between pod/podTemplate validation
|
||||||
allErrs = append(allErrs, validateContainersOnlyForPod([]core.Container{c}, idxPath)...)
|
allErrs = append(allErrs, validateContainersOnlyForPod([]core.Container{c}, idxPath)...)
|
||||||
|
|
||||||
@ -2748,10 +2780,10 @@ func validateFieldAllowList(value interface{}, allowedFields map[string]bool, er
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInitContainers(containers, otherContainers []core.Container, deviceVolumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
|
func validateInitContainers(containers, otherContainers []core.Container, deviceVolumes map[string]core.VolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
var allErrs field.ErrorList
|
var allErrs field.ErrorList
|
||||||
if len(containers) > 0 {
|
if len(containers) > 0 {
|
||||||
allErrs = append(allErrs, validateContainers(containers, true, deviceVolumes, fldPath)...)
|
allErrs = append(allErrs, validateContainers(containers, true, deviceVolumes, fldPath, opts)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
allNames := sets.String{}
|
allNames := sets.String{}
|
||||||
@ -2782,7 +2814,7 @@ func validateInitContainers(containers, otherContainers []core.Container, device
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateContainers(containers []core.Container, isInitContainers bool, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
|
func validateContainers(containers []core.Container, isInitContainers bool, volumes map[string]core.VolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(containers) == 0 {
|
if len(containers) == 0 {
|
||||||
@ -2836,7 +2868,7 @@ func validateContainers(containers []core.Container, isInitContainers bool, volu
|
|||||||
|
|
||||||
allErrs = append(allErrs, validateProbe(ctr.ReadinessProbe, idxPath.Child("readinessProbe"))...)
|
allErrs = append(allErrs, validateProbe(ctr.ReadinessProbe, idxPath.Child("readinessProbe"))...)
|
||||||
allErrs = append(allErrs, validateContainerPorts(ctr.Ports, idxPath.Child("ports"))...)
|
allErrs = append(allErrs, validateContainerPorts(ctr.Ports, idxPath.Child("ports"))...)
|
||||||
allErrs = append(allErrs, ValidateEnv(ctr.Env, idxPath.Child("env"))...)
|
allErrs = append(allErrs, ValidateEnv(ctr.Env, idxPath.Child("env"), opts)...)
|
||||||
allErrs = append(allErrs, ValidateEnvFrom(ctr.EnvFrom, idxPath.Child("envFrom"))...)
|
allErrs = append(allErrs, ValidateEnvFrom(ctr.EnvFrom, idxPath.Child("envFrom"))...)
|
||||||
allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, &ctr, idxPath.Child("volumeMounts"))...)
|
allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, &ctr, idxPath.Child("volumeMounts"))...)
|
||||||
allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, idxPath.Child("volumeDevices"))...)
|
allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, idxPath.Child("volumeDevices"))...)
|
||||||
@ -3133,28 +3165,6 @@ func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path) fie
|
|||||||
return allErrors
|
return allErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func toResourceNames(resources core.ResourceList) []core.ResourceName {
|
|
||||||
result := []core.ResourceName{}
|
|
||||||
for resourceName := range resources {
|
|
||||||
result = append(result, resourceName)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func toSet(resourceNames []core.ResourceName) sets.String {
|
|
||||||
result := sets.NewString()
|
|
||||||
for _, resourceName := range resourceNames {
|
|
||||||
result.Insert(string(resourceName))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func toContainerResourcesSet(ctr *core.Container) sets.String {
|
|
||||||
resourceNames := toResourceNames(ctr.Resources.Requests)
|
|
||||||
resourceNames = append(resourceNames, toResourceNames(ctr.Resources.Limits)...)
|
|
||||||
return toSet(resourceNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateContainersOnlyForPod does additional validation for containers on a pod versus a pod template
|
// validateContainersOnlyForPod does additional validation for containers on a pod versus a pod template
|
||||||
// it only does additive validation of fields not covered in validateContainers
|
// it only does additive validation of fields not covered in validateContainers
|
||||||
func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Path) field.ErrorList {
|
func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Path) field.ErrorList {
|
||||||
@ -3172,21 +3182,21 @@ func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Pa
|
|||||||
type PodValidationOptions struct {
|
type PodValidationOptions struct {
|
||||||
// Allow pod spec to have more than one huge page resource (with different sizes)
|
// Allow pod spec to have more than one huge page resource (with different sizes)
|
||||||
AllowMultipleHugePageResources bool
|
AllowMultipleHugePageResources bool
|
||||||
|
// Allow pod spec to use hugepages in downward API
|
||||||
|
AllowDownwardAPIHugePages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePodSingleHugePageResources checks if there are multiple huge
|
// ValidatePodSingleHugePageResources checks if there are multiple huge
|
||||||
// pages resources in the pod object.
|
// pages resources in the pod object.
|
||||||
func ValidatePodSingleHugePageResources(pod *core.Pod, specPath *field.Path) field.ErrorList {
|
func ValidatePodSingleHugePageResources(pod *core.Pod, specPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
resourceSet := helper.ToPodResourcesSet(&pod.Spec)
|
||||||
hugePageResources := sets.NewString()
|
hugePageResources := sets.NewString()
|
||||||
for i := range pod.Spec.Containers {
|
|
||||||
resourceSet := toContainerResourcesSet(&pod.Spec.Containers[i])
|
|
||||||
for resourceStr := range resourceSet {
|
for resourceStr := range resourceSet {
|
||||||
if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
|
if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
|
||||||
hugePageResources.Insert(resourceStr)
|
hugePageResources.Insert(resourceStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if len(hugePageResources) > 1 {
|
if len(hugePageResources) > 1 {
|
||||||
allErrs = append(allErrs, field.Invalid(specPath, hugePageResources.List(), "must use a single hugepage size in a pod spec"))
|
allErrs = append(allErrs, field.Invalid(specPath, hugePageResources.List(), "must use a single hugepage size in a pod spec"))
|
||||||
}
|
}
|
||||||
@ -3199,7 +3209,7 @@ func validatePodMetadataAndSpec(pod *core.Pod, opts PodValidationOptions) field.
|
|||||||
fldPath := field.NewPath("metadata")
|
fldPath := field.NewPath("metadata")
|
||||||
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
|
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
|
||||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
|
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
|
||||||
allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, field.NewPath("spec"), opts)...)
|
||||||
|
|
||||||
// we do additional validation only pertinent for pods and not pod templates
|
// we do additional validation only pertinent for pods and not pod templates
|
||||||
// this was done to preserve backwards compatibility
|
// this was done to preserve backwards compatibility
|
||||||
@ -3280,14 +3290,14 @@ func validatePodIPs(pod *core.Pod) field.ErrorList {
|
|||||||
// tricks.
|
// tricks.
|
||||||
// The pod metadata is needed to validate generic ephemeral volumes. It is optional
|
// The pod metadata is needed to validate generic ephemeral volumes. It is optional
|
||||||
// and should be left empty unless the spec is from a real pod object.
|
// and should be left empty unless the spec is from a real pod object.
|
||||||
func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
|
func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
vols, vErrs := ValidateVolumes(spec.Volumes, podMeta, fldPath.Child("volumes"))
|
vols, vErrs := ValidateVolumes(spec.Volumes, podMeta, fldPath.Child("volumes"), opts)
|
||||||
allErrs = append(allErrs, vErrs...)
|
allErrs = append(allErrs, vErrs...)
|
||||||
allErrs = append(allErrs, validateContainers(spec.Containers, false, vols, fldPath.Child("containers"))...)
|
allErrs = append(allErrs, validateContainers(spec.Containers, false, vols, fldPath.Child("containers"), opts)...)
|
||||||
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, fldPath.Child("initContainers"))...)
|
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, fldPath.Child("initContainers"), opts)...)
|
||||||
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, fldPath.Child("ephemeralContainers"))...)
|
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, fldPath.Child("ephemeralContainers"), opts)...)
|
||||||
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
|
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
|
||||||
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
|
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
|
||||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
|
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
|
||||||
@ -4077,7 +4087,7 @@ func validatePodConditions(conditions []core.PodCondition, fldPath *field.Path)
|
|||||||
|
|
||||||
// ValidatePodEphemeralContainersUpdate tests that a user update to EphemeralContainers is valid.
|
// ValidatePodEphemeralContainersUpdate tests that a user update to EphemeralContainers is valid.
|
||||||
// newPod and oldPod must only differ in their EphemeralContainers.
|
// newPod and oldPod must only differ in their EphemeralContainers.
|
||||||
func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod) field.ErrorList {
|
func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList {
|
||||||
spec := newPod.Spec
|
spec := newPod.Spec
|
||||||
specPath := field.NewPath("spec").Child("ephemeralContainers")
|
specPath := field.NewPath("spec").Child("ephemeralContainers")
|
||||||
|
|
||||||
@ -4085,7 +4095,7 @@ func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod) field.ErrorL
|
|||||||
for _, vol := range spec.Volumes {
|
for _, vol := range spec.Volumes {
|
||||||
vols[vol.Name] = vol.VolumeSource
|
vols[vol.Name] = vol.VolumeSource
|
||||||
}
|
}
|
||||||
allErrs := validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, specPath)
|
allErrs := validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, specPath, opts)
|
||||||
|
|
||||||
// Existing EphemeralContainers may not be changed. Order isn't preserved by patch, so check each individually.
|
// Existing EphemeralContainers may not be changed. Order isn't preserved by patch, so check each individually.
|
||||||
newContainerIndex := make(map[string]*core.EphemeralContainer)
|
newContainerIndex := make(map[string]*core.EphemeralContainer)
|
||||||
@ -4121,17 +4131,17 @@ func ValidatePodBinding(binding *core.Binding) field.ErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePodTemplate tests if required fields in the pod template are set.
|
// ValidatePodTemplate tests if required fields in the pod template are set.
|
||||||
func ValidatePodTemplate(pod *core.PodTemplate) field.ErrorList {
|
func ValidatePodTemplate(pod *core.PodTemplate, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, field.NewPath("metadata"))
|
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpec(&pod.Template, field.NewPath("template"))...)
|
allErrs = append(allErrs, ValidatePodTemplateSpec(&pod.Template, field.NewPath("template"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
|
// ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
|
||||||
// that cannot be changed.
|
// that cannot be changed.
|
||||||
func ValidatePodTemplateUpdate(newPod, oldPod *core.PodTemplate) field.ErrorList {
|
func ValidatePodTemplateUpdate(newPod, oldPod *core.PodTemplate, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, field.NewPath("metadata"))
|
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpec(&newPod.Template, field.NewPath("template"))...)
|
allErrs = append(allErrs, ValidatePodTemplateSpec(&newPod.Template, field.NewPath("template"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4490,16 +4500,16 @@ func ValidateServiceStatusUpdate(service, oldService *core.Service) field.ErrorL
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateReplicationController tests if required fields in the replication controller are set.
|
// ValidateReplicationController tests if required fields in the replication controller are set.
|
||||||
func ValidateReplicationController(controller *core.ReplicationController) field.ErrorList {
|
func ValidateReplicationController(controller *core.ReplicationController, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName, field.NewPath("metadata"))
|
allErrs := ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
|
// ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
|
||||||
func ValidateReplicationControllerUpdate(controller, oldController *core.ReplicationController) field.ErrorList {
|
func ValidateReplicationControllerUpdate(controller, oldController *core.ReplicationController, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata"))
|
allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, field.NewPath("spec"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4544,7 +4554,7 @@ func ValidateNonEmptySelector(selectorMap map[string]string, fldPath *field.Path
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validates the given template and ensures that it is in accordance with the desired selector and replicas.
|
// Validates the given template and ensures that it is in accordance with the desired selector and replicas.
|
||||||
func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap map[string]string, replicas int32, fldPath *field.Path) field.ErrorList {
|
func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap map[string]string, replicas int32, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
if template == nil {
|
if template == nil {
|
||||||
allErrs = append(allErrs, field.Required(fldPath, ""))
|
allErrs = append(allErrs, field.Required(fldPath, ""))
|
||||||
@ -4557,7 +4567,7 @@ func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap ma
|
|||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpec(template, fldPath)...)
|
allErrs = append(allErrs, ValidatePodTemplateSpec(template, fldPath, opts)...)
|
||||||
if replicas > 1 {
|
if replicas > 1 {
|
||||||
allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...)
|
allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...)
|
||||||
}
|
}
|
||||||
@ -4573,22 +4583,22 @@ func ValidatePodTemplateSpecForRC(template *core.PodTemplateSpec, selectorMap ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
|
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
|
||||||
func ValidateReplicationControllerSpec(spec *core.ReplicationControllerSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateReplicationControllerSpec(spec *core.ReplicationControllerSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
|
||||||
allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
|
allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
|
||||||
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
|
||||||
allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"))...)
|
allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"), opts)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePodTemplateSpec validates the spec of a pod template
|
// ValidatePodTemplateSpec validates the spec of a pod template
|
||||||
func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path) field.ErrorList {
|
func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...)
|
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...)
|
||||||
allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
|
allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
|
||||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"))...)
|
allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"))...)
|
||||||
allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, nil, fldPath.Child("spec"))...)
|
allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, nil, fldPath.Child("spec"), opts)...)
|
||||||
allErrs = append(allErrs, validateSeccompAnnotationsAndFields(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
|
allErrs = append(allErrs, validateSeccompAnnotationsAndFields(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
|
||||||
|
|
||||||
if len(spec.Spec.EphemeralContainers) > 0 {
|
if len(spec.Spec.EphemeralContainers) > 0 {
|
||||||
|
@ -1307,7 +1307,8 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, errs = ValidateVolumes(volumes, nil, field.NewPath(""))
|
opts := PodValidationOptions{}
|
||||||
|
_, errs = ValidateVolumes(volumes, nil, field.NewPath(""), opts)
|
||||||
} else {
|
} else {
|
||||||
errs = ValidatePersistentVolumeClaim(scenario.claim)
|
errs = ValidatePersistentVolumeClaim(scenario.claim)
|
||||||
}
|
}
|
||||||
@ -2199,6 +2200,7 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
vol core.Volume
|
vol core.Volume
|
||||||
errs []verr
|
errs []verr
|
||||||
|
opts PodValidationOptions
|
||||||
}{
|
}{
|
||||||
// EmptyDir and basic volume names
|
// EmptyDir and basic volume names
|
||||||
{
|
{
|
||||||
@ -3294,6 +3296,79 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "hugepages-downwardAPI-enabled",
|
||||||
|
vol: core.Volume{
|
||||||
|
Name: "downwardapi",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
DownwardAPI: &core.DownwardAPIVolumeSource{
|
||||||
|
Items: []core.DownwardAPIVolumeFile{
|
||||||
|
{
|
||||||
|
Path: "hugepages_request",
|
||||||
|
ResourceFieldRef: &core.ResourceFieldSelector{
|
||||||
|
ContainerName: "test-container",
|
||||||
|
Resource: "requests.hugepages-2Mi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "hugepages_limit",
|
||||||
|
ResourceFieldRef: &core.ResourceFieldSelector{
|
||||||
|
ContainerName: "test-container",
|
||||||
|
Resource: "limits.hugepages-2Mi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opts: PodValidationOptions{AllowDownwardAPIHugePages: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hugepages-downwardAPI-requests-disabled",
|
||||||
|
vol: core.Volume{
|
||||||
|
Name: "downwardapi",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
DownwardAPI: &core.DownwardAPIVolumeSource{
|
||||||
|
Items: []core.DownwardAPIVolumeFile{
|
||||||
|
{
|
||||||
|
Path: "hugepages_request",
|
||||||
|
ResourceFieldRef: &core.ResourceFieldSelector{
|
||||||
|
ContainerName: "test-container",
|
||||||
|
Resource: "requests.hugepages-2Mi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errs: []verr{{
|
||||||
|
etype: field.ErrorTypeNotSupported,
|
||||||
|
field: "downwardAPI.resourceFieldRef.resource",
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hugepages-downwardAPI-limits-disabled",
|
||||||
|
vol: core.Volume{
|
||||||
|
Name: "downwardapi",
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
DownwardAPI: &core.DownwardAPIVolumeSource{
|
||||||
|
Items: []core.DownwardAPIVolumeFile{
|
||||||
|
{
|
||||||
|
Path: "hugepages_limit",
|
||||||
|
ResourceFieldRef: &core.ResourceFieldSelector{
|
||||||
|
ContainerName: "test-container",
|
||||||
|
Resource: "limits.hugepages-2Mi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errs: []verr{{
|
||||||
|
etype: field.ErrorTypeNotSupported,
|
||||||
|
field: "downwardAPI.resourceFieldRef.resource",
|
||||||
|
}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "downapi valid defaultMode",
|
name: "downapi valid defaultMode",
|
||||||
vol: core.Volume{
|
vol: core.Volume{
|
||||||
@ -3990,7 +4065,7 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
names, errs := ValidateVolumes([]core.Volume{tc.vol}, nil, field.NewPath("field"))
|
names, errs := ValidateVolumes([]core.Volume{tc.vol}, nil, field.NewPath("field"), tc.opts)
|
||||||
if len(errs) != len(tc.errs) {
|
if len(errs) != len(tc.errs) {
|
||||||
t.Fatalf("unexpected error(s): got %d, want %d: %v", len(tc.errs), len(errs), errs)
|
t.Fatalf("unexpected error(s): got %d, want %d: %v", len(tc.errs), len(errs), errs)
|
||||||
}
|
}
|
||||||
@ -4016,7 +4091,7 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
{Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
{Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
||||||
{Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
{Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
||||||
}
|
}
|
||||||
_, errs := ValidateVolumes(dupsCase, nil, field.NewPath("field"))
|
_, errs := ValidateVolumes(dupsCase, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected error")
|
t.Errorf("expected error")
|
||||||
} else if len(errs) != 1 {
|
} else if len(errs) != 1 {
|
||||||
@ -4029,7 +4104,7 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
hugePagesCase := core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{Medium: core.StorageMediumHugePages}}
|
hugePagesCase := core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{Medium: core.StorageMediumHugePages}}
|
||||||
|
|
||||||
// Enable HugePages
|
// Enable HugePages
|
||||||
if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working", nil); len(errs) != 0 {
|
if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working", nil, PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("Unexpected error when HugePages feature is enabled.")
|
t.Errorf("Unexpected error when HugePages feature is enabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4208,7 +4283,7 @@ func TestHugePagesIsolation(t *testing.T) {
|
|||||||
for tcName, tc := range testCases {
|
for tcName, tc := range testCases {
|
||||||
t.Run(tcName, func(t *testing.T) {
|
t.Run(tcName, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, tc.enableHugePageStorageMediumSize)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, tc.enableHugePageStorageMediumSize)()
|
||||||
errs := ValidatePodCreate(tc.pod, PodValidationOptions{tc.enableHugePageStorageMediumSize})
|
errs := ValidatePodCreate(tc.pod, PodValidationOptions{AllowMultipleHugePageResources: tc.enableHugePageStorageMediumSize})
|
||||||
if tc.expectError && len(errs) == 0 {
|
if tc.expectError && len(errs) == 0 {
|
||||||
t.Errorf("Unexpected success")
|
t.Errorf("Unexpected success")
|
||||||
}
|
}
|
||||||
@ -4359,7 +4434,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol", nil); len(errs) != 0 {
|
if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol", nil, PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4529,12 +4604,55 @@ func TestLocalStorageEnvWithFeatureGate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field")); len(errs) != 0 {
|
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success, got: %v", errs)
|
t.Errorf("expected success, got: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHugePagesEnv(t *testing.T) {
|
||||||
|
testCases := []core.EnvVar{
|
||||||
|
{
|
||||||
|
Name: "hugepages-limits",
|
||||||
|
ValueFrom: &core.EnvVarSource{
|
||||||
|
ResourceFieldRef: &core.ResourceFieldSelector{
|
||||||
|
ContainerName: "test-container",
|
||||||
|
Resource: "limits.hugepages-2Mi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "hugepages-requests",
|
||||||
|
ValueFrom: &core.EnvVarSource{
|
||||||
|
ResourceFieldRef: &core.ResourceFieldSelector{
|
||||||
|
ContainerName: "test-container",
|
||||||
|
Resource: "requests.hugepages-2Mi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// enable gate
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.Name, func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DownwardAPIHugePages, true)()
|
||||||
|
opts := PodValidationOptions{AllowDownwardAPIHugePages: true}
|
||||||
|
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field"), opts); len(errs) != 0 {
|
||||||
|
t.Errorf("expected success, got: %v", errs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// disable gate
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.Name, func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DownwardAPIHugePages, false)()
|
||||||
|
opts := PodValidationOptions{AllowDownwardAPIHugePages: false}
|
||||||
|
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field"), opts); len(errs) == 0 {
|
||||||
|
t.Errorf("expected failure")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateEnv(t *testing.T) {
|
func TestValidateEnv(t *testing.T) {
|
||||||
successCase := []core.EnvVar{
|
successCase := []core.EnvVar{
|
||||||
{Name: "abc", Value: "value"},
|
{Name: "abc", Value: "value"},
|
||||||
@ -4656,7 +4774,7 @@ func TestValidateEnv(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if errs := ValidateEnv(successCase, field.NewPath("field")); len(errs) != 0 {
|
if errs := ValidateEnv(successCase, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success, got: %v", errs)
|
t.Errorf("expected success, got: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4920,7 +5038,7 @@ func TestValidateEnv(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range errorCases {
|
for _, tc := range errorCases {
|
||||||
if errs := ValidateEnv(tc.envs, field.NewPath("field")); len(errs) == 0 {
|
if errs := ValidateEnv(tc.envs, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", tc.name)
|
t.Errorf("expected failure for %s", tc.name)
|
||||||
} else {
|
} else {
|
||||||
for i := range errs {
|
for i := range errs {
|
||||||
@ -5101,7 +5219,7 @@ func TestValidateVolumeMounts(t *testing.T) {
|
|||||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
}
|
}
|
||||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(v1err) > 0 {
|
if len(v1err) > 0 {
|
||||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||||
return
|
return
|
||||||
@ -5164,7 +5282,7 @@ func TestValidateDisabledSubpath(t *testing.T) {
|
|||||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
}
|
}
|
||||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(v1err) > 0 {
|
if len(v1err) > 0 {
|
||||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||||
return
|
return
|
||||||
@ -5226,7 +5344,7 @@ func TestValidateSubpathMutuallyExclusive(t *testing.T) {
|
|||||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
}
|
}
|
||||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(v1err) > 0 {
|
if len(v1err) > 0 {
|
||||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||||
return
|
return
|
||||||
@ -5307,7 +5425,7 @@ func TestValidateDisabledSubpathExpr(t *testing.T) {
|
|||||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
}
|
}
|
||||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(v1err) > 0 {
|
if len(v1err) > 0 {
|
||||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||||
return
|
return
|
||||||
@ -5501,7 +5619,7 @@ func TestValidateMountPropagation(t *testing.T) {
|
|||||||
volumes := []core.Volume{
|
volumes := []core.Volume{
|
||||||
{Name: "foo", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
{Name: "foo", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
}
|
}
|
||||||
vols2, v2err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
vols2, v2err := ValidateVolumes(volumes, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(v2err) > 0 {
|
if len(v2err) > 0 {
|
||||||
t.Errorf("Invalid test volume - expected success %v", v2err)
|
t.Errorf("Invalid test volume - expected success %v", v2err)
|
||||||
return
|
return
|
||||||
@ -5524,7 +5642,7 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
|
|||||||
{Name: "def", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
{Name: "def", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"), PodValidationOptions{})
|
||||||
if len(v1err) > 0 {
|
if len(v1err) > 0 {
|
||||||
t.Errorf("Invalid test volumes - expected success %v", v1err)
|
t.Errorf("Invalid test volumes - expected success %v", v1err)
|
||||||
return
|
return
|
||||||
@ -5737,7 +5855,7 @@ func TestValidateEphemeralContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
if errs := validateEphemeralContainers(ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers")); len(errs) != 0 {
|
if errs := validateEphemeralContainers(ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers"), PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success for '%s' but got errors: %v", title, errs)
|
t.Errorf("expected success for '%s' but got errors: %v", title, errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5901,7 +6019,7 @@ func TestValidateEphemeralContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
errs := validateEphemeralContainers(tc.ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers"))
|
errs := validateEphemeralContainers(tc.ephemeralContainers, containers, initContainers, vols, field.NewPath("ephemeralContainers"), PodValidationOptions{})
|
||||||
|
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("for test %q, expected error but received none", tc.title)
|
t.Errorf("for test %q, expected error but received none", tc.title)
|
||||||
@ -6070,7 +6188,7 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)},
|
{Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)},
|
||||||
}
|
}
|
||||||
if errs := validateContainers(successCase, false, volumeDevices, field.NewPath("field")); len(errs) != 0 {
|
if errs := validateContainers(successCase, false, volumeDevices, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6310,7 +6428,7 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
if errs := validateContainers(v, false, volumeDevices, field.NewPath("field")); len(errs) == 0 {
|
if errs := validateContainers(v, false, volumeDevices, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6344,7 +6462,7 @@ func TestValidateInitContainers(t *testing.T) {
|
|||||||
TerminationMessagePolicy: "File",
|
TerminationMessagePolicy: "File",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if errs := validateContainers(successCase, true, volumeDevices, field.NewPath("field")); len(errs) != 0 {
|
if errs := validateContainers(successCase, true, volumeDevices, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6370,7 +6488,7 @@ func TestValidateInitContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
if errs := validateContainers(v, true, volumeDevices, field.NewPath("field")); len(errs) == 0 {
|
if errs := validateContainers(v, true, volumeDevices, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6881,7 +6999,7 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field")); len(errs) != 0 {
|
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -7085,7 +7203,7 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range failureCases {
|
for k, v := range failureCases {
|
||||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field")); len(errs) == 0 {
|
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %q", k)
|
t.Errorf("expected failure for %q", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9875,7 +9993,7 @@ func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
new := core.Pod{Spec: core.PodSpec{EphemeralContainers: test.new}}
|
new := core.Pod{Spec: core.PodSpec{EphemeralContainers: test.new}}
|
||||||
old := core.Pod{Spec: core.PodSpec{EphemeralContainers: test.old}}
|
old := core.Pod{Spec: core.PodSpec{EphemeralContainers: test.old}}
|
||||||
errs := ValidatePodEphemeralContainersUpdate(&new, &old)
|
errs := ValidatePodEphemeralContainersUpdate(&new, &old, PodValidationOptions{})
|
||||||
if test.err == "" {
|
if test.err == "" {
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
|
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
|
||||||
@ -11508,7 +11626,7 @@ func TestValidateReplicationControllerUpdate(t *testing.T) {
|
|||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||||
if errs := ValidateReplicationControllerUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
|
if errs := ValidateReplicationControllerUpdate(&successCase.update, &successCase.old, PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11583,7 +11701,7 @@ func TestValidateReplicationControllerUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for testName, errorCase := range errorCases {
|
for testName, errorCase := range errorCases {
|
||||||
if errs := ValidateReplicationControllerUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
|
if errs := ValidateReplicationControllerUpdate(&errorCase.update, &errorCase.old, PodValidationOptions{}); len(errs) == 0 {
|
||||||
t.Errorf("expected failure: %s", testName)
|
t.Errorf("expected failure: %s", testName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11653,7 +11771,7 @@ func TestValidateReplicationController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := ValidateReplicationController(&successCase); len(errs) != 0 {
|
if errs := ValidateReplicationController(&successCase, PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11803,7 +11921,7 @@ func TestValidateReplicationController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateReplicationController(&v)
|
errs := ValidateReplicationController(&v, PodValidationOptions{})
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
}
|
}
|
||||||
@ -16919,7 +17037,7 @@ func TestValidatePodTemplateSpecSeccomp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
err := ValidatePodTemplateSpec(test.spec, rootFld)
|
err := ValidatePodTemplateSpec(test.spec, rootFld, PodValidationOptions{})
|
||||||
asserttestify.Equal(t, test.expectedErr, err, "TestCase[%d]: %s", i, test.description)
|
asserttestify.Equal(t, test.expectedErr, err, "TestCase[%d]: %s", i, test.description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,6 +588,12 @@ const (
|
|||||||
// medium: HugePages-1Gi
|
// medium: HugePages-1Gi
|
||||||
HugePageStorageMediumSize featuregate.Feature = "HugePageStorageMediumSize"
|
HugePageStorageMediumSize featuregate.Feature = "HugePageStorageMediumSize"
|
||||||
|
|
||||||
|
// owner: @derekwaynecarr
|
||||||
|
// alpha: v1.20
|
||||||
|
//
|
||||||
|
// Enables usage of hugepages-<size> in downward API.
|
||||||
|
DownwardAPIHugePages featuregate.Feature = "DownwardAPIHugePages"
|
||||||
|
|
||||||
// owner: @freehan
|
// owner: @freehan
|
||||||
// GA: v1.18
|
// GA: v1.18
|
||||||
//
|
//
|
||||||
@ -757,6 +763,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
ServiceAppProtocol: {Default: true, PreRelease: featuregate.Beta},
|
ServiceAppProtocol: {Default: true, PreRelease: featuregate.Beta},
|
||||||
ImmutableEphemeralVolumes: {Default: true, PreRelease: featuregate.Beta},
|
ImmutableEphemeralVolumes: {Default: true, PreRelease: featuregate.Beta},
|
||||||
HugePageStorageMediumSize: {Default: true, PreRelease: featuregate.Beta},
|
HugePageStorageMediumSize: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
DownwardAPIHugePages: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
ExternalPolicyForExternalIP: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.22
|
ExternalPolicyForExternalIP: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.22
|
||||||
AnyVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha},
|
AnyVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
DefaultPodTopologySpread: {Default: true, PreRelease: featuregate.Beta},
|
DefaultPodTopologySpread: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
@ -57,7 +57,7 @@ var podResources = []corev1.ResourceName{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// podResourcePrefixes are the set of prefixes for resources (Hugepages, and other
|
// podResourcePrefixes are the set of prefixes for resources (Hugepages, and other
|
||||||
// potential extended reources with specific prefix) managed by quota associated with pods.
|
// potential extended resources with specific prefix) managed by quota associated with pods.
|
||||||
var podResourcePrefixes = []string{
|
var podResourcePrefixes = []string{
|
||||||
corev1.ResourceHugePagesPrefix,
|
corev1.ResourceHugePagesPrefix,
|
||||||
corev1.ResourceRequestsHugePagesPrefix,
|
corev1.ResourceRequestsHugePagesPrefix,
|
||||||
|
@ -115,7 +115,8 @@ func (daemonSetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
|
|||||||
// Validate validates a new daemon set.
|
// Validate validates a new daemon set.
|
||||||
func (daemonSetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (daemonSetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
daemonSet := obj.(*apps.DaemonSet)
|
daemonSet := obj.(*apps.DaemonSet)
|
||||||
return validation.ValidateDaemonSet(daemonSet)
|
opts := pod.GetValidationOptionsFromPodTemplate(&daemonSet.Spec.Template, nil)
|
||||||
|
return validation.ValidateDaemonSet(daemonSet, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize normalizes the object after validation.
|
// Canonicalize normalizes the object after validation.
|
||||||
@ -132,8 +133,10 @@ func (daemonSetStrategy) AllowCreateOnUpdate() bool {
|
|||||||
func (daemonSetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (daemonSetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
newDaemonSet := obj.(*apps.DaemonSet)
|
newDaemonSet := obj.(*apps.DaemonSet)
|
||||||
oldDaemonSet := old.(*apps.DaemonSet)
|
oldDaemonSet := old.(*apps.DaemonSet)
|
||||||
allErrs := validation.ValidateDaemonSet(obj.(*apps.DaemonSet))
|
|
||||||
allErrs = append(allErrs, validation.ValidateDaemonSetUpdate(newDaemonSet, oldDaemonSet)...)
|
opts := pod.GetValidationOptionsFromPodTemplate(&newDaemonSet.Spec.Template, &oldDaemonSet.Spec.Template)
|
||||||
|
allErrs := validation.ValidateDaemonSet(obj.(*apps.DaemonSet), opts)
|
||||||
|
allErrs = append(allErrs, validation.ValidateDaemonSetUpdate(newDaemonSet, oldDaemonSet, opts)...)
|
||||||
|
|
||||||
// Update is not allowed to set Spec.Selector for apps/v1 and apps/v1beta2 (allowed for extensions/v1beta1).
|
// Update is not allowed to set Spec.Selector for apps/v1 and apps/v1beta2 (allowed for extensions/v1beta1).
|
||||||
// If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
|
// If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
|
||||||
|
@ -79,7 +79,8 @@ func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Obje
|
|||||||
// Validate validates a new deployment.
|
// Validate validates a new deployment.
|
||||||
func (deploymentStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (deploymentStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
deployment := obj.(*apps.Deployment)
|
deployment := obj.(*apps.Deployment)
|
||||||
return validation.ValidateDeployment(deployment)
|
opts := pod.GetValidationOptionsFromPodTemplate(&deployment.Spec.Template, nil)
|
||||||
|
return validation.ValidateDeployment(deployment, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize normalizes the object after validation.
|
// Canonicalize normalizes the object after validation.
|
||||||
@ -112,7 +113,9 @@ func (deploymentStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime
|
|||||||
func (deploymentStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (deploymentStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
newDeployment := obj.(*apps.Deployment)
|
newDeployment := obj.(*apps.Deployment)
|
||||||
oldDeployment := old.(*apps.Deployment)
|
oldDeployment := old.(*apps.Deployment)
|
||||||
allErrs := validation.ValidateDeploymentUpdate(newDeployment, oldDeployment)
|
|
||||||
|
opts := pod.GetValidationOptionsFromPodTemplate(&newDeployment.Spec.Template, &oldDeployment.Spec.Template)
|
||||||
|
allErrs := validation.ValidateDeploymentUpdate(newDeployment, oldDeployment, opts)
|
||||||
|
|
||||||
// Update is not allowed to set Spec.Selector for all groups/versions except extensions/v1beta1.
|
// Update is not allowed to set Spec.Selector for all groups/versions except extensions/v1beta1.
|
||||||
// If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
|
// If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
|
||||||
|
@ -108,7 +108,8 @@ func (rsStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object)
|
|||||||
// Validate validates a new ReplicaSet.
|
// Validate validates a new ReplicaSet.
|
||||||
func (rsStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (rsStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
rs := obj.(*apps.ReplicaSet)
|
rs := obj.(*apps.ReplicaSet)
|
||||||
return validation.ValidateReplicaSet(rs)
|
opts := pod.GetValidationOptionsFromPodTemplate(&rs.Spec.Template, nil)
|
||||||
|
return validation.ValidateReplicaSet(rs, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize normalizes the object after validation.
|
// Canonicalize normalizes the object after validation.
|
||||||
@ -125,8 +126,10 @@ func (rsStrategy) AllowCreateOnUpdate() bool {
|
|||||||
func (rsStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (rsStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
newReplicaSet := obj.(*apps.ReplicaSet)
|
newReplicaSet := obj.(*apps.ReplicaSet)
|
||||||
oldReplicaSet := old.(*apps.ReplicaSet)
|
oldReplicaSet := old.(*apps.ReplicaSet)
|
||||||
allErrs := validation.ValidateReplicaSet(obj.(*apps.ReplicaSet))
|
|
||||||
allErrs = append(allErrs, validation.ValidateReplicaSetUpdate(newReplicaSet, oldReplicaSet)...)
|
opts := pod.GetValidationOptionsFromPodTemplate(&newReplicaSet.Spec.Template, &oldReplicaSet.Spec.Template)
|
||||||
|
allErrs := validation.ValidateReplicaSet(obj.(*apps.ReplicaSet), opts)
|
||||||
|
allErrs = append(allErrs, validation.ValidateReplicaSetUpdate(newReplicaSet, oldReplicaSet, opts)...)
|
||||||
|
|
||||||
// Update is not allowed to set Spec.Selector for all groups/versions except extensions/v1beta1.
|
// Update is not allowed to set Spec.Selector for all groups/versions except extensions/v1beta1.
|
||||||
// If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
|
// If RequestInfo is nil, it is better to revert to old behavior (i.e. allow update to set Spec.Selector)
|
||||||
|
@ -83,7 +83,8 @@ func (cronJobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
|
|||||||
// Validate validates a new scheduled job.
|
// Validate validates a new scheduled job.
|
||||||
func (cronJobStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (cronJobStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
cronJob := obj.(*batch.CronJob)
|
cronJob := obj.(*batch.CronJob)
|
||||||
return validation.ValidateCronJob(cronJob)
|
opts := pod.GetValidationOptionsFromPodTemplate(&cronJob.Spec.JobTemplate.Spec.Template, nil)
|
||||||
|
return validation.ValidateCronJob(cronJob, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize normalizes the object after validation.
|
// Canonicalize normalizes the object after validation.
|
||||||
@ -103,7 +104,9 @@ func (cronJobStrategy) AllowCreateOnUpdate() bool {
|
|||||||
func (cronJobStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (cronJobStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
newCronJob := obj.(*batch.CronJob)
|
newCronJob := obj.(*batch.CronJob)
|
||||||
oldCronJob := old.(*batch.CronJob)
|
oldCronJob := old.(*batch.CronJob)
|
||||||
return validation.ValidateCronJobUpdate(newCronJob, oldCronJob)
|
|
||||||
|
opts := pod.GetValidationOptionsFromPodTemplate(&newCronJob.Spec.JobTemplate.Spec.Template, &oldCronJob.Spec.JobTemplate.Spec.Template)
|
||||||
|
return validation.ValidateCronJobUpdate(newCronJob, oldCronJob, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cronJobStatusStrategy struct {
|
type cronJobStatusStrategy struct {
|
||||||
|
@ -103,7 +103,8 @@ func (jobStrategy) Validate(ctx context.Context, obj runtime.Object) field.Error
|
|||||||
if job.Spec.ManualSelector == nil || *job.Spec.ManualSelector == false {
|
if job.Spec.ManualSelector == nil || *job.Spec.ManualSelector == false {
|
||||||
generateSelector(job)
|
generateSelector(job)
|
||||||
}
|
}
|
||||||
return validation.ValidateJob(job)
|
opts := pod.GetValidationOptionsFromPodTemplate(&job.Spec.Template, nil)
|
||||||
|
return validation.ValidateJob(job, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateSelector adds a selector to a job and labels to its template
|
// generateSelector adds a selector to a job and labels to its template
|
||||||
@ -173,8 +174,10 @@ func (jobStrategy) AllowCreateOnUpdate() bool {
|
|||||||
func (jobStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (jobStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
job := obj.(*batch.Job)
|
job := obj.(*batch.Job)
|
||||||
oldJob := old.(*batch.Job)
|
oldJob := old.(*batch.Job)
|
||||||
validationErrorList := validation.ValidateJob(job)
|
|
||||||
updateErrorList := validation.ValidateJobUpdate(job, oldJob)
|
opts := pod.GetValidationOptionsFromPodTemplate(&job.Spec.Template, &oldJob.Spec.Template)
|
||||||
|
validationErrorList := validation.ValidateJob(job, opts)
|
||||||
|
updateErrorList := validation.ValidateJobUpdate(job, oldJob, opts)
|
||||||
return append(validationErrorList, updateErrorList...)
|
return append(validationErrorList, updateErrorList...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,10 +91,7 @@ func (podStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
|
|||||||
// Validate validates a new pod.
|
// Validate validates a new pod.
|
||||||
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
opts := validation.PodValidationOptions{
|
opts := podutil.GetValidationOptionsFromPodSpec(&pod.Spec, nil)
|
||||||
// Allow multiple huge pages on pod create if feature is enabled
|
|
||||||
AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
|
|
||||||
}
|
|
||||||
return validation.ValidatePodCreate(pod, opts)
|
return validation.ValidatePodCreate(pod, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,11 +106,10 @@ func (podStrategy) AllowCreateOnUpdate() bool {
|
|||||||
|
|
||||||
// ValidateUpdate is the default update validation for an end user.
|
// ValidateUpdate is the default update validation for an end user.
|
||||||
func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
oldFailsSingleHugepagesValidation := len(validation.ValidatePodSingleHugePageResources(old.(*api.Pod), field.NewPath("spec"))) > 0
|
// Allow downward api usage of hugepages on pod update if feature is enabled or if the old pod already had used them.
|
||||||
opts := validation.PodValidationOptions{
|
pod := obj.(*api.Pod)
|
||||||
// Allow multiple huge pages on pod create if feature is enabled or if the old pod already has multiple hugepages specified
|
oldPod := old.(*api.Pod)
|
||||||
AllowMultipleHugePageResources: oldFailsSingleHugepagesValidation || utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
|
opts := podutil.GetValidationOptionsFromPodSpec(&pod.Spec, &oldPod.Spec)
|
||||||
}
|
|
||||||
return validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
return validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +178,10 @@ type podEphemeralContainersStrategy struct {
|
|||||||
var EphemeralContainersStrategy = podEphemeralContainersStrategy{Strategy}
|
var EphemeralContainersStrategy = podEphemeralContainersStrategy{Strategy}
|
||||||
|
|
||||||
func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
return validation.ValidatePodEphemeralContainersUpdate(obj.(*api.Pod), old.(*api.Pod))
|
newPod := obj.(*api.Pod)
|
||||||
|
oldPod := old.(*api.Pod)
|
||||||
|
opts := podutil.GetValidationOptionsFromPodSpec(&newPod.Spec, &oldPod.Spec)
|
||||||
|
return validation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttrs returns labels and fields of a given object for filtering purposes.
|
// GetAttrs returns labels and fields of a given object for filtering purposes.
|
||||||
|
@ -53,7 +53,8 @@ func (podTemplateStrategy) PrepareForCreate(ctx context.Context, obj runtime.Obj
|
|||||||
// Validate validates a new pod template.
|
// Validate validates a new pod template.
|
||||||
func (podTemplateStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (podTemplateStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
template := obj.(*api.PodTemplate)
|
template := obj.(*api.PodTemplate)
|
||||||
return corevalidation.ValidatePodTemplate(template)
|
opts := pod.GetValidationOptionsFromPodTemplate(&template.Template, nil)
|
||||||
|
return corevalidation.ValidatePodTemplate(template, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize normalizes the object after validation.
|
// Canonicalize normalizes the object after validation.
|
||||||
@ -77,7 +78,10 @@ func (podTemplateStrategy) PrepareForUpdate(ctx context.Context, obj, old runtim
|
|||||||
func (podTemplateStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (podTemplateStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
template := obj.(*api.PodTemplate)
|
template := obj.(*api.PodTemplate)
|
||||||
oldTemplate := old.(*api.PodTemplate)
|
oldTemplate := old.(*api.PodTemplate)
|
||||||
return corevalidation.ValidatePodTemplateUpdate(template, oldTemplate)
|
|
||||||
|
// Allow downward api usage of hugepages on pod update if feature is enabled or if the old pod already had used them.
|
||||||
|
opts := pod.GetValidationOptionsFromPodTemplate(&template.Template, &oldTemplate.Template)
|
||||||
|
return corevalidation.ValidatePodTemplateUpdate(template, oldTemplate, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (podTemplateStrategy) AllowUnconditionalUpdate() bool {
|
func (podTemplateStrategy) AllowUnconditionalUpdate() bool {
|
||||||
|
@ -108,7 +108,8 @@ func (rcStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object)
|
|||||||
// Validate validates a new replication controller.
|
// Validate validates a new replication controller.
|
||||||
func (rcStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (rcStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
controller := obj.(*api.ReplicationController)
|
controller := obj.(*api.ReplicationController)
|
||||||
return validation.ValidateReplicationController(controller)
|
opts := pod.GetValidationOptionsFromPodTemplate(controller.Spec.Template, nil)
|
||||||
|
return validation.ValidateReplicationController(controller, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize normalizes the object after validation.
|
// Canonicalize normalizes the object after validation.
|
||||||
@ -126,8 +127,9 @@ func (rcStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) f
|
|||||||
oldRc := old.(*api.ReplicationController)
|
oldRc := old.(*api.ReplicationController)
|
||||||
newRc := obj.(*api.ReplicationController)
|
newRc := obj.(*api.ReplicationController)
|
||||||
|
|
||||||
validationErrorList := validation.ValidateReplicationController(newRc)
|
opts := pod.GetValidationOptionsFromPodTemplate(newRc.Spec.Template, oldRc.Spec.Template)
|
||||||
updateErrorList := validation.ValidateReplicationControllerUpdate(newRc, oldRc)
|
validationErrorList := validation.ValidateReplicationController(newRc, opts)
|
||||||
|
updateErrorList := validation.ValidateReplicationControllerUpdate(newRc, oldRc, opts)
|
||||||
errs := append(validationErrorList, updateErrorList...)
|
errs := append(validationErrorList, updateErrorList...)
|
||||||
|
|
||||||
for key, value := range helper.NonConvertibleFields(oldRc.Annotations) {
|
for key, value := range helper.NonConvertibleFields(oldRc.Annotations) {
|
||||||
|
@ -25,40 +25,35 @@ func (rn ResourceName) String() string {
|
|||||||
return string(rn)
|
return string(rn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the CPU limit if specified.
|
// Cpu returns the Cpu limit if specified.
|
||||||
func (rl *ResourceList) Cpu() *resource.Quantity {
|
func (rl *ResourceList) Cpu() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceCPU]; ok {
|
return rl.Name(ResourceCPU, resource.DecimalSI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{Format: resource.DecimalSI}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the Memory limit if specified.
|
// Memory returns the Memory limit if specified.
|
||||||
func (rl *ResourceList) Memory() *resource.Quantity {
|
func (rl *ResourceList) Memory() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceMemory]; ok {
|
return rl.Name(ResourceMemory, resource.BinarySI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{Format: resource.BinarySI}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the Storage limit if specified.
|
// Storage returns the Storage limit if specified.
|
||||||
func (rl *ResourceList) Storage() *resource.Quantity {
|
func (rl *ResourceList) Storage() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceStorage]; ok {
|
return rl.Name(ResourceStorage, resource.BinarySI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{Format: resource.BinarySI}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pods returns the list of pods
|
||||||
func (rl *ResourceList) Pods() *resource.Quantity {
|
func (rl *ResourceList) Pods() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourcePods]; ok {
|
return rl.Name(ResourcePods, resource.DecimalSI)
|
||||||
return &val
|
|
||||||
}
|
|
||||||
return &resource.Quantity{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StorageEphemeral returns the list of ephemeral storage volumes, if any
|
||||||
func (rl *ResourceList) StorageEphemeral() *resource.Quantity {
|
func (rl *ResourceList) StorageEphemeral() *resource.Quantity {
|
||||||
if val, ok := (*rl)[ResourceEphemeralStorage]; ok {
|
return rl.Name(ResourceEphemeralStorage, resource.BinarySI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the resource with name if specified, otherwise it returns a nil quantity with default format.
|
||||||
|
func (rl *ResourceList) Name(name ResourceName, defaultFormat resource.Format) *resource.Quantity {
|
||||||
|
if val, ok := (*rl)[name]; ok {
|
||||||
return &val
|
return &val
|
||||||
}
|
}
|
||||||
return &resource.Quantity{}
|
return &resource.Quantity{Format: defaultFormat}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,20 @@ func extractContainerResourceValue(fs *corev1.ResourceFieldSelector, container *
|
|||||||
case "requests.ephemeral-storage":
|
case "requests.ephemeral-storage":
|
||||||
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
||||||
}
|
}
|
||||||
|
// handle extended standard resources with dynamic names
|
||||||
|
// example: requests.hugepages-<pageSize> or limits.hugepages-<pageSize>
|
||||||
|
if strings.HasPrefix(fs.Resource, "requests.") {
|
||||||
|
resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
|
||||||
|
if IsHugePageResourceName(resourceName) {
|
||||||
|
return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(fs.Resource, "limits.") {
|
||||||
|
resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
|
||||||
|
if IsHugePageResourceName(resourceName) {
|
||||||
|
return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
|
return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +230,13 @@ func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Q
|
|||||||
return strconv.FormatInt(m, 10), nil
|
return strconv.FormatInt(m, 10), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertResourceHugePagesToString converts hugepages value to the format of divisor and returns
|
||||||
|
// ceiling of the value.
|
||||||
|
func convertResourceHugePagesToString(hugePages *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||||
|
m := int64(math.Ceil(float64(hugePages.Value()) / float64(divisor.Value())))
|
||||||
|
return strconv.FormatInt(m, 10), nil
|
||||||
|
}
|
||||||
|
|
||||||
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
||||||
// ceiling of the value.
|
// ceiling of the value.
|
||||||
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||||
@ -269,3 +289,9 @@ func GetEnvVarRefString(from *corev1.EnvVarSource) string {
|
|||||||
|
|
||||||
return "invalid valueFrom"
|
return "invalid valueFrom"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsHugePageResourceName returns true if the resource name has the huge page
|
||||||
|
// resource prefix.
|
||||||
|
func IsHugePageResourceName(name corev1.ResourceName) bool {
|
||||||
|
return strings.HasPrefix(string(name), corev1.ResourceHugePagesPrefix)
|
||||||
|
}
|
||||||
|
@ -108,7 +108,20 @@ func ExtractContainerResourceValue(fs *corev1.ResourceFieldSelector, container *
|
|||||||
case "requests.ephemeral-storage":
|
case "requests.ephemeral-storage":
|
||||||
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
||||||
}
|
}
|
||||||
|
// handle extended standard resources with dynamic names
|
||||||
|
// example: requests.hugepages-<pageSize> or limits.hugepages-<pageSize>
|
||||||
|
if strings.HasPrefix(fs.Resource, "requests.") {
|
||||||
|
resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
|
||||||
|
if IsHugePageResourceName(resourceName) {
|
||||||
|
return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(fs.Resource, "limits.") {
|
||||||
|
resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
|
||||||
|
if IsHugePageResourceName(resourceName) {
|
||||||
|
return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
|
return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +139,13 @@ func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Q
|
|||||||
return strconv.FormatInt(m, 10), nil
|
return strconv.FormatInt(m, 10), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertResourceHugePagesToString converts hugepages value to the format of divisor and returns
|
||||||
|
// ceiling of the value.
|
||||||
|
func convertResourceHugePagesToString(hugePages *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||||
|
m := int64(math.Ceil(float64(hugePages.Value()) / float64(divisor.Value())))
|
||||||
|
return strconv.FormatInt(m, 10), nil
|
||||||
|
}
|
||||||
|
|
||||||
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
||||||
// ceiling of the value.
|
// ceiling of the value.
|
||||||
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||||
|
@ -261,7 +261,6 @@ var _ = ginkgo.Describe("[sig-storage] Downward API volume", func() {
|
|||||||
|
|
||||||
f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
|
f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
func downwardAPIVolumePodForModeTest(name, filePath string, itemMode, defaultMode *int32) *v1.Pod {
|
func downwardAPIVolumePodForModeTest(name, filePath string, itemMode, defaultMode *int32) *v1.Pod {
|
||||||
|
Loading…
Reference in New Issue
Block a user