Merge pull request #50924 from liggitt/alpha-fields

Automatic merge from submit-queue (batch tested with PRs 51682, 51546, 51369, 50924, 51827)

Clear values for disabled alpha fields

Fixes #51831

Before persisting new or updated resources, alpha fields that are disabled by feature gate must be removed from the incoming objects.

This adds a helper for clearing these values for pod specs and calls it from the strategies of all in-tree resources containing pod specs.

Addresses https://github.com/kubernetes/community/pull/869
pull/6/head
Kubernetes Submit Queue 2017-09-03 15:54:22 -07:00 committed by GitHub
commit f9a82dd3b7
22 changed files with 107 additions and 3 deletions

View File

@ -11,7 +11,9 @@ go_library(
srcs = ["util.go"],
deps = [
"//pkg/api:go_default_library",
"//pkg/features:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@ -18,7 +18,9 @@ package pod
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/features"
)
// Visitor is called with each object name, and returns true if visiting should continue
@ -225,3 +227,20 @@ func UpdatePodCondition(status *api.PodStatus, condition *api.PodCondition) bool
// Return true if one of the fields have changed.
return !isEqual
}
// DropDisabledAlphaFields removes disabled fields from the pod spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pod spec.
func DropDisabledAlphaFields(podSpec *api.PodSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.PodPriority) {
podSpec.Priority = nil
podSpec.PriorityClassName = ""
}
if !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
for i := range podSpec.Volumes {
if podSpec.Volumes[i].EmptyDir != nil {
podSpec.Volumes[i].EmptyDir.SizeLimit = nil
}
}
}
}

View File

@ -15,6 +15,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",

View File

