Merge pull request #101688 from liggitt/field-warnings

Add field-level warning plumbing and add pod spec warnings
This commit is contained in:
Kubernetes Prow Robot 2021-05-19 17:23:04 -07:00 committed by GitHub
commit c115435adc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 1573 additions and 3 deletions

257
pkg/api/pod/warnings.go Normal file
View File

@ -0,0 +1,257 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pod
import (
"context"
"fmt"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/pods"
)
func GetWarningsForPod(ctx context.Context, pod, oldPod *api.Pod) []string {
if pod == nil {
return nil
}
var (
oldSpec *api.PodSpec
oldMeta *metav1.ObjectMeta
)
if oldPod != nil {
oldSpec = &oldPod.Spec
oldMeta = &oldPod.ObjectMeta
}
return warningsForPodSpecAndMeta(nil, &pod.Spec, &pod.ObjectMeta, oldSpec, oldMeta)
}
func GetWarningsForPodTemplate(ctx context.Context, fieldPath *field.Path, podTemplate, oldPodTemplate *api.PodTemplateSpec) []string {
if podTemplate == nil {
return nil
}
var (
oldSpec *api.PodSpec
oldMeta *metav1.ObjectMeta
)
if oldPodTemplate != nil {
oldSpec = &oldPodTemplate.Spec
oldMeta = &oldPodTemplate.ObjectMeta
}
return warningsForPodSpecAndMeta(fieldPath, &podTemplate.Spec, &podTemplate.ObjectMeta, oldSpec, oldMeta)
}
var deprecatedNodeLabels = map[string]string{
`beta.kubernetes.io/arch`: `deprecated since v1.14; use "kubernetes.io/arch" instead`,
`beta.kubernetes.io/os`: `deprecated since v1.14; use "kubernetes.io/os" instead`,
`failure-domain.beta.kubernetes.io/region`: `deprecated since v1.17; use "topology.kubernetes.io/region" instead`,
`failure-domain.beta.kubernetes.io/zone`: `deprecated since v1.17; use "topology.kubernetes.io/zone" instead`,
`beta.kubernetes.io/instance-type`: `deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`,
}
var deprecatedAnnotations = []struct {
key string
prefix string
message string
}{
{
key: `scheduler.alpha.kubernetes.io/critical-pod`,
message: `non-functional in v1.16+; use the "priorityClassName" field instead`,
},
{
key: `seccomp.security.alpha.kubernetes.io/pod`,
prefix: `container.seccomp.security.alpha.kubernetes.io/`,
message: `deprecated since v1.19; use the "seccompProfile" field instead`,
},
{
key: `security.alpha.kubernetes.io/sysctls`,
message: `non-functional in v1.11+; use the "sysctls" field instead`,
},
{
key: `security.alpha.kubernetes.io/unsafe-sysctls`,
message: `non-functional in v1.11+; use the "sysctls" field instead`,
},
}
func warningsForPodSpecAndMeta(fieldPath *field.Path, podSpec *api.PodSpec, meta *metav1.ObjectMeta, oldPodSpec *api.PodSpec, oldMeta *metav1.ObjectMeta) []string {
var warnings []string
// use of deprecated node labels in selectors/affinity/topology
for k := range podSpec.NodeSelector {
if msg, deprecated := deprecatedNodeLabels[k]; deprecated {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("spec", "nodeSelector").Key(k), msg))
}
}
if podSpec.Affinity != nil && podSpec.Affinity.NodeAffinity != nil {
n := podSpec.Affinity.NodeAffinity
if n.RequiredDuringSchedulingIgnoredDuringExecution != nil {
for i, t := range n.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
for j, e := range t.MatchExpressions {
if msg, deprecated := deprecatedNodeLabels[e.Key]; deprecated {
warnings = append(
warnings,
fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("spec", "affinity", "nodeAffinity", "requiredDuringSchedulingIgnoredDuringExecution", "nodeSelectorTerms").Index(i).
Child("matchExpressions").Index(j).
Child("key"),
e.Key,
msg,
),
)
}
}
}
}
for i, t := range n.PreferredDuringSchedulingIgnoredDuringExecution {
for j, e := range t.Preference.MatchExpressions {
if msg, deprecated := deprecatedNodeLabels[e.Key]; deprecated {
warnings = append(
warnings,
fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("spec", "affinity", "nodeAffinity", "preferredDuringSchedulingIgnoredDuringExecution").Index(i).
Child("preference").
Child("matchExpressions").Index(j).
Child("key"),
e.Key,
msg,
),
)
}
}
}
}
for i, t := range podSpec.TopologySpreadConstraints {
if msg, deprecated := deprecatedNodeLabels[t.TopologyKey]; deprecated {
warnings = append(warnings, fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("spec", "topologySpreadConstraints").Index(i).Child("topologyKey"),
t.TopologyKey,
msg,
))
}
}
// use of deprecated annotations
for _, deprecated := range deprecatedAnnotations {
if _, exists := meta.Annotations[deprecated.key]; exists {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("metadata", "annotations").Key(deprecated.key), deprecated.message))
}
if len(deprecated.prefix) > 0 {
for k := range meta.Annotations {
if strings.HasPrefix(k, deprecated.prefix) {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("metadata", "annotations").Key(k), deprecated.message))
break
}
}
}
}
// removed volume plugins
for i, v := range podSpec.Volumes {
if v.PhotonPersistentDisk != nil {
warnings = append(warnings, fmt.Sprintf("%s: non-functional in v1.16+", fieldPath.Child("spec", "volumes").Index(i).Child("photonPersistentDisk")))
}
}
// duplicate hostAliases (#91670, #58477)
if len(podSpec.HostAliases) > 1 {
items := sets.NewString()
for i, item := range podSpec.HostAliases {
if items.Has(item.IP) {
warnings = append(warnings, fmt.Sprintf("%s: duplicate ip %q", fieldPath.Child("spec", "hostAliases").Index(i).Child("ip"), item.IP))
} else {
items.Insert(item.IP)
}
}
}
// duplicate imagePullSecrets (#91629, #58477)
if len(podSpec.ImagePullSecrets) > 1 {
items := sets.NewString()
for i, item := range podSpec.ImagePullSecrets {
if items.Has(item.Name) {
warnings = append(warnings, fmt.Sprintf("%s: duplicate name %q", fieldPath.Child("spec", "imagePullSecrets").Index(i).Child("name"), item.Name))
} else {
items.Insert(item.Name)
}
}
}
// imagePullSecrets with empty name (#99454#issuecomment-787838112)
for i, item := range podSpec.ImagePullSecrets {
if len(item.Name) == 0 {
warnings = append(warnings, fmt.Sprintf("%s: invalid empty name %q", fieldPath.Child("spec", "imagePullSecrets").Index(i).Child("name"), item.Name))
}
}
// duplicate volume names (#78266, #58477)
if len(podSpec.Volumes) > 1 {
items := sets.NewString()
for i, item := range podSpec.Volumes {
if items.Has(item.Name) {
warnings = append(warnings, fmt.Sprintf("%s: duplicate name %q", fieldPath.Child("spec", "volumes").Index(i).Child("name"), item.Name))
} else {
items.Insert(item.Name)
}
}
}
// fractional memory/ephemeral-storage requests/limits (#79950, #49442, #18538)
if value, ok := podSpec.Overhead[api.ResourceMemory]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", fieldPath.Child("spec", "overhead").Key(string(api.ResourceMemory)), value.String()))
}
if value, ok := podSpec.Overhead[api.ResourceEphemeralStorage]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", fieldPath.Child("spec", "overhead").Key(string(api.ResourceEphemeralStorage)), value.String()))
}
pods.VisitContainersWithPath(podSpec, fieldPath.Child("spec"), func(c *api.Container, p *field.Path) bool {
// fractional memory/ephemeral-storage requests/limits (#79950, #49442, #18538)
if value, ok := c.Resources.Limits[api.ResourceMemory]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "limits").Key(string(api.ResourceMemory)), value.String()))
}
if value, ok := c.Resources.Requests[api.ResourceMemory]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "requests").Key(string(api.ResourceMemory)), value.String()))
}
if value, ok := c.Resources.Limits[api.ResourceEphemeralStorage]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "limits").Key(string(api.ResourceEphemeralStorage)), value.String()))
}
if value, ok := c.Resources.Requests[api.ResourceEphemeralStorage]; ok && value.MilliValue()%int64(1000) != int64(0) {
warnings = append(warnings, fmt.Sprintf("%s: fractional byte value %q is invalid, must be an integer", p.Child("resources", "requests").Key(string(api.ResourceEphemeralStorage)), value.String()))
}
// duplicate containers[*].env (#86163, #93266, #58477)
if len(c.Env) > 1 {
items := sets.NewString()
for i, item := range c.Env {
if items.Has(item.Name) {
warnings = append(warnings, fmt.Sprintf("%s: duplicate name %q", p.Child("env").Index(i).Child("name"), item.Name))
} else {
items.Insert(item.Name)
}
}
}
return true
})
return warnings
}

View File

@ -0,0 +1,416 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pod
import (
"context"
"testing"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
api "k8s.io/kubernetes/pkg/apis/core"
)
func BenchmarkNoWarnings(b *testing.B) {
ctx := context.TODO()
resources := api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("4M"),
api.ResourceEphemeralStorage: resource.MustParse("4G"),
}
env := []api.EnvVar{
{Name: "a"},
{Name: "b"},
}
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{`foo`: `bar`},
},
Spec: api.PodSpec{
NodeSelector: map[string]string{"foo": "bar", "baz": "quux"},
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{MatchExpressions: []api.NodeSelectorRequirement{{Key: `foo`}}},
},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{
{Preference: api.NodeSelectorTerm{MatchExpressions: []api.NodeSelectorRequirement{{Key: `foo`}}}},
},
},
},
TopologySpreadConstraints: []api.TopologySpreadConstraint{
{TopologyKey: `foo`},
},
HostAliases: []api.HostAlias{
{IP: "1.1.1.1"},
{IP: "2.2.2.2"},
},
ImagePullSecrets: []api.LocalObjectReference{
{Name: "secret1"},
{Name: "secret2"},
},
InitContainers: []api.Container{
{Name: "init1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
{Name: "init2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
},
Containers: []api.Container{
{Name: "container1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
{Name: "container2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
},
Overhead: resources,
Volumes: []api.Volume{
{Name: "a"},
{Name: "b"},
},
},
}
oldPod := &api.Pod{}
b.ResetTimer()
for i := 0; i < b.N; i++ {
w := GetWarningsForPod(ctx, pod, oldPod)
if len(w) > 0 {
b.Fatalf("expected 0 warnings, got %q", w)
}
}
}
func BenchmarkWarnings(b *testing.B) {
ctx := context.TODO()
resources := api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("4m"),
api.ResourceEphemeralStorage: resource.MustParse("4m"),
}
env := []api.EnvVar{
{Name: "a"},
{Name: "a"},
}
pod := &api.Pod{
Spec: api.PodSpec{
HostAliases: []api.HostAlias{
{IP: "1.1.1.1"},
{IP: "1.1.1.1"},
},
ImagePullSecrets: []api.LocalObjectReference{
{Name: "secret1"},
{Name: "secret1"},
{Name: ""},
},
InitContainers: []api.Container{
{Name: "init1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
{Name: "init2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
},
Containers: []api.Container{
{Name: "container1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
{Name: "container2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}},
},
Overhead: resources,
Volumes: []api.Volume{
{Name: "a"},
{Name: "a"},
},
},
}
oldPod := &api.Pod{}
b.ResetTimer()
for i := 0; i < b.N; i++ {
GetWarningsForPod(ctx, pod, oldPod)
}
}
func TestWarnings(t *testing.T) {
resources := api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("4m"),
api.ResourceEphemeralStorage: resource.MustParse("4m"),
}
testcases := []struct {
name string
template *api.PodTemplateSpec
expected []string
}{
{
name: "null",
template: nil,
expected: nil,
},
{
name: "photon",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
Volumes: []api.Volume{
{Name: "p", VolumeSource: api.VolumeSource{PhotonPersistentDisk: &api.PhotonPersistentDiskVolumeSource{}}},
}},
},
expected: []string{`spec.volumes[0].photonPersistentDisk: non-functional in v1.16+`},
},
{
name: "duplicate hostAlias",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
HostAliases: []api.HostAlias{
{IP: "1.1.1.1"},
{IP: "1.1.1.1"},
{IP: "1.1.1.1"},
}},
},
expected: []string{
`spec.hostAliases[1].ip: duplicate ip "1.1.1.1"`,
`spec.hostAliases[2].ip: duplicate ip "1.1.1.1"`,
},
},
{
name: "duplicate imagePullSecret",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
ImagePullSecrets: []api.LocalObjectReference{
{Name: "a"},
{Name: "a"},
{Name: "a"},
}},
},
expected: []string{
`spec.imagePullSecrets[1].name: duplicate name "a"`,
`spec.imagePullSecrets[2].name: duplicate name "a"`,
},
},
{
name: "empty imagePullSecret",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
ImagePullSecrets: []api.LocalObjectReference{
{Name: ""},
}},
},
expected: []string{
`spec.imagePullSecrets[0].name: invalid empty name ""`,
},
},
{
name: "duplicate volume",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
Volumes: []api.Volume{
{Name: "a"},
{Name: "a"},
{Name: "a"},
}},
},
expected: []string{
`spec.volumes[1].name: duplicate name "a"`,
`spec.volumes[2].name: duplicate name "a"`,
},
},
{
name: "duplicate env",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
InitContainers: []api.Container{{Env: []api.EnvVar{
{Name: "a"},
{Name: "a"},
{Name: "a"},
}}},
Containers: []api.Container{{Env: []api.EnvVar{
{Name: "b"},
{Name: "b"},
{Name: "b"},
}}},
}},
expected: []string{
`spec.initContainers[0].env[1].name: duplicate name "a"`,
`spec.initContainers[0].env[2].name: duplicate name "a"`,
`spec.containers[0].env[1].name: duplicate name "b"`,
`spec.containers[0].env[2].name: duplicate name "b"`,
},
},
{
name: "fractional resources",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
InitContainers: []api.Container{{
Resources: api.ResourceRequirements{Requests: resources, Limits: resources},
}},
Containers: []api.Container{{
Resources: api.ResourceRequirements{Requests: resources, Limits: resources},
}},
Overhead: resources,
}},
expected: []string{
`spec.initContainers[0].resources.requests[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`,
`spec.initContainers[0].resources.requests[memory]: fractional byte value "4m" is invalid, must be an integer`,
`spec.initContainers[0].resources.limits[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`,
`spec.initContainers[0].resources.limits[memory]: fractional byte value "4m" is invalid, must be an integer`,
`spec.containers[0].resources.requests[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`,
`spec.containers[0].resources.requests[memory]: fractional byte value "4m" is invalid, must be an integer`,
`spec.containers[0].resources.limits[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`,
`spec.containers[0].resources.limits[memory]: fractional byte value "4m" is invalid, must be an integer`,
`spec.overhead[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`,
`spec.overhead[memory]: fractional byte value "4m" is invalid, must be an integer`,
},
},
{
name: "node labels in nodeSelector",
template: &api.PodTemplateSpec{Spec: api.PodSpec{
NodeSelector: map[string]string{
`beta.kubernetes.io/arch`: `true`,
`beta.kubernetes.io/os`: `true`,
`failure-domain.beta.kubernetes.io/region`: `true`,
`failure-domain.beta.kubernetes.io/zone`: `true`,
`beta.kubernetes.io/instance-type`: `true`,
},
}},
expected: []string{
`spec.nodeSelector[beta.kubernetes.io/arch]: deprecated since v1.14; use "kubernetes.io/arch" instead`,
`spec.nodeSelector[beta.kubernetes.io/instance-type]: deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`,
`spec.nodeSelector[beta.kubernetes.io/os]: deprecated since v1.14; use "kubernetes.io/os" instead`,
`spec.nodeSelector[failure-domain.beta.kubernetes.io/region]: deprecated since v1.17; use "topology.kubernetes.io/region" instead`,
`spec.nodeSelector[failure-domain.beta.kubernetes.io/zone]: deprecated since v1.17; use "topology.kubernetes.io/zone" instead`,
},
},
{
name: "node labels in affinity requiredDuringSchedulingIgnoredDuringExecution",
template: &api.PodTemplateSpec{
Spec: api.PodSpec{
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{Key: `foo`},
{Key: `beta.kubernetes.io/arch`},
{Key: `beta.kubernetes.io/os`},
{Key: `failure-domain.beta.kubernetes.io/region`},
{Key: `failure-domain.beta.kubernetes.io/zone`},
{Key: `beta.kubernetes.io/instance-type`},
},
},
},
},
},
},
},
},
expected: []string{
`spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[1].key: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`,
`spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[2].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`,
`spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[3].key: failure-domain.beta.kubernetes.io/region is deprecated since v1.17; use "topology.kubernetes.io/region" instead`,
`spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[4].key: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead`,
`spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[5].key: beta.kubernetes.io/instance-type is deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`,
},
},
{
name: "node labels in affinity preferredDuringSchedulingIgnoredDuringExecution",
template: &api.PodTemplateSpec{
Spec: api.PodSpec{
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{
{
Preference: api.NodeSelectorTerm{
MatchExpressions: []api.NodeSelectorRequirement{
{Key: `foo`},
{Key: `beta.kubernetes.io/arch`},
{Key: `beta.kubernetes.io/os`},
{Key: `failure-domain.beta.kubernetes.io/region`},
{Key: `failure-domain.beta.kubernetes.io/zone`},
{Key: `beta.kubernetes.io/instance-type`},
},
},
},
},
},
},
},
},
expected: []string{
`spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[1].key: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`,
`spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[2].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`,
`spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[3].key: failure-domain.beta.kubernetes.io/region is deprecated since v1.17; use "topology.kubernetes.io/region" instead`,
`spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[4].key: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead`,
`spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[5].key: beta.kubernetes.io/instance-type is deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`,
},
},
{
name: "node labels in topologySpreadConstraints",
template: &api.PodTemplateSpec{
Spec: api.PodSpec{
TopologySpreadConstraints: []api.TopologySpreadConstraint{
{TopologyKey: `foo`},
{TopologyKey: `beta.kubernetes.io/arch`},
{TopologyKey: `beta.kubernetes.io/os`},
{TopologyKey: `failure-domain.beta.kubernetes.io/region`},
{TopologyKey: `failure-domain.beta.kubernetes.io/zone`},
{TopologyKey: `beta.kubernetes.io/instance-type`},
},
},
},
expected: []string{
`spec.topologySpreadConstraints[1].topologyKey: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`,
`spec.topologySpreadConstraints[2].topologyKey: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`,
`spec.topologySpreadConstraints[3].topologyKey: failure-domain.beta.kubernetes.io/region is deprecated since v1.17; use "topology.kubernetes.io/region" instead`,
`spec.topologySpreadConstraints[4].topologyKey: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead`,
`spec.topologySpreadConstraints[5].topologyKey: beta.kubernetes.io/instance-type is deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`,
},
},
{
name: "annotations",
template: &api.PodTemplateSpec{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
`foo`: `bar`,
`scheduler.alpha.kubernetes.io/critical-pod`: `true`,
`seccomp.security.alpha.kubernetes.io/pod`: `default`,
`container.seccomp.security.alpha.kubernetes.io/foo`: `default`,
`security.alpha.kubernetes.io/sysctls`: `a,b,c`,
`security.alpha.kubernetes.io/unsafe-sysctls`: `d,e,f`,
}}},
expected: []string{
`metadata.annotations[scheduler.alpha.kubernetes.io/critical-pod]: non-functional in v1.16+; use the "priorityClassName" field instead`,
`metadata.annotations[seccomp.security.alpha.kubernetes.io/pod]: deprecated since v1.19; use the "seccompProfile" field instead`,
`metadata.annotations[container.seccomp.security.alpha.kubernetes.io/foo]: deprecated since v1.19; use the "seccompProfile" field instead`,
`metadata.annotations[security.alpha.kubernetes.io/sysctls]: non-functional in v1.11+; use the "sysctls" field instead`,
`metadata.annotations[security.alpha.kubernetes.io/unsafe-sysctls]: non-functional in v1.11+; use the "sysctls" field instead`,
},
},
}
for _, tc := range testcases {
t.Run("podspec_"+tc.name, func(t *testing.T) {
actual := sets.NewString(GetWarningsForPodTemplate(context.TODO(), nil, tc.template, &api.PodTemplateSpec{})...)
expected := sets.NewString(tc.expected...)
for _, missing := range expected.Difference(actual).List() {
t.Errorf("missing: %s", missing)
}
for _, extra := range actual.Difference(expected).List() {
t.Errorf("extra: %s", extra)
}
})
t.Run("pod_"+tc.name, func(t *testing.T) {
var pod *api.Pod
if tc.template != nil {
pod = &api.Pod{
ObjectMeta: tc.template.ObjectMeta,
Spec: tc.template.Spec,
}
}
actual := sets.NewString(GetWarningsForPod(context.TODO(), pod, &api.Pod{})...)
expected := sets.NewString(tc.expected...)
for _, missing := range expected.Difference(actual).List() {
t.Errorf("missing: %s", missing)
}
for _, extra := range actual.Difference(expected).List() {
t.Errorf("extra: %s", extra)
}
})
}
}

View File