@ -24,6 +24,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation"
)
@ -55,6 +56,8 @@ func (statefulSetStrategy) PrepareForCreate(ctx genericapirequest.Context, obj r
statefulSet.Status = apps.StatefulSetStatus{}
statefulSet.Generation = 1
pod.DropDisabledAlphaFields(&statefulSet.Spec.Template.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -64,6 +67,9 @@ func (statefulSetStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj,
// Update is not allowed to set status
newStatefulSet.Status = oldStatefulSet.Status
pod.DropDisabledAlphaFields(&newStatefulSet.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldStatefulSet.Spec.Template.Spec)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.

View File

@ -14,6 +14,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",

View File

@ -23,6 +23,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
)
@ -51,6 +52,8 @@ func (cronJobStrategy) NamespaceScoped() bool {
func (cronJobStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
cronJob := obj.(*batch.CronJob)
cronJob.Status = batch.CronJobStatus{}
pod.DropDisabledAlphaFields(&cronJob.Spec.JobTemplate.Spec.Template.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -58,6 +61,9 @@ func (cronJobStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old
newCronJob := obj.(*batch.CronJob)
oldCronJob := old.(*batch.CronJob)
newCronJob.Status = oldCronJob.Status
pod.DropDisabledAlphaFields(&newCronJob.Spec.JobTemplate.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldCronJob.Spec.JobTemplate.Spec.Template.Spec)
}
// Validate validates a new scheduled job.

View File

@ -14,6 +14,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
)
@ -59,6 +60,8 @@ func (jobStrategy) NamespaceScoped() bool {
func (jobStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
job := obj.(*batch.Job)
job.Status = batch.JobStatus{}
pod.DropDisabledAlphaFields(&job.Spec.Template.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -66,6 +69,9 @@ func (jobStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runt
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
newJob.Status = oldJob.Status
pod.DropDisabledAlphaFields(&newJob.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldJob.Spec.Template.Spec)
}
// Validate validates a new job.

View File

@ -16,6 +16,7 @@ go_library(
deps = [
"//pkg/api:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/client:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
@ -32,6 +33,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@ -34,8 +34,10 @@ import (
"k8s.io/apiserver/pkg/registry/generic"
pkgstorage "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/client"
)
@ -61,8 +63,12 @@ func (nodeStrategy) AllowCreateOnUpdate() bool {
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (nodeStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
_ = obj.(*api.Node)
node := obj.(*api.Node)
// Nodes allow *all* fields, including status, to be set on create.
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
node.Spec.ConfigSource = nil
}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -70,6 +76,11 @@ func (nodeStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old run
newNode := obj.(*api.Node)
oldNode := old.(*api.Node)
newNode.Status = oldNode.Status
if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
newNode.Spec.ConfigSource = nil
oldNode.Spec.ConfigSource = nil
}
}
// Validate validates a new node.

View File

@ -15,6 +15,7 @@ go_library(
deps = [
"//pkg/api:go_default_library",
"//pkg/api/helper/qos:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/kubelet/client:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",

View File

@ -39,6 +39,7 @@ import (
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/helper/qos"
podutil "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/kubelet/client"
)
@ -65,6 +66,8 @@ func (podStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.O
Phase: api.PodPending,
QOSClass: qos.GetPodQOS(pod),
}
podutil.DropDisabledAlphaFields(&pod.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -72,6 +75,9 @@ func (podStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runt
newPod := obj.(*api.Pod)
oldPod := old.(*api.Pod)
newPod.Status = oldPod.Status
podutil.DropDisabledAlphaFields(&newPod.Spec)
podutil.DropDisabledAlphaFields(&oldPod.Spec)
}
// Validate validates a new pod.

View File

@ -13,6 +13,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/api/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",

View File

@ -22,6 +22,7 @@ import (
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/api/validation"
)
@ -42,7 +43,9 @@ func (podTemplateStrategy) NamespaceScoped() bool {
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (podTemplateStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
_ = obj.(*api.PodTemplate)
template := obj.(*api.PodTemplate)
pod.DropDisabledAlphaFields(&template.Template.Spec)
}
// Validate validates a new pod template.
@ -62,7 +65,11 @@ func (podTemplateStrategy) AllowCreateOnUpdate() bool {
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (podTemplateStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
_ = obj.(*api.PodTemplate)
newTemplate := obj.(*api.PodTemplate)
oldTemplate := old.(*api.PodTemplate)
pod.DropDisabledAlphaFields(&newTemplate.Template.Spec)
pod.DropDisabledAlphaFields(&oldTemplate.Template.Spec)
}
// ValidateUpdate is the default update validation for an end user.

View File

@ -16,6 +16,7 @@ go_library(
deps = [
"//pkg/api:go_default_library",
"//pkg/api/helper:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/api/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",

View File

@ -35,6 +35,7 @@ import (
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/helper"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/api/validation"
)
@ -64,6 +65,10 @@ func (rcStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Ob
controller.Status = api.ReplicationControllerStatus{}
controller.Generation = 1
if controller.Spec.Template != nil {
pod.DropDisabledAlphaFields(&controller.Spec.Template.Spec)
}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -73,6 +78,13 @@ func (rcStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runti
// update is not allowed to set status
newController.Status = oldController.Status
if oldController.Spec.Template != nil {
pod.DropDisabledAlphaFields(&oldController.Spec.Template.Spec)
}
if newController.Spec.Template != nil {
pod.DropDisabledAlphaFields(&newController.Spec.Template.Spec)
}
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object. We push
// the burden of managing the status onto the clients because we can't (in general)

View File

@ -14,6 +14,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/validation:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
)
@ -63,6 +64,8 @@ func (daemonSetStrategy) PrepareForCreate(ctx genericapirequest.Context, obj run
if daemonSet.Spec.TemplateGeneration < 1 {
daemonSet.Spec.TemplateGeneration = 1
}
pod.DropDisabledAlphaFields(&daemonSet.Spec.Template.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -70,6 +73,9 @@ func (daemonSetStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, ol
newDaemonSet := obj.(*extensions.DaemonSet)
oldDaemonSet := old.(*extensions.DaemonSet)
pod.DropDisabledAlphaFields(&newDaemonSet.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldDaemonSet.Spec.Template.Spec)
// update is not allowed to set status
newDaemonSet.Status = oldDaemonSet.Status

View File

@ -15,6 +15,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/validation:go_default_library",
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
)
@ -61,6 +62,8 @@ func (deploymentStrategy) PrepareForCreate(ctx genericapirequest.Context, obj ru
deployment := obj.(*extensions.Deployment)
deployment.Status = extensions.DeploymentStatus{}
deployment.Generation = 1
pod.DropDisabledAlphaFields(&deployment.Spec.Template.Spec)
}
// Validate validates a new deployment.
@ -84,6 +87,9 @@ func (deploymentStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, o
oldDeployment := old.(*extensions.Deployment)
newDeployment.Status = oldDeployment.Status
pod.DropDisabledAlphaFields(&newDeployment.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldDeployment.Spec.Template.Spec)
// Spec updates bump the generation so that we can distinguish between
// scaling events and template changes, annotation updates bump the generation
// because annotations are copied from deployments to their replica sets.

View File

@ -15,6 +15,7 @@ go_library(
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/validation:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",

View File

@ -37,6 +37,7 @@ import (
apistorage "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
)
@ -67,6 +68,8 @@ func (rsStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Ob
rs.Status = extensions.ReplicaSetStatus{}
rs.Generation = 1
pod.DropDisabledAlphaFields(&rs.Spec.Template.Spec)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@ -76,6 +79,9 @@ func (rsStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runti
// update is not allowed to set status
newRS.Status = oldRS.Status
pod.DropDisabledAlphaFields(&newRS.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldRS.Spec.Template.Spec)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object. We push
// the burden of managing the status onto the clients because we can't (in general)