@ -50,6 +50,11 @@ func (mutatingWebhookConfigurationStrategy) PrepareForCreate(ctx context.Context
ic.Generation = 1 ic.Generation = 1
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (mutatingWebhookConfigurationStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update. // PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (mutatingWebhookConfigurationStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (mutatingWebhookConfigurationStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.MutatingWebhookConfiguration) newIC := obj.(*admissionregistration.MutatingWebhookConfiguration)
@ -93,6 +98,11 @@ func (mutatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context,
return validation.ValidateMutatingWebhookConfigurationUpdate(obj.(*admissionregistration.MutatingWebhookConfiguration), old.(*admissionregistration.MutatingWebhookConfiguration), groupVersion) return validation.ValidateMutatingWebhookConfigurationUpdate(obj.(*admissionregistration.MutatingWebhookConfiguration), old.(*admissionregistration.MutatingWebhookConfiguration), groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update.
func (mutatingWebhookConfigurationStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for mutatingWebhookConfiguration objects. Status update should // AllowUnconditionalUpdate is the default update policy for mutatingWebhookConfiguration objects. Status update should
// only be allowed if version match. // only be allowed if version match.
func (mutatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool { func (mutatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool {

View File

@ -73,6 +73,11 @@ func (validatingWebhookConfigurationStrategy) Validate(ctx context.Context, obj
return validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion) return validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (validatingWebhookConfigurationStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (validatingWebhookConfigurationStrategy) Canonicalize(obj runtime.Object) { func (validatingWebhookConfigurationStrategy) Canonicalize(obj runtime.Object) {
} }
@ -92,6 +97,11 @@ func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context
return validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion) return validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update.
func (validatingWebhookConfigurationStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for validatingWebhookConfiguration objects. Status update should // AllowUnconditionalUpdate is the default update policy for validatingWebhookConfiguration objects. Status update should
// only be allowed if version match. // only be allowed if version match.
func (validatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool { func (validatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool {

View File

@ -73,6 +73,11 @@ func (storageVersionStrategy) Validate(ctx context.Context, obj runtime.Object)
return validation.ValidateStorageVersion(sv) return validation.ValidateStorageVersion(sv)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (storageVersionStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (storageVersionStrategy) Canonicalize(obj runtime.Object) { func (storageVersionStrategy) Canonicalize(obj runtime.Object) {
} }
@ -90,6 +95,11 @@ func (storageVersionStrategy) ValidateUpdate(ctx context.Context, obj, old runti
return validationErrorList return validationErrorList
} }
// WarningsOnUpdate returns warnings for the given update.
func (storageVersionStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for storageVersion objects. Status update should // AllowUnconditionalUpdate is the default update policy for storageVersion objects. Status update should
// only be allowed if version match. // only be allowed if version match.
func (storageVersionStrategy) AllowUnconditionalUpdate() bool { func (storageVersionStrategy) AllowUnconditionalUpdate() bool {
@ -127,3 +137,8 @@ func (storageVersionStatusStrategy) PrepareForUpdate(ctx context.Context, obj, o
func (storageVersionStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (storageVersionStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateStorageVersionStatusUpdate(obj.(*apiserverinternal.StorageVersion), old.(*apiserverinternal.StorageVersion)) return validation.ValidateStorageVersionStatusUpdate(obj.(*apiserverinternal.StorageVersion), old.(*apiserverinternal.StorageVersion))
} }
// WarningsOnUpdate returns warnings for the given update.
func (storageVersionStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -65,6 +65,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateControllerRevision(revision) return validation.ValidateControllerRevision(revision)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (strategy) PrepareForUpdate(ctx context.Context, newObj, oldObj runtime.Object) { func (strategy) PrepareForUpdate(ctx context.Context, newObj, oldObj runtime.Object) {
_ = oldObj.(*apps.ControllerRevision) _ = oldObj.(*apps.ControllerRevision)
_ = newObj.(*apps.ControllerRevision) _ = newObj.(*apps.ControllerRevision)
@ -78,3 +81,6 @@ func (strategy) ValidateUpdate(ctx context.Context, newObj, oldObj runtime.Objec
oldRevision, newRevision := oldObj.(*apps.ControllerRevision), newObj.(*apps.ControllerRevision) oldRevision, newRevision := oldObj.(*apps.ControllerRevision), newObj.(*apps.ControllerRevision)
return validation.ValidateControllerRevisionUpdate(newRevision, oldRevision) return validation.ValidateControllerRevisionUpdate(newRevision, oldRevision)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { return nil }

View File

@ -166,6 +166,12 @@ func (daemonSetStrategy) Validate(ctx context.Context, obj runtime.Object) field
return validation.ValidateDaemonSet(daemonSet, opts) return validation.ValidateDaemonSet(daemonSet, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (daemonSetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newDaemonSet := obj.(*apps.DaemonSet)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newDaemonSet.Spec.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (daemonSetStrategy) Canonicalize(obj runtime.Object) { func (daemonSetStrategy) Canonicalize(obj runtime.Object) {
} }
@ -203,6 +209,17 @@ func (daemonSetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return allErrs return allErrs
} }
// WarningsOnUpdate returns warnings for the given update.
func (daemonSetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newDaemonSet := obj.(*apps.DaemonSet)
oldDaemonSet := old.(*apps.DaemonSet)
if newDaemonSet.Spec.TemplateGeneration != oldDaemonSet.Spec.TemplateGeneration {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newDaemonSet.Spec.Template, &oldDaemonSet.Spec.Template)
}
return warnings
}
// AllowUnconditionalUpdate is the default update policy for daemon set objects. // AllowUnconditionalUpdate is the default update policy for daemon set objects.
func (daemonSetStrategy) AllowUnconditionalUpdate() bool { func (daemonSetStrategy) AllowUnconditionalUpdate() bool {
return true return true
@ -234,3 +251,8 @@ func (daemonSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old ru
func (daemonSetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (daemonSetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateDaemonSetStatusUpdate(obj.(*apps.DaemonSet), old.(*apps.DaemonSet)) return validation.ValidateDaemonSetStatusUpdate(obj.(*apps.DaemonSet), old.(*apps.DaemonSet))
} }
// WarningsOnUpdate returns warnings for the given update.
func (daemonSetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -96,6 +96,12 @@ func (deploymentStrategy) Validate(ctx context.Context, obj runtime.Object) fiel
return validation.ValidateDeployment(deployment, opts) return validation.ValidateDeployment(deployment, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (deploymentStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newDeployment := obj.(*apps.Deployment)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newDeployment.Spec.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (deploymentStrategy) Canonicalize(obj runtime.Object) { func (deploymentStrategy) Canonicalize(obj runtime.Object) {
} }
@ -149,6 +155,17 @@ func (deploymentStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.O
return allErrs return allErrs
} }
// WarningsOnUpdate returns warnings for the given update.
func (deploymentStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newDeployment := obj.(*apps.Deployment)
oldDeployment := old.(*apps.Deployment)
if newDeployment.Generation != oldDeployment.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newDeployment.Spec.Template, &oldDeployment.Spec.Template)
}
return warnings
}
func (deploymentStrategy) AllowUnconditionalUpdate() bool { func (deploymentStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -183,3 +200,8 @@ func (deploymentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old r
func (deploymentStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (deploymentStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateDeploymentStatusUpdate(obj.(*apps.Deployment), old.(*apps.Deployment)) return validation.ValidateDeploymentStatusUpdate(obj.(*apps.Deployment), old.(*apps.Deployment))
} }
// WarningsOnUpdate returns warnings for the given update.
func (deploymentStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -125,6 +125,12 @@ func (rsStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorL
return validation.ValidateReplicaSet(rs, opts) return validation.ValidateReplicaSet(rs, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (rsStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newRS := obj.(*apps.ReplicaSet)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newRS.Spec.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (rsStrategy) Canonicalize(obj runtime.Object) { func (rsStrategy) Canonicalize(obj runtime.Object) {
} }
@ -162,6 +168,17 @@ func (rsStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) f
return allErrs return allErrs
} }
// WarningsOnUpdate returns warnings for the given update.
func (rsStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newReplicaSet := obj.(*apps.ReplicaSet)
oldReplicaSet := old.(*apps.ReplicaSet)
if newReplicaSet.Generation != oldReplicaSet.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newReplicaSet.Spec.Template, &oldReplicaSet.Spec.Template)
}
return warnings
}
func (rsStrategy) AllowUnconditionalUpdate() bool { func (rsStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -222,3 +239,8 @@ func (rsStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.O
func (rsStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (rsStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateReplicaSetStatusUpdate(obj.(*apps.ReplicaSet), old.(*apps.ReplicaSet)) return validation.ValidateReplicaSetStatusUpdate(obj.(*apps.ReplicaSet), old.(*apps.ReplicaSet))
} }
// WarningsOnUpdate returns warnings for the given update.
func (rsStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -113,6 +113,12 @@ func (statefulSetStrategy) Validate(ctx context.Context, obj runtime.Object) fie
return validation.ValidateStatefulSet(statefulSet, opts) return validation.ValidateStatefulSet(statefulSet, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (statefulSetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newStatefulSet := obj.(*apps.StatefulSet)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newStatefulSet.Spec.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (statefulSetStrategy) Canonicalize(obj runtime.Object) { func (statefulSetStrategy) Canonicalize(obj runtime.Object) {
} }
@ -133,6 +139,17 @@ func (statefulSetStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.
return append(validationErrorList, updateErrorList...) return append(validationErrorList, updateErrorList...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (statefulSetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newStatefulSet := obj.(*apps.StatefulSet)
oldStatefulSet := old.(*apps.StatefulSet)
if newStatefulSet.Generation != oldStatefulSet.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newStatefulSet.Spec.Template, &oldStatefulSet.Spec.Template)
}
return warnings
}
// AllowUnconditionalUpdate is the default update policy for StatefulSet objects. // AllowUnconditionalUpdate is the default update policy for StatefulSet objects.
func (statefulSetStrategy) AllowUnconditionalUpdate() bool { func (statefulSetStrategy) AllowUnconditionalUpdate() bool {
return true return true
@ -168,3 +185,8 @@ func (statefulSetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old ru
// TODO: Validate status updates. // TODO: Validate status updates.
return validation.ValidateStatefulSetStatusUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet)) return validation.ValidateStatefulSetStatusUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet))
} }
// WarningsOnUpdate returns warnings for the given update.
func (statefulSetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -81,6 +81,11 @@ func (autoscalerStrategy) Validate(ctx context.Context, obj runtime.Object) fiel
return validation.ValidateHorizontalPodAutoscaler(autoscaler) return validation.ValidateHorizontalPodAutoscaler(autoscaler)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (autoscalerStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (autoscalerStrategy) Canonicalize(obj runtime.Object) { func (autoscalerStrategy) Canonicalize(obj runtime.Object) {
} }
@ -123,6 +128,11 @@ func (autoscalerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.O
return validation.ValidateHorizontalPodAutoscalerUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler)) return validation.ValidateHorizontalPodAutoscalerUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler))
} }
// WarningsOnUpdate returns warnings for the given update.
func (autoscalerStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (autoscalerStrategy) AllowUnconditionalUpdate() bool { func (autoscalerStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -162,3 +172,8 @@ func (autoscalerStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old r
func (autoscalerStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (autoscalerStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateHorizontalPodAutoscalerStatusUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler)) return validation.ValidateHorizontalPodAutoscalerStatusUpdate(obj.(*autoscaling.HorizontalPodAutoscaler), old.(*autoscaling.HorizontalPodAutoscaler))
} }
// WarningsOnUpdate returns warnings for the given update.
func (autoscalerStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -20,6 +20,7 @@ import (
"context" "context"
batchv1beta1 "k8s.io/api/batch/v1beta1" batchv1beta1 "k8s.io/api/batch/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
@ -83,6 +84,8 @@ func (cronJobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object)
cronJob := obj.(*batch.CronJob) cronJob := obj.(*batch.CronJob)
cronJob.Status = batch.CronJobStatus{} cronJob.Status = batch.CronJobStatus{}
cronJob.Generation = 1
pod.DropDisabledTemplateFields(&cronJob.Spec.JobTemplate.Spec.Template, nil) pod.DropDisabledTemplateFields(&cronJob.Spec.JobTemplate.Spec.Template, nil)
} }
@ -93,6 +96,12 @@ func (cronJobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
newCronJob.Status = oldCronJob.Status newCronJob.Status = oldCronJob.Status
pod.DropDisabledTemplateFields(&newCronJob.Spec.JobTemplate.Spec.Template, &oldCronJob.Spec.JobTemplate.Spec.Template) pod.DropDisabledTemplateFields(&newCronJob.Spec.JobTemplate.Spec.Template, &oldCronJob.Spec.JobTemplate.Spec.Template)
// Any changes to the spec increment the generation number.
// See metav1.ObjectMeta description for more information on Generation.
if !apiequality.Semantic.DeepEqual(newCronJob.Spec, oldCronJob.Spec) {
newCronJob.Generation = oldCronJob.Generation + 1
}
} }
// Validate validates a new scheduled job. // Validate validates a new scheduled job.
@ -102,6 +111,12 @@ func (cronJobStrategy) Validate(ctx context.Context, obj runtime.Object) field.E
return validation.ValidateCronJob(cronJob, opts) return validation.ValidateCronJob(cronJob, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (cronJobStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newCronJob := obj.(*batch.CronJob)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "jobTemplate", "spec", "template"), &newCronJob.Spec.JobTemplate.Spec.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (cronJobStrategy) Canonicalize(obj runtime.Object) { func (cronJobStrategy) Canonicalize(obj runtime.Object) {
} }
@ -124,6 +139,17 @@ func (cronJobStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Obje
return validation.ValidateCronJobUpdate(newCronJob, oldCronJob, opts) return validation.ValidateCronJobUpdate(newCronJob, oldCronJob, opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (cronJobStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newCronJob := obj.(*batch.CronJob)
oldCronJob := old.(*batch.CronJob)
if newCronJob.Generation != oldCronJob.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "jobTemplate", "spec", "template"), &newCronJob.Spec.JobTemplate.Spec.Template, &oldCronJob.Spec.JobTemplate.Spec.Template)
}
return warnings
}
type cronJobStatusStrategy struct { type cronJobStatusStrategy struct {
cronJobStrategy cronJobStrategy
} }
@ -153,3 +179,8 @@ func (cronJobStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runt
func (cronJobStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (cronJobStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{} return field.ErrorList{}
} }
// WarningsOnUpdate returns warnings for the given update.
func (cronJobStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -44,8 +44,9 @@ func TestCronJobStrategy(t *testing.T) {
} }
cronJob := &batch.CronJob{ cronJob := &batch.CronJob{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob", Name: "mycronjob",
Namespace: metav1.NamespaceDefault, Namespace: metav1.NamespaceDefault,
Generation: 999,
}, },
Spec: batch.CronJobSpec{ Spec: batch.CronJobSpec{
Schedule: "* * * * ?", Schedule: "* * * * ?",
@ -62,11 +63,23 @@ func TestCronJobStrategy(t *testing.T) {
if len(cronJob.Status.Active) != 0 { if len(cronJob.Status.Active) != 0 {
t.Errorf("CronJob does not allow setting status on create") t.Errorf("CronJob does not allow setting status on create")
} }
if cronJob.Generation != 1 {
t.Errorf("expected Generation=1, got %d", cronJob.Generation)
}
errs := Strategy.Validate(ctx, cronJob) errs := Strategy.Validate(ctx, cronJob)
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs) t.Errorf("Unexpected error validating %v", errs)
} }
now := metav1.Now() now := metav1.Now()
// ensure we do not change generation for non-spec updates
updatedLabelCronJob := cronJob.DeepCopy()
updatedLabelCronJob.Labels = map[string]string{"a": "true"}
Strategy.PrepareForUpdate(ctx, updatedLabelCronJob, cronJob)
if updatedLabelCronJob.Generation != 1 {
t.Errorf("expected Generation=1, got %d", updatedLabelCronJob.Generation)
}
updatedCronJob := &batch.CronJob{ updatedCronJob := &batch.CronJob{
ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"},
Spec: batch.CronJobSpec{ Spec: batch.CronJobSpec{
@ -82,6 +95,9 @@ func TestCronJobStrategy(t *testing.T) {
if updatedCronJob.Status.Active != nil { if updatedCronJob.Status.Active != nil {
t.Errorf("PrepareForUpdate should have preserved prior version status") t.Errorf("PrepareForUpdate should have preserved prior version status")
} }
if updatedCronJob.Generation != 2 {
t.Errorf("expected Generation=2, got %d", updatedCronJob.Generation)
}
errs = Strategy.ValidateUpdate(ctx, updatedCronJob, cronJob) errs = Strategy.ValidateUpdate(ctx, updatedCronJob, cronJob)
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("Expected a validation error") t.Errorf("Expected a validation error")

View File

@ -22,6 +22,7 @@ import (
"strconv" "strconv"
batchv1 "k8s.io/api/batch/v1" batchv1 "k8s.io/api/batch/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
@ -89,6 +90,8 @@ func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
job := obj.(*batch.Job) job := obj.(*batch.Job)
job.Status = batch.JobStatus{} job.Status = batch.JobStatus{}
job.Generation = 1
if !utilfeature.DefaultFeatureGate.Enabled(features.TTLAfterFinished) { if !utilfeature.DefaultFeatureGate.Enabled(features.TTLAfterFinished) {
job.Spec.TTLSecondsAfterFinished = nil job.Spec.TTLSecondsAfterFinished = nil
} }
@ -129,6 +132,12 @@ func (jobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
} }
pod.DropDisabledTemplateFields(&newJob.Spec.Template, &oldJob.Spec.Template) pod.DropDisabledTemplateFields(&newJob.Spec.Template, &oldJob.Spec.Template)
// Any changes to the spec increment the generation number.
// See metav1.ObjectMeta description for more information on Generation.
if !apiequality.Semantic.DeepEqual(newJob.Spec, oldJob.Spec) {
newJob.Generation = oldJob.Generation + 1
}
} }
// Validate validates a new job. // Validate validates a new job.
@ -142,6 +151,12 @@ func (jobStrategy) Validate(ctx context.Context, obj runtime.Object) field.Error
return validation.ValidateJob(job, opts) return validation.ValidateJob(job, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (jobStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newJob := obj.(*batch.Job)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newJob.Spec.Template, nil)
}
// generateSelector adds a selector to a job and labels to its template // generateSelector adds a selector to a job and labels to its template
// which can be used to uniquely identify the pods created by that job, // which can be used to uniquely identify the pods created by that job,
// if the user has requested this behavior. // if the user has requested this behavior.
@ -216,6 +231,17 @@ func (jobStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object)
return append(validationErrorList, updateErrorList...) return append(validationErrorList, updateErrorList...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (jobStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
if newJob.Generation != oldJob.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), &newJob.Spec.Template, &oldJob.Spec.Template)
}
return warnings
}
type jobStatusStrategy struct { type jobStatusStrategy struct {
jobStrategy jobStrategy
} }
@ -242,6 +268,11 @@ func (jobStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return validation.ValidateJobUpdateStatus(obj.(*batch.Job), old.(*batch.Job)) return validation.ValidateJobUpdateStatus(obj.(*batch.Job), old.(*batch.Job))
} }
// WarningsOnUpdate returns warnings for the given update.
func (jobStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// JobSelectableFields returns a field set that represents the object for matching purposes. // JobSelectableFields returns a field set that represents the object for matching purposes.
func JobToSelectableFields(job *batch.Job) fields.Set { func JobToSelectableFields(job *batch.Job) fields.Set {
objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&job.ObjectMeta, true) objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&job.ObjectMeta, true)

View File

@ -110,6 +110,9 @@ func testJobStrategy(t *testing.T) {
if job.Status.Active != 0 { if job.Status.Active != 0 {
t.Errorf("Job does not allow setting status on create") t.Errorf("Job does not allow setting status on create")
} }
if job.Generation != 1 {
t.Errorf("expected Generation=1, got %d", job.Generation)
}
errs := Strategy.Validate(ctx, job) errs := Strategy.Validate(ctx, job)
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs) t.Errorf("Unexpected error validating %v", errs)
@ -125,6 +128,15 @@ func testJobStrategy(t *testing.T) {
} }
parallelism := int32(10) parallelism := int32(10)
// ensure we do not change generation for non-spec updates
updatedLabelJob := job.DeepCopy()
updatedLabelJob.Labels = map[string]string{"a": "true"}
Strategy.PrepareForUpdate(ctx, updatedLabelJob, job)
if updatedLabelJob.Generation != 1 {
t.Errorf("expected Generation=1, got %d", updatedLabelJob.Generation)
}
updatedJob := &batch.Job{ updatedJob := &batch.Job{
ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"}, ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"},
Spec: batch.JobSpec{ Spec: batch.JobSpec{
@ -144,6 +156,9 @@ func testJobStrategy(t *testing.T) {
if updatedJob.Status.Active != 10 { if updatedJob.Status.Active != 10 {
t.Errorf("PrepareForUpdate should have preserved prior version status") t.Errorf("PrepareForUpdate should have preserved prior version status")
} }
if updatedJob.Generation != 2 {
t.Errorf("expected Generation=2, got %d", updatedJob.Generation)
}
if ttlEnabled != (updatedJob.Spec.TTLSecondsAfterFinished != nil) { if ttlEnabled != (updatedJob.Spec.TTLSecondsAfterFinished != nil) {
t.Errorf("Job should only allow updating .spec.ttlSecondsAfterFinished when %v feature is enabled", features.TTLAfterFinished) t.Errorf("Job should only allow updating .spec.ttlSecondsAfterFinished when %v feature is enabled", features.TTLAfterFinished)
} }

View File

@ -117,6 +117,9 @@ func (csrStrategy) Validate(ctx context.Context, obj runtime.Object) field.Error
return validation.ValidateCertificateSigningRequestCreate(csr, requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestCreate(csr, requestGroupVersion(ctx))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (csrStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation (which includes a signature check). // Canonicalize normalizes the object after validation (which includes a signature check).
func (csrStrategy) Canonicalize(obj runtime.Object) {} func (csrStrategy) Canonicalize(obj runtime.Object) {}
@ -127,6 +130,11 @@ func (csrStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object)
return validation.ValidateCertificateSigningRequestUpdate(newCSR, oldCSR, requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestUpdate(newCSR, oldCSR, requestGroupVersion(ctx))
} }
// WarningsOnUpdate returns warnings for the given update.
func (csrStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// If AllowUnconditionalUpdate() is true and the object specified by // If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update() // the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the // populates it with the latest version. Else, it checks that the
@ -244,6 +252,11 @@ func (csrStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return validation.ValidateCertificateSigningRequestStatusUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest), requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestStatusUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest), requestGroupVersion(ctx))
} }
// WarningsOnUpdate returns warnings for the given update.
func (csrStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (csrStatusStrategy) Canonicalize(obj runtime.Object) { func (csrStatusStrategy) Canonicalize(obj runtime.Object) {
} }
@ -291,6 +304,11 @@ func (csrApprovalStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.
return validation.ValidateCertificateSigningRequestApprovalUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest), requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestApprovalUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest), requestGroupVersion(ctx))
} }
// WarningsOnUpdate returns warnings for the given update.
func (csrApprovalStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes. // GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
csr, ok := obj.(*certificates.CertificateSigningRequest) csr, ok := obj.(*certificates.CertificateSigningRequest)

View File

@ -55,6 +55,9 @@ func (leaseStrategy) Validate(ctx context.Context, obj runtime.Object) field.Err
return validation.ValidateLease(lease) return validation.ValidateLease(lease)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (leaseStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (leaseStrategy) Canonicalize(obj runtime.Object) { func (leaseStrategy) Canonicalize(obj runtime.Object) {
} }
@ -69,6 +72,11 @@ func (leaseStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object
return validation.ValidateLeaseUpdate(obj.(*coordination.Lease), old.(*coordination.Lease)) return validation.ValidateLeaseUpdate(obj.(*coordination.Lease), old.(*coordination.Lease))
} }
// WarningsOnUpdate returns warnings for the given update.
func (leaseStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for Lease objects. // AllowUnconditionalUpdate is the default update policy for Lease objects.
func (leaseStrategy) AllowUnconditionalUpdate() bool { func (leaseStrategy) AllowUnconditionalUpdate() bool {
return false return false

View File

@ -64,6 +64,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateConfigMap(cfg) return validation.ValidateConfigMap(cfg)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
} }
@ -84,6 +87,9 @@ func (strategy) ValidateUpdate(ctx context.Context, newObj, oldObj runtime.Objec
return validation.ValidateConfigMapUpdate(newCfg, oldCfg) return validation.ValidateConfigMapUpdate(newCfg, oldCfg)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { return nil }
func dropDisabledFields(configMap *api.ConfigMap, oldConfigMap *api.ConfigMap) { func dropDisabledFields(configMap *api.ConfigMap, oldConfigMap *api.ConfigMap) {
} }

View File

@ -55,6 +55,11 @@ func (endpointsStrategy) Validate(ctx context.Context, obj runtime.Object) field
return validation.ValidateEndpointsCreate(obj.(*api.Endpoints)) return validation.ValidateEndpointsCreate(obj.(*api.Endpoints))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (endpointsStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (endpointsStrategy) Canonicalize(obj runtime.Object) { func (endpointsStrategy) Canonicalize(obj runtime.Object) {
} }
@ -69,6 +74,11 @@ func (endpointsStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return validation.ValidateEndpointsUpdate(obj.(*api.Endpoints), old.(*api.Endpoints)) return validation.ValidateEndpointsUpdate(obj.(*api.Endpoints), old.(*api.Endpoints))
} }
// WarningsOnUpdate returns warnings for the given update.
func (endpointsStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (endpointsStrategy) AllowUnconditionalUpdate() bool { func (endpointsStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }

View File

@ -64,6 +64,9 @@ func (eventStrategy) Validate(ctx context.Context, obj runtime.Object) field.Err
return validation.ValidateEventCreate(event, groupVersion) return validation.ValidateEventCreate(event, groupVersion)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (eventStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (eventStrategy) Canonicalize(obj runtime.Object) { func (eventStrategy) Canonicalize(obj runtime.Object) {
} }
@ -79,6 +82,11 @@ func (eventStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object
return validation.ValidateEventUpdate(event, oldEvent, groupVersion) return validation.ValidateEventUpdate(event, oldEvent, groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update.
func (eventStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (eventStrategy) AllowUnconditionalUpdate() bool { func (eventStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }

View File

@ -56,6 +56,11 @@ func (limitrangeStrategy) Validate(ctx context.Context, obj runtime.Object) fiel
return validation.ValidateLimitRange(limitRange) return validation.ValidateLimitRange(limitRange)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (limitrangeStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (limitrangeStrategy) Canonicalize(obj runtime.Object) { func (limitrangeStrategy) Canonicalize(obj runtime.Object) {
} }
@ -69,6 +74,11 @@ func (limitrangeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.O
return validation.ValidateLimitRange(limitRange) return validation.ValidateLimitRange(limitRange)
} }
// WarningsOnUpdate returns warnings for the given update.
func (limitrangeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (limitrangeStrategy) AllowUnconditionalUpdate() bool { func (limitrangeStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }

View File

@ -100,6 +100,11 @@ func (namespaceStrategy) Validate(ctx context.Context, obj runtime.Object) field
return validation.ValidateNamespace(namespace) return validation.ValidateNamespace(namespace)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (namespaceStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (namespaceStrategy) Canonicalize(obj runtime.Object) { func (namespaceStrategy) Canonicalize(obj runtime.Object) {
// Ensure the label matches the name for namespaces just created using GenerateName, // Ensure the label matches the name for namespaces just created using GenerateName,
@ -139,6 +144,11 @@ func (namespaceStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return append(errorList, validation.ValidateNamespaceUpdate(obj.(*api.Namespace), old.(*api.Namespace))...) return append(errorList, validation.ValidateNamespaceUpdate(obj.(*api.Namespace), old.(*api.Namespace))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (namespaceStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (namespaceStrategy) AllowUnconditionalUpdate() bool { func (namespaceStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -169,6 +179,11 @@ func (namespaceStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runt
return validation.ValidateNamespaceStatusUpdate(obj.(*api.Namespace), old.(*api.Namespace)) return validation.ValidateNamespaceStatusUpdate(obj.(*api.Namespace), old.(*api.Namespace))
} }
// WarningsOnUpdate returns warnings for the given update.
func (namespaceStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
type namespaceFinalizeStrategy struct { type namespaceFinalizeStrategy struct {
namespaceStrategy namespaceStrategy
} }
@ -179,6 +194,11 @@ func (namespaceFinalizeStrategy) ValidateUpdate(ctx context.Context, obj, old ru
return validation.ValidateNamespaceFinalizeUpdate(obj.(*api.Namespace), old.(*api.Namespace)) return validation.ValidateNamespaceFinalizeUpdate(obj.(*api.Namespace), old.(*api.Namespace))
} }
// WarningsOnUpdate returns warnings for the given update.
func (namespaceFinalizeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetResetFields returns the set of fields that get reset by the strategy // GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user. // and should not be modified by the user.
func (namespaceFinalizeStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { func (namespaceFinalizeStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {

View File

@ -140,6 +140,9 @@ func (nodeStrategy) Validate(ctx context.Context, obj runtime.Object) field.Erro
return validation.ValidateNode(node) return validation.ValidateNode(node)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (nodeStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (nodeStrategy) Canonicalize(obj runtime.Object) { func (nodeStrategy) Canonicalize(obj runtime.Object) {
} }
@ -150,6 +153,11 @@ func (nodeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object)
return append(errorList, validation.ValidateNodeUpdate(obj.(*api.Node), old.(*api.Node))...) return append(errorList, validation.ValidateNodeUpdate(obj.(*api.Node), old.(*api.Node))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (nodeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (nodeStrategy) AllowUnconditionalUpdate() bool { func (nodeStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -197,6 +205,11 @@ func (nodeStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.O
return validation.ValidateNodeUpdate(obj.(*api.Node), old.(*api.Node)) return validation.ValidateNodeUpdate(obj.(*api.Node), old.(*api.Node))
} }
// WarningsOnUpdate returns warnings for the given update.
func (nodeStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (nodeStatusStrategy) Canonicalize(obj runtime.Object) { func (nodeStatusStrategy) Canonicalize(obj runtime.Object) {
} }

View File

@ -75,6 +75,11 @@ func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object
return append(errorList, volumevalidation.ValidatePersistentVolume(persistentvolume)...) return append(errorList, volumevalidation.ValidatePersistentVolume(persistentvolume)...)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (persistentvolumeStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (persistentvolumeStrategy) Canonicalize(obj runtime.Object) { func (persistentvolumeStrategy) Canonicalize(obj runtime.Object) {
} }
@ -99,6 +104,11 @@ func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old run
return append(errorList, validation.ValidatePersistentVolumeUpdate(newPv, old.(*api.PersistentVolume))...) return append(errorList, validation.ValidatePersistentVolumeUpdate(newPv, old.(*api.PersistentVolume))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (persistentvolumeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (persistentvolumeStrategy) AllowUnconditionalUpdate() bool { func (persistentvolumeStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -132,6 +142,11 @@ func (persistentvolumeStatusStrategy) ValidateUpdate(ctx context.Context, obj, o
return validation.ValidatePersistentVolumeStatusUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume)) return validation.ValidatePersistentVolumeStatusUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume))
} }
// WarningsOnUpdate returns warnings for the given update.
func (persistentvolumeStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes. // GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeObj, ok := obj.(*api.PersistentVolume) persistentvolumeObj, ok := obj.(*api.PersistentVolume)

View File

@ -75,6 +75,11 @@ func (persistentvolumeclaimStrategy) Validate(ctx context.Context, obj runtime.O
return validation.ValidatePersistentVolumeClaim(pvc) return validation.ValidatePersistentVolumeClaim(pvc)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (persistentvolumeclaimStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (persistentvolumeclaimStrategy) Canonicalize(obj runtime.Object) { func (persistentvolumeclaimStrategy) Canonicalize(obj runtime.Object) {
} }
@ -97,6 +102,11 @@ func (persistentvolumeclaimStrategy) ValidateUpdate(ctx context.Context, obj, ol
return append(errorList, validation.ValidatePersistentVolumeClaimUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))...) return append(errorList, validation.ValidatePersistentVolumeClaimUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (persistentvolumeclaimStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (persistentvolumeclaimStrategy) AllowUnconditionalUpdate() bool { func (persistentvolumeclaimStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -133,6 +143,11 @@ func (persistentvolumeclaimStatusStrategy) ValidateUpdate(ctx context.Context, o
return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim)) return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))
} }
// WarningsOnUpdate returns warnings for the given update.
func (persistentvolumeclaimStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes. // GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim) persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim)

View File

@ -107,6 +107,11 @@ func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.Error
return validation.ValidatePodCreate(pod, opts) return validation.ValidatePodCreate(pod, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (podStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return podutil.GetWarningsForPod(ctx, obj.(*api.Pod), nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (podStrategy) Canonicalize(obj runtime.Object) { func (podStrategy) Canonicalize(obj runtime.Object) {
} }
@ -125,6 +130,13 @@ func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object)
return validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts) return validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (podStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
// skip warnings on pod update, since humans don't typically interact directly with pods,
// and we don't want to pay the evaluation cost on what might be a high-frequency update path
return nil
}
// AllowUnconditionalUpdate allows pods to be overwritten // AllowUnconditionalUpdate allows pods to be overwritten
func (podStrategy) AllowUnconditionalUpdate() bool { func (podStrategy) AllowUnconditionalUpdate() bool {
return true return true
@ -198,6 +210,11 @@ func (podStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return validation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts) return validation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (podStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
type podEphemeralContainersStrategy struct { type podEphemeralContainersStrategy struct {
podStrategy podStrategy
} }
@ -231,6 +248,11 @@ func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, o
return validation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts) return validation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (podEphemeralContainersStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes. // GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pod, ok := obj.(*api.Pod) pod, ok := obj.(*api.Pod)

View File

@ -19,6 +19,7 @@ package podtemplate
import ( import (
"context" "context"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
@ -46,7 +47,7 @@ func (podTemplateStrategy) NamespaceScoped() bool {
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. // PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (podTemplateStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (podTemplateStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
template := obj.(*api.PodTemplate) template := obj.(*api.PodTemplate)
template.Generation = 1
pod.DropDisabledTemplateFields(&template.Template, nil) pod.DropDisabledTemplateFields(&template.Template, nil)
} }
@ -57,6 +58,12 @@ func (podTemplateStrategy) Validate(ctx context.Context, obj runtime.Object) fie
return corevalidation.ValidatePodTemplate(template, opts) return corevalidation.ValidatePodTemplate(template, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (podTemplateStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newPodTemplate := obj.(*api.PodTemplate)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("template"), &newPodTemplate.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (podTemplateStrategy) Canonicalize(obj runtime.Object) { func (podTemplateStrategy) Canonicalize(obj runtime.Object) {
} }
@ -72,6 +79,13 @@ func (podTemplateStrategy) PrepareForUpdate(ctx context.Context, obj, old runtim
oldTemplate := old.(*api.PodTemplate) oldTemplate := old.(*api.PodTemplate)
pod.DropDisabledTemplateFields(&newTemplate.Template, &oldTemplate.Template) pod.DropDisabledTemplateFields(&newTemplate.Template, &oldTemplate.Template)
// Any changes to the template increment the generation number.
// See metav1.ObjectMeta description for more information on Generation.
if !apiequality.Semantic.DeepEqual(newTemplate.Template, oldTemplate.Template) {
newTemplate.Generation = oldTemplate.Generation + 1
}
} }
// ValidateUpdate is the default update validation for an end user. // ValidateUpdate is the default update validation for an end user.
@ -84,6 +98,17 @@ func (podTemplateStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.
return corevalidation.ValidatePodTemplateUpdate(template, oldTemplate, opts) return corevalidation.ValidatePodTemplateUpdate(template, oldTemplate, opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (podTemplateStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
newTemplate := obj.(*api.PodTemplate)
oldTemplate := old.(*api.PodTemplate)
if newTemplate.Generation != oldTemplate.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("template"), &newTemplate.Template, &oldTemplate.Template)
}
return warnings
}
func (podTemplateStrategy) AllowUnconditionalUpdate() bool { func (podTemplateStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }

View File

@ -0,0 +1,90 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podtemplate
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("should not allow create on update")
}
podTemplate := &api.PodTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "mytemplate",
Namespace: metav1.NamespaceDefault,
Generation: 999,
},
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
},
},
}
Strategy.PrepareForCreate(ctx, podTemplate)
if podTemplate.Generation != 1 {
t.Errorf("expected Generation=1, got %d", podTemplate.Generation)
}
errs := Strategy.Validate(ctx, podTemplate)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
// ensure we do not change generation for non-spec updates
updatedLabel := podTemplate.DeepCopy()
updatedLabel.Labels = map[string]string{"a": "true"}
Strategy.PrepareForUpdate(ctx, updatedLabel, podTemplate)
if updatedLabel.Generation != 1 {
t.Errorf("expected Generation=1, got %d", updatedLabel.Generation)
}
updatedTemplate := podTemplate.DeepCopy()
updatedTemplate.ResourceVersion = "10"
updatedTemplate.Generation = 999
updatedTemplate.Template.Spec.RestartPolicy = api.RestartPolicyNever
// ensure generation is updated for spec changes
Strategy.PrepareForUpdate(ctx, updatedTemplate, podTemplate)
if updatedTemplate.Generation != 2 {
t.Errorf("expected Generation=2, got %d", updatedTemplate.Generation)
}
errs = Strategy.ValidateUpdate(ctx, updatedTemplate, podTemplate)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
invalidUpdatedTemplate := updatedTemplate.DeepCopy()
invalidUpdatedTemplate.Name = "changed"
Strategy.PrepareForUpdate(ctx, invalidUpdatedTemplate, podTemplate)
errs = Strategy.ValidateUpdate(ctx, invalidUpdatedTemplate, podTemplate)
if len(errs) == 0 {
t.Errorf("expected error validating, got none")
}
}

View File

@ -125,6 +125,12 @@ func (rcStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorL
return validation.ValidateReplicationController(controller, opts) return validation.ValidateReplicationController(controller, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (rcStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
newRC := obj.(*api.ReplicationController)
return pod.GetWarningsForPodTemplate(ctx, field.NewPath("template"), newRC.Spec.Template, nil)
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (rcStrategy) Canonicalize(obj runtime.Object) { func (rcStrategy) Canonicalize(obj runtime.Object) {
} }
@ -165,6 +171,17 @@ func (rcStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) f
return errs return errs
} }
// WarningsOnUpdate returns warnings for the given update.
func (rcStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
var warnings []string
oldRc := old.(*api.ReplicationController)
newRc := obj.(*api.ReplicationController)
if oldRc.Generation != newRc.Generation {
warnings = pod.GetWarningsForPodTemplate(ctx, field.NewPath("spec", "template"), oldRc.Spec.Template, newRc.Spec.Template)
}
return warnings
}
func (rcStrategy) AllowUnconditionalUpdate() bool { func (rcStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -225,3 +242,8 @@ func (rcStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.O
func (rcStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (rcStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateReplicationControllerStatusUpdate(obj.(*api.ReplicationController), old.(*api.ReplicationController)) return validation.ValidateReplicationControllerStatusUpdate(obj.(*api.ReplicationController), old.(*api.ReplicationController))
} }
// WarningsOnUpdate returns warnings for the given update.
func (rcStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -77,6 +77,11 @@ func (resourcequotaStrategy) Validate(ctx context.Context, obj runtime.Object) f
return validation.ValidateResourceQuota(resourcequota, opts) return validation.ValidateResourceQuota(resourcequota, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (resourcequotaStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (resourcequotaStrategy) Canonicalize(obj runtime.Object) { func (resourcequotaStrategy) Canonicalize(obj runtime.Object) {
} }
@ -93,6 +98,11 @@ func (resourcequotaStrategy) ValidateUpdate(ctx context.Context, obj, old runtim
return validation.ValidateResourceQuotaUpdate(newObj, oldObj, opts) return validation.ValidateResourceQuotaUpdate(newObj, oldObj, opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (resourcequotaStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (resourcequotaStrategy) AllowUnconditionalUpdate() bool { func (resourcequotaStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -126,6 +136,11 @@ func (resourcequotaStatusStrategy) ValidateUpdate(ctx context.Context, obj, old
return validation.ValidateResourceQuotaStatusUpdate(obj.(*api.ResourceQuota), old.(*api.ResourceQuota)) return validation.ValidateResourceQuotaStatusUpdate(obj.(*api.ResourceQuota), old.(*api.ResourceQuota))
} }
// WarningsOnUpdate returns warnings for the given update.
func (resourcequotaStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func getValidationOptionsFromResourceQuota(newObj *api.ResourceQuota, oldObj *api.ResourceQuota) validation.ResourceQuotaValidationOptions { func getValidationOptionsFromResourceQuota(newObj *api.ResourceQuota, oldObj *api.ResourceQuota) validation.ResourceQuotaValidationOptions {
opts := validation.ResourceQuotaValidationOptions{ opts := validation.ResourceQuotaValidationOptions{
AllowPodAffinityNamespaceSelector: utilfeature.DefaultFeatureGate.Enabled(features.PodAffinityNamespaceSelector), AllowPodAffinityNamespaceSelector: utilfeature.DefaultFeatureGate.Enabled(features.PodAffinityNamespaceSelector),

View File

@ -60,6 +60,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateSecret(obj.(*api.Secret)) return validation.ValidateSecret(obj.(*api.Secret))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
} }
@ -83,6 +86,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return validation.ValidateSecretUpdate(obj.(*api.Secret), old.(*api.Secret)) return validation.ValidateSecretUpdate(obj.(*api.Secret), old.(*api.Secret))
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func dropDisabledFields(secret *api.Secret, oldSecret *api.Secret) { func dropDisabledFields(secret *api.Secret, oldSecret *api.Secret) {
} }

View File

@ -133,6 +133,9 @@ func (strategy svcStrategy) Validate(ctx context.Context, obj runtime.Object) fi
return allErrs return allErrs
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (svcStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (svcStrategy) Canonicalize(obj runtime.Object) { func (svcStrategy) Canonicalize(obj runtime.Object) {
} }
@ -147,6 +150,11 @@ func (strategy svcStrategy) ValidateUpdate(ctx context.Context, obj, old runtime
return allErrs return allErrs
} }
// WarningsOnUpdate returns warnings for the given update.
func (svcStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (svcStrategy) AllowUnconditionalUpdate() bool { func (svcStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
@ -302,6 +310,11 @@ func (serviceStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtim
return validation.ValidateServiceStatusUpdate(obj.(*api.Service), old.(*api.Service)) return validation.ValidateServiceStatusUpdate(obj.(*api.Service), old.(*api.Service))
} }
// WarningsOnUpdate returns warnings for the given update.
func (serviceStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// NormalizeClusterIPs adjust clusterIPs based on ClusterIP. This must not // NormalizeClusterIPs adjust clusterIPs based on ClusterIP. This must not
// consider any other fields. // consider any other fields.
func NormalizeClusterIPs(oldSvc, newSvc *api.Service) { func NormalizeClusterIPs(oldSvc, newSvc *api.Service) {

View File

@ -49,6 +49,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateServiceAccount(obj.(*api.ServiceAccount)) return validation.ValidateServiceAccount(obj.(*api.ServiceAccount))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
} }
@ -71,6 +74,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return validation.ValidateServiceAccountUpdate(obj.(*api.ServiceAccount), old.(*api.ServiceAccount)) return validation.ValidateServiceAccountUpdate(obj.(*api.ServiceAccount), old.(*api.ServiceAccount))
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (strategy) AllowUnconditionalUpdate() bool { func (strategy) AllowUnconditionalUpdate() bool {
return true return true
} }

View File

@ -87,6 +87,11 @@ func (endpointSliceStrategy) Validate(ctx context.Context, obj runtime.Object) f
return err return err
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (endpointSliceStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (endpointSliceStrategy) Canonicalize(obj runtime.Object) { func (endpointSliceStrategy) Canonicalize(obj runtime.Object) {
} }
@ -103,6 +108,11 @@ func (endpointSliceStrategy) ValidateUpdate(ctx context.Context, new, old runtim
return validation.ValidateEndpointSliceUpdate(newEPS, oldEPS) return validation.ValidateEndpointSliceUpdate(newEPS, oldEPS)
} }
// WarningsOnUpdate returns warnings for the given update.
func (endpointSliceStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for EndpointSlice objects. // AllowUnconditionalUpdate is the default update policy for EndpointSlice objects.
func (endpointSliceStrategy) AllowUnconditionalUpdate() bool { func (endpointSliceStrategy) AllowUnconditionalUpdate() bool {
return true return true

View File

@ -82,6 +82,11 @@ func (flowSchemaStrategy) Validate(ctx context.Context, obj runtime.Object) fiel
return validation.ValidateFlowSchema(obj.(*flowcontrol.FlowSchema)) return validation.ValidateFlowSchema(obj.(*flowcontrol.FlowSchema))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (flowSchemaStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (flowSchemaStrategy) Canonicalize(obj runtime.Object) { func (flowSchemaStrategy) Canonicalize(obj runtime.Object) {
} }
@ -100,6 +105,11 @@ func (flowSchemaStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.O
return validation.ValidateFlowSchemaUpdate(old.(*flowcontrol.FlowSchema), obj.(*flowcontrol.FlowSchema)) return validation.ValidateFlowSchemaUpdate(old.(*flowcontrol.FlowSchema), obj.(*flowcontrol.FlowSchema))
} }
// WarningsOnUpdate returns warnings for the given update.
func (flowSchemaStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
type flowSchemaStatusStrategy struct { type flowSchemaStatusStrategy struct {
flowSchemaStrategy flowSchemaStrategy
} }
@ -139,3 +149,8 @@ func (flowSchemaStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old r
func (flowSchemaStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (flowSchemaStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateFlowSchemaStatusUpdate(old.(*flowcontrol.FlowSchema), obj.(*flowcontrol.FlowSchema)) return validation.ValidateFlowSchemaStatusUpdate(old.(*flowcontrol.FlowSchema), obj.(*flowcontrol.FlowSchema))
} }
// WarningsOnUpdate returns warnings for the given update.
func (flowSchemaStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -82,6 +82,11 @@ func (priorityLevelConfigurationStrategy) Validate(ctx context.Context, obj runt
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration)) return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (priorityLevelConfigurationStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (priorityLevelConfigurationStrategy) Canonicalize(obj runtime.Object) { func (priorityLevelConfigurationStrategy) Canonicalize(obj runtime.Object) {
} }
@ -100,6 +105,11 @@ func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, ob
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration)) return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration))
} }
// WarningsOnUpdate returns warnings for the given update.
func (priorityLevelConfigurationStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
type priorityLevelConfigurationStatusStrategy struct { type priorityLevelConfigurationStatusStrategy struct {
priorityLevelConfigurationStrategy priorityLevelConfigurationStrategy
} }
@ -139,3 +149,8 @@ func (priorityLevelConfigurationStatusStrategy) PrepareForUpdate(ctx context.Con
func (priorityLevelConfigurationStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (priorityLevelConfigurationStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidatePriorityLevelConfigurationStatusUpdate(old.(*flowcontrol.PriorityLevelConfiguration), obj.(*flowcontrol.PriorityLevelConfiguration)) return validation.ValidatePriorityLevelConfigurationStatusUpdate(old.(*flowcontrol.PriorityLevelConfiguration), obj.(*flowcontrol.PriorityLevelConfiguration))
} }
// WarningsOnUpdate returns warnings for the given update.
func (priorityLevelConfigurationStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -98,6 +98,9 @@ func (ingressStrategy) Validate(ctx context.Context, obj runtime.Object) field.E
return validation.ValidateIngressCreate(ingress, requestGV) return validation.ValidateIngressCreate(ingress, requestGV)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (ingressStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (ingressStrategy) Canonicalize(obj runtime.Object) { func (ingressStrategy) Canonicalize(obj runtime.Object) {
} }
@ -116,6 +119,11 @@ func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Obje
return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress), requestGV) return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress), requestGV)
} }
// WarningsOnUpdate returns warnings for the given update.
func (ingressStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for Ingress objects. // AllowUnconditionalUpdate is the default update policy for Ingress objects.
func (ingressStrategy) AllowUnconditionalUpdate() bool { func (ingressStrategy) AllowUnconditionalUpdate() bool {
return true return true
@ -158,3 +166,8 @@ func (ingressStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runt
func (ingressStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (ingressStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateIngressStatusUpdate(obj.(*networking.Ingress), old.(*networking.Ingress)) return validation.ValidateIngressStatusUpdate(obj.(*networking.Ingress), old.(*networking.Ingress))
} }
// WarningsOnUpdate returns warnings for the given update.
func (ingressStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -80,6 +80,11 @@ func (ingressClassStrategy) Validate(ctx context.Context, obj runtime.Object) fi
return validation.ValidateIngressClass(ingressClass) return validation.ValidateIngressClass(ingressClass)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (ingressClassStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (ingressClassStrategy) Canonicalize(obj runtime.Object) { func (ingressClassStrategy) Canonicalize(obj runtime.Object) {
} }
@ -98,6 +103,11 @@ func (ingressClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime
return validation.ValidateIngressClassUpdate(newIngressClass, oldIngressClass) return validation.ValidateIngressClassUpdate(newIngressClass, oldIngressClass)
} }
// WarningsOnUpdate returns warnings for the given update.
func (ingressClassStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for IngressClass // AllowUnconditionalUpdate is the default update policy for IngressClass
// objects. // objects.
func (ingressClassStrategy) AllowUnconditionalUpdate() bool { func (ingressClassStrategy) AllowUnconditionalUpdate() bool {

View File

@ -77,6 +77,11 @@ func (networkPolicyStrategy) Validate(ctx context.Context, obj runtime.Object) f
return validation.ValidateNetworkPolicy(networkPolicy) return validation.ValidateNetworkPolicy(networkPolicy)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (networkPolicyStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (networkPolicyStrategy) Canonicalize(obj runtime.Object) {} func (networkPolicyStrategy) Canonicalize(obj runtime.Object) {}
@ -92,6 +97,11 @@ func (networkPolicyStrategy) ValidateUpdate(ctx context.Context, obj, old runtim
return append(validationErrorList, updateErrorList...) return append(validationErrorList, updateErrorList...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (networkPolicyStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for NetworkPolicy objects. // AllowUnconditionalUpdate is the default update policy for NetworkPolicy objects.
func (networkPolicyStrategy) AllowUnconditionalUpdate() bool { func (networkPolicyStrategy) AllowUnconditionalUpdate() bool {
return true return true

View File

@ -80,6 +80,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateRuntimeClass(runtimeClass) return validation.ValidateRuntimeClass(runtimeClass)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*node.RuntimeClass) _ = obj.(*node.RuntimeClass)
@ -92,6 +95,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return append(errorList, validation.ValidateRuntimeClassUpdate(newObj, old.(*node.RuntimeClass))...) return append(errorList, validation.ValidateRuntimeClassUpdate(newObj, old.(*node.RuntimeClass))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// If AllowUnconditionalUpdate() is true and the object specified by // If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update() // the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the // populates it with the latest version. Else, it checks that the

View File

@ -90,6 +90,11 @@ func (podDisruptionBudgetStrategy) Validate(ctx context.Context, obj runtime.Obj
return validation.ValidatePodDisruptionBudget(podDisruptionBudget) return validation.ValidatePodDisruptionBudget(podDisruptionBudget)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (podDisruptionBudgetStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (podDisruptionBudgetStrategy) Canonicalize(obj runtime.Object) { func (podDisruptionBudgetStrategy) Canonicalize(obj runtime.Object) {
} }
@ -104,6 +109,11 @@ func (podDisruptionBudgetStrategy) ValidateUpdate(ctx context.Context, obj, old
return validation.ValidatePodDisruptionBudget(obj.(*policy.PodDisruptionBudget)) return validation.ValidatePodDisruptionBudget(obj.(*policy.PodDisruptionBudget))
} }
// WarningsOnUpdate returns warnings for the given update.
func (podDisruptionBudgetStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for PodDisruptionBudget objects. Status update should // AllowUnconditionalUpdate is the default update policy for PodDisruptionBudget objects. Status update should
// only be allowed if version match. // only be allowed if version match.
func (podDisruptionBudgetStrategy) AllowUnconditionalUpdate() bool { func (podDisruptionBudgetStrategy) AllowUnconditionalUpdate() bool {
@ -152,3 +162,8 @@ func (podDisruptionBudgetStatusStrategy) ValidateUpdate(ctx context.Context, obj
return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status, return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status,
old.(*policy.PodDisruptionBudget).Status, field.NewPath("status"), apiVersion) old.(*policy.PodDisruptionBudget).Status, field.NewPath("status"), apiVersion)
} }
// WarningsOnUpdate returns warnings for the given update.
func (podDisruptionBudgetStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -81,6 +81,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidatePodSecurityPolicy(obj.(*policy.PodSecurityPolicy), opts) return validation.ValidatePodSecurityPolicy(obj.(*policy.PodSecurityPolicy), opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
opts := validation.PodSecurityPolicyValidationOptions{ opts := validation.PodSecurityPolicyValidationOptions{
// Allowed if the feature is enabled or the old policy already had it. // Allowed if the feature is enabled or the old policy already had it.
@ -91,6 +94,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return validation.ValidatePodSecurityPolicyUpdate(old.(*policy.PodSecurityPolicy), obj.(*policy.PodSecurityPolicy), opts) return validation.ValidatePodSecurityPolicyUpdate(old.(*policy.PodSecurityPolicy), obj.(*policy.PodSecurityPolicy), opts)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func volumeInUse(oldPSP *policy.PodSecurityPolicy, volume policy.FSType) bool { func volumeInUse(oldPSP *policy.PodSecurityPolicy, volume policy.FSType) bool {
if oldPSP == nil { if oldPSP == nil {
return false return false

View File

@ -74,6 +74,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateClusterRole(clusterRole) return validation.ValidateClusterRole(clusterRole)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.ClusterRole) _ = obj.(*rbac.ClusterRole)
@ -86,6 +89,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return append(errorList, validation.ValidateClusterRoleUpdate(newObj, old.(*rbac.ClusterRole))...) return append(errorList, validation.ValidateClusterRoleUpdate(newObj, old.(*rbac.ClusterRole))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// If AllowUnconditionalUpdate() is true and the object specified by // If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update() // the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the // populates it with the latest version. Else, it checks that the

View File

@ -74,6 +74,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateClusterRoleBinding(clusterRoleBinding) return validation.ValidateClusterRoleBinding(clusterRoleBinding)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.ClusterRoleBinding) _ = obj.(*rbac.ClusterRoleBinding)
@ -86,6 +89,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return append(errorList, validation.ValidateClusterRoleBindingUpdate(newObj, old.(*rbac.ClusterRoleBinding))...) return append(errorList, validation.ValidateClusterRoleBindingUpdate(newObj, old.(*rbac.ClusterRoleBinding))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// If AllowUnconditionalUpdate() is true and the object specified by // If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update() // the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the // populates it with the latest version. Else, it checks that the

View File

@ -74,6 +74,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateRole(role) return validation.ValidateRole(role)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.Role) _ = obj.(*rbac.Role)
@ -86,6 +89,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return append(errorList, validation.ValidateRoleUpdate(newObj, old.(*rbac.Role))...) return append(errorList, validation.ValidateRoleUpdate(newObj, old.(*rbac.Role))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// If AllowUnconditionalUpdate() is true and the object specified by // If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update() // the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the // populates it with the latest version. Else, it checks that the

View File

@ -74,6 +74,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateRoleBinding(roleBinding) return validation.ValidateRoleBinding(roleBinding)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) { func (strategy) Canonicalize(obj runtime.Object) {
_ = obj.(*rbac.RoleBinding) _ = obj.(*rbac.RoleBinding)
@ -86,6 +89,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return append(errorList, validation.ValidateRoleBindingUpdate(newObj, old.(*rbac.RoleBinding))...) return append(errorList, validation.ValidateRoleBindingUpdate(newObj, old.(*rbac.RoleBinding))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// If AllowUnconditionalUpdate() is true and the object specified by // If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update() // the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the // populates it with the latest version. Else, it checks that the

View File

@ -63,6 +63,11 @@ func (priorityClassStrategy) Validate(ctx context.Context, obj runtime.Object) f
return validation.ValidatePriorityClass(pc) return validation.ValidatePriorityClass(pc)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (priorityClassStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (priorityClassStrategy) Canonicalize(obj runtime.Object) {} func (priorityClassStrategy) Canonicalize(obj runtime.Object) {}
@ -76,6 +81,11 @@ func (priorityClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtim
return validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass)) return validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass))
} }
// WarningsOnUpdate returns warnings for the given update.
func (priorityClassStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for PriorityClass objects. // AllowUnconditionalUpdate is the default update policy for PriorityClass objects.
func (priorityClassStrategy) AllowUnconditionalUpdate() bool { func (priorityClassStrategy) AllowUnconditionalUpdate() bool {
return true return true

View File

@ -68,6 +68,11 @@ func (csiDriverStrategy) Validate(ctx context.Context, obj runtime.Object) field
return validation.ValidateCSIDriver(csiDriver) return validation.ValidateCSIDriver(csiDriver)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (csiDriverStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (csiDriverStrategy) Canonicalize(obj runtime.Object) { func (csiDriverStrategy) Canonicalize(obj runtime.Object) {
} }
@ -116,6 +121,11 @@ func (csiDriverStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return validation.ValidateCSIDriverUpdate(newCSIDriverObj, oldCSIDriverObj) return validation.ValidateCSIDriverUpdate(newCSIDriverObj, oldCSIDriverObj)
} }
// WarningsOnUpdate returns warnings for the given update.
func (csiDriverStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (csiDriverStrategy) AllowUnconditionalUpdate() bool { func (csiDriverStrategy) AllowUnconditionalUpdate() bool {
return false return false
} }

View File

@ -59,6 +59,9 @@ func (csiNodeStrategy) Validate(ctx context.Context, obj runtime.Object) field.E
return errs return errs
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (csiNodeStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (csiNodeStrategy) Canonicalize(obj runtime.Object) { func (csiNodeStrategy) Canonicalize(obj runtime.Object) {
} }
@ -90,6 +93,11 @@ func (csiNodeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Obje
return append(errorList, validation.ValidateCSINodeUpdate(newCSINodeObj, oldCSINodeObj, validateOptions)...) return append(errorList, validation.ValidateCSINodeUpdate(newCSINodeObj, oldCSINodeObj, validateOptions)...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (csiNodeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (csiNodeStrategy) AllowUnconditionalUpdate() bool { func (csiNodeStrategy) AllowUnconditionalUpdate() bool {
return false return false
} }

View File

@ -54,6 +54,11 @@ func (csiStorageCapacityStrategy) Validate(ctx context.Context, obj runtime.Obje
return errs return errs
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (csiStorageCapacityStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (csiStorageCapacityStrategy) Canonicalize(obj runtime.Object) { func (csiStorageCapacityStrategy) Canonicalize(obj runtime.Object) {
} }
@ -73,6 +78,11 @@ func (csiStorageCapacityStrategy) ValidateUpdate(ctx context.Context, obj, old r
return append(errorList, validation.ValidateCSIStorageCapacityUpdate(newCSIStorageCapacityObj, oldCSIStorageCapacityObj)...) return append(errorList, validation.ValidateCSIStorageCapacityUpdate(newCSIStorageCapacityObj, oldCSIStorageCapacityObj)...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (csiStorageCapacityStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (csiStorageCapacityStrategy) AllowUnconditionalUpdate() bool { func (csiStorageCapacityStrategy) AllowUnconditionalUpdate() bool {
return false return false
} }

View File

@ -54,6 +54,11 @@ func (storageClassStrategy) Validate(ctx context.Context, obj runtime.Object) fi
return validation.ValidateStorageClass(storageClass) return validation.ValidateStorageClass(storageClass)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (storageClassStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (storageClassStrategy) Canonicalize(obj runtime.Object) { func (storageClassStrategy) Canonicalize(obj runtime.Object) {
} }
@ -75,6 +80,11 @@ func (storageClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime
return append(errorList, validation.ValidateStorageClassUpdate(obj.(*storage.StorageClass), old.(*storage.StorageClass))...) return append(errorList, validation.ValidateStorageClassUpdate(obj.(*storage.StorageClass), old.(*storage.StorageClass))...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (storageClassStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (storageClassStrategy) AllowUnconditionalUpdate() bool { func (storageClassStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }

View File

@ -104,6 +104,11 @@ func (volumeAttachmentStrategy) Validate(ctx context.Context, obj runtime.Object
return errs return errs
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (volumeAttachmentStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (volumeAttachmentStrategy) Canonicalize(obj runtime.Object) { func (volumeAttachmentStrategy) Canonicalize(obj runtime.Object) {
} }
@ -143,6 +148,11 @@ func (volumeAttachmentStrategy) ValidateUpdate(ctx context.Context, obj, old run
return append(errorList, validation.ValidateVolumeAttachmentUpdate(newVolumeAttachmentObj, oldVolumeAttachmentObj)...) return append(errorList, validation.ValidateVolumeAttachmentUpdate(newVolumeAttachmentObj, oldVolumeAttachmentObj)...)
} }
// WarningsOnUpdate returns warnings for the given update.
func (volumeAttachmentStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (volumeAttachmentStrategy) AllowUnconditionalUpdate() bool { func (volumeAttachmentStrategy) AllowUnconditionalUpdate() bool {
return false return false
} }

View File

@ -83,3 +83,8 @@ func (a statusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.O
func (a statusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (a statusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return a.customResourceStrategy.validator.ValidateStatusUpdate(ctx, obj, old, a.scale) return a.customResourceStrategy.validator.ValidateStatusUpdate(ctx, obj, old, a.scale)
} }
// WarningsOnUpdate returns warnings for the given update.
func (statusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -161,6 +161,11 @@ func (a customResourceStrategy) Validate(ctx context.Context, obj runtime.Object
return errs return errs
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (customResourceStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
func (customResourceStrategy) Canonicalize(obj runtime.Object) { func (customResourceStrategy) Canonicalize(obj runtime.Object) {
} }
@ -202,6 +207,11 @@ func (a customResourceStrategy) ValidateUpdate(ctx context.Context, obj, old run
return errs return errs
} }
// WarningsOnUpdate returns warnings for the given update.
func (customResourceStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes. // GetAttrs returns labels and fields of a given object for filtering purposes.
func (a customResourceStrategy) GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func (a customResourceStrategy) GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
accessor, err := meta.Accessor(obj) accessor, err := meta.Accessor(obj)

View File

@ -93,6 +93,11 @@ func (a customResourceValidator) ValidateUpdate(ctx context.Context, obj, old ru
return allErrs return allErrs
} }
// WarningsOnUpdate returns warnings for the given update.
func (customResourceValidator) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (a customResourceValidator) ValidateStatusUpdate(ctx context.Context, obj, old runtime.Object, scale *apiextensions.CustomResourceSubresourceScale) field.ErrorList { func (a customResourceValidator) ValidateStatusUpdate(ctx context.Context, obj, old runtime.Object, scale *apiextensions.CustomResourceSubresourceScale) field.ErrorList {
u, ok := obj.(*unstructured.Unstructured) u, ok := obj.(*unstructured.Unstructured)
if !ok { if !ok {

View File

@ -119,6 +119,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition), groupVersion) return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition), groupVersion)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
// AllowCreateOnUpdate is false for CustomResourceDefinition; this means a POST is // AllowCreateOnUpdate is false for CustomResourceDefinition; this means a POST is
// needed to create one. // needed to create one.
func (strategy) AllowCreateOnUpdate() bool { func (strategy) AllowCreateOnUpdate() bool {
@ -144,6 +147,11 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
return validation.ValidateCustomResourceDefinitionUpdate(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition), groupVersion) return validation.ValidateCustomResourceDefinitionUpdate(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition), groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
type statusStrategy struct { type statusStrategy struct {
runtime.ObjectTyper runtime.ObjectTyper
names.NameGenerator names.NameGenerator
@ -198,6 +206,11 @@ func (statusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Objec
return validation.ValidateUpdateCustomResourceDefinitionStatus(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition)) return validation.ValidateUpdateCustomResourceDefinitionStatus(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition))
} }
// WarningsOnUpdate returns warnings for the given update.
func (statusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes. // GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
apiserver, ok := obj.(*apiextensions.CustomResourceDefinition) apiserver, ok := obj.(*apiextensions.CustomResourceDefinition)

View File

@ -115,9 +115,15 @@ func (t *testRESTStrategy) PrepareForUpdate(ctx context.Context, obj, old runtim
func (t *testRESTStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (t *testRESTStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
return nil return nil
} }
func (t *testRESTStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
func (t *testRESTStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (t *testRESTStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return nil return nil
} }
func (t *testRESTStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (t *testRESTStrategy) Canonicalize(obj runtime.Object) {} func (t *testRESTStrategy) Canonicalize(obj runtime.Object) {}
func NewTestGenericStoreRegistry(t *testing.T) (factory.DestroyFunc, *Store) { func NewTestGenericStoreRegistry(t *testing.T) (factory.DestroyFunc, *Store) {

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/warning"
) )
// RESTCreateStrategy defines the minimum validation, accepted input, and // RESTCreateStrategy defines the minimum validation, accepted input, and
@ -59,6 +60,26 @@ type RESTCreateStrategy interface {
// before the object is persisted. This method should not mutate the // before the object is persisted. This method should not mutate the
// object. // object.
Validate(ctx context.Context, obj runtime.Object) field.ErrorList Validate(ctx context.Context, obj runtime.Object) field.ErrorList
// WarningsOnCreate returns warnings to the client performing a create.
// WarningsOnCreate is invoked after default fields in the object have been filled in
// and after Validate has passed, before Canonicalize is called, and the object is persisted.
// This method must not mutate the object.
//
// Be brief; limit warnings to 120 characters if possible.
// Don't include a "Warning:" prefix in the message (that is added by clients on output).
// Warnings returned about a specific field should be formatted as "path.to.field: message".
// For example: `spec.imagePullSecrets[0].name: invalid empty name ""`
//
// Use warning messages to describe problems the client making the API request should correct or be aware of.
// For example:
// - use of deprecated fields/labels/annotations that will stop working in a future release
// - use of obsolete fields/labels/annotations that are non-functional
// - malformed or invalid specifications that prevent successful handling of the submitted object,
// but are not rejected by validation for compatibility reasons
//
// Warnings should not be returned for fields which cannot be resolved by the caller.
// For example, do not warn about spec fields in a subresource creation request.
WarningsOnCreate(ctx context.Context, obj runtime.Object) []string
// Canonicalize allows an object to be mutated into a canonical form. This // Canonicalize allows an object to be mutated into a canonical form. This
// ensures that code that operates on these objects can rely on the common // ensures that code that operates on these objects can rely on the common
// form for things like comparison. Canonicalize is invoked after // form for things like comparison. Canonicalize is invoked after
@ -113,6 +134,10 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime.
return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs) return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs)
} }
for _, w := range strategy.WarningsOnCreate(ctx, obj) {
warning.AddWarning(ctx, "", w)
}
strategy.Canonicalize(obj) strategy.Canonicalize(obj)
return nil return nil

View File

@ -39,6 +39,26 @@ type RESTCreateUpdateStrategy interface {
// filled in before the object is persisted. This method should not mutate // filled in before the object is persisted. This method should not mutate
// the object. // the object.
ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList
// WarningsOnUpdate returns warnings to the client performing the update.
// WarningsOnUpdate is invoked after default fields in the object have been filled in
// and after ValidateUpdate has passed, before Canonicalize is called, and before the object is persisted.
// This method must not mutate either object.
//
// Be brief; limit warnings to 120 characters if possible.
// Don't include a "Warning:" prefix in the message (that is added by clients on output).
// Warnings returned about a specific field should be formatted as "path.to.field: message".
// For example: `spec.imagePullSecrets[0].name: invalid empty name ""`
//
// Use warning messages to describe problems the client making the API request should correct or be aware of.
// For example:
// - use of deprecated fields/labels/annotations that will stop working in a future release
// - use of obsolete fields/labels/annotations that are non-functional
// - malformed or invalid specifications that prevent successful handling of the submitted object,
// but are not rejected by validation for compatibility reasons
//
// Warnings should not be returned for fields which cannot be resolved by the caller.
// For example, do not warn about spec fields in a status update.
WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string
// AllowUnconditionalUpdate returns true if the object can be updated // AllowUnconditionalUpdate returns true if the object can be updated
// unconditionally (irrespective of the latest resource version), when // unconditionally (irrespective of the latest resource version), when
// there is no resource version specified in the object. // there is no resource version specified in the object.

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/warning"
) )
// RESTUpdateStrategy defines the minimum validation, accepted input, and // RESTUpdateStrategy defines the minimum validation, accepted input, and
@ -51,6 +52,26 @@ type RESTUpdateStrategy interface {
// filled in before the object is persisted. This method should not mutate // filled in before the object is persisted. This method should not mutate
// the object. // the object.
ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList
// WarningsOnUpdate returns warnings to the client performing the update.
// WarningsOnUpdate is invoked after default fields in the object have been filled in
// and after ValidateUpdate has passed, before Canonicalize is called, and before the object is persisted.
// This method must not mutate either object.
//
// Be brief; limit warnings to 120 characters if possible.
// Don't include a "Warning:" prefix in the message (that is added by clients on output).
// Warnings returned about a specific field should be formatted as "path.to.field: message".
// For example: `spec.imagePullSecrets[0].name: invalid empty name ""`
//
// Use warning messages to describe problems the client making the API request should correct or be aware of.
// For example:
// - use of deprecated fields/labels/annotations that will stop working in a future release
// - use of obsolete fields/labels/annotations that are non-functional
// - malformed or invalid specifications that prevent successful handling of the submitted object,
// but are not rejected by validation for compatibility reasons
//
// Warnings should not be returned for fields which cannot be resolved by the caller.
// For example, do not warn about spec fields in a status update.
WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string
// Canonicalize allows an object to be mutated into a canonical form. This // Canonicalize allows an object to be mutated into a canonical form. This
// ensures that code that operates on these objects can rely on the common // ensures that code that operates on these objects can rely on the common
// form for things like comparison. Canonicalize is invoked after // form for things like comparison. Canonicalize is invoked after
@ -144,6 +165,10 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx context.Context, obj, old run
return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs) return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs)
} }
for _, w := range strategy.WarningsOnUpdate(ctx, obj, old) {
warning.AddWarning(ctx, "", w)
}
strategy.Canonicalize(obj) strategy.Canonicalize(obj)
return nil return nil

View File

@ -85,6 +85,11 @@ func (apiServerStrategy) Validate(ctx context.Context, obj runtime.Object) field
return validation.ValidateAPIService(obj.(*apiregistration.APIService)) return validation.ValidateAPIService(obj.(*apiregistration.APIService))
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (apiServerStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
func (apiServerStrategy) AllowCreateOnUpdate() bool { func (apiServerStrategy) AllowCreateOnUpdate() bool {
return false return false
} }
@ -100,6 +105,11 @@ func (apiServerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
return validation.ValidateAPIServiceUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService)) return validation.ValidateAPIServiceUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService))
} }
// WarningsOnUpdate returns warnings for the given update.
func (apiServerStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
type apiServerStatusStrategy struct { type apiServerStatusStrategy struct {
runtime.ObjectTyper runtime.ObjectTyper
names.NameGenerator names.NameGenerator
@ -156,6 +166,11 @@ func (apiServerStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runt
return validation.ValidateAPIServiceStatusUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService)) return validation.ValidateAPIServiceStatusUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService))
} }
// WarningsOnUpdate returns warnings for the given update.
func (apiServerStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// GetAttrs returns the labels and fields of an API server for filtering purposes. // GetAttrs returns the labels and fields of an API server for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
apiserver, ok := obj.(*apiregistration.APIService) apiserver, ok := obj.(*apiregistration.APIService)

View File

@ -79,6 +79,9 @@ func (fischerStrategy) Validate(ctx context.Context, obj runtime.Object) field.E
return field.ErrorList{} return field.ErrorList{}
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (fischerStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (fischerStrategy) AllowCreateOnUpdate() bool { func (fischerStrategy) AllowCreateOnUpdate() bool {
return false return false
} }
@ -93,3 +96,8 @@ func (fischerStrategy) Canonicalize(obj runtime.Object) {
func (fischerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (fischerStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{} return field.ErrorList{}
} }
// WarningsOnUpdate returns warnings for the given update.
func (fischerStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View File

@ -81,6 +81,9 @@ func (flunderStrategy) Validate(ctx context.Context, obj runtime.Object) field.E
return validation.ValidateFlunder(flunder) return validation.ValidateFlunder(flunder)
} }
// WarningsOnCreate returns warnings for the creation of the given object.
func (flunderStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (flunderStrategy) AllowCreateOnUpdate() bool { func (flunderStrategy) AllowCreateOnUpdate() bool {
return false return false
} }
@ -95,3 +98,8 @@ func (flunderStrategy) Canonicalize(obj runtime.Object) {
func (flunderStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (flunderStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{} return field.ErrorList{}
} }
// WarningsOnUpdate returns warnings for the given update.
func (flunderStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}