Merge pull request #108554 from pacoxu/bad-input-1

add deprecated warning for node beta labels in pv/sc/rc/csi storage capacity
This commit is contained in:
Kubernetes Prow Robot 2022-08-02 10:23:44 -07:00 committed by GitHub
commit 9ef16e7908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 529 additions and 51 deletions

108
pkg/api/node/util.go Normal file
View File

@ -0,0 +1,108 @@
/*
Copyright 2022 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 node
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/node"
)
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`,
}
// GetNodeLabelDeprecatedMessage returns the message for the deprecated node label
// and a bool indicating if the label is deprecated.
func GetNodeLabelDeprecatedMessage(key string) (string, bool) {
msg, ok := deprecatedNodeLabels[key]
return msg, ok
}
func GetWarningsForRuntimeClass(rc *node.RuntimeClass) []string {
var warnings []string
if rc != nil && rc.Scheduling != nil && rc.Scheduling.NodeSelector != nil {
// use of deprecated node labels in scheduling's node affinity
for key := range rc.Scheduling.NodeSelector {
if msg, deprecated := GetNodeLabelDeprecatedMessage(key); deprecated {
warnings = append(warnings, fmt.Sprintf("%s: %s", field.NewPath("scheduling", "nodeSelector"), msg))
}
}
}
return warnings
}
// GetWarningsForNodeSelector tests if any of the node selector requirements in the template is deprecated.
// If there are deprecated node selector requirements in either match expressions or match labels, a warning is returned.
func GetWarningsForNodeSelector(nodeSelector *metav1.LabelSelector, fieldPath *field.Path) []string {
if nodeSelector == nil {
return nil
}
var warnings []string
// use of deprecated node labels in matchLabelExpressions
for i, expression := range nodeSelector.MatchExpressions {
if msg, deprecated := GetNodeLabelDeprecatedMessage(expression.Key); deprecated {
warnings = append(
warnings,
fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("matchExpressions").Index(i).Child("key"),
expression.Key,
msg,
),
)
}
}
// use of deprecated node labels in matchLabels
for label := range nodeSelector.MatchLabels {
if msg, deprecated := GetNodeLabelDeprecatedMessage(label); deprecated {
warnings = append(warnings, fmt.Sprintf("%s: %s", fieldPath.Child("matchLabels").Child(label), msg))
}
}
return warnings
}
// GetWarningsForNodeSelectorTerm checks match expressions of node selector term
func GetWarningsForNodeSelectorTerm(nodeSelectorTerm api.NodeSelectorTerm, fieldPath *field.Path) []string {
var warnings []string
// use of deprecated node labels in matchLabelExpressions
for i, expression := range nodeSelectorTerm.MatchExpressions {
if msg, deprecated := GetNodeLabelDeprecatedMessage(expression.Key); deprecated {
warnings = append(
warnings,
fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("matchExpressions").Index(i).Child("key"),
expression.Key,
msg,
),
)
}
}
return warnings
}

81
pkg/api/node/util_test.go Normal file
View File

@ -0,0 +1,81 @@
/*
Copyright 2022 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 node
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/apis/node"
)
func TestWarnings(t *testing.T) {
testcases := []struct {
name string
template *node.RuntimeClass
expected []string
}{
{
name: "null",
template: nil,
expected: nil,
},
{
name: "no warning",
template: &node.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
expected: nil,
},
{
name: "warning",
template: &node.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Scheduling: &node.Scheduling{
NodeSelector: map[string]string{
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
},
},
},
expected: []string{
`scheduling.nodeSelector: deprecated since v1.14; use "kubernetes.io/arch" instead`,
`scheduling.nodeSelector: deprecated since v1.14; use "kubernetes.io/os" instead`,
},
},
}
for _, tc := range testcases {
t.Run("podspec_"+tc.name, func(t *testing.T) {
actual := sets.NewString(GetWarningsForRuntimeClass(tc.template)...)
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

@ -17,7 +17,9 @@ limitations under the License.
package persistentvolume
import (
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
nodeapi "k8s.io/kubernetes/pkg/api/node"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
)
@ -42,3 +44,24 @@ func hasNodeExpansionSecrets(oldPVSpec *api.PersistentVolumeSpec) bool {
}
return false
}
func GetWarningsForPersistentVolume(pv *api.PersistentVolume) []string {
if pv == nil {
return nil
}
return warningsForPersistentVolumeSpecAndMeta(nil, &pv.Spec)
}
func warningsForPersistentVolumeSpecAndMeta(fieldPath *field.Path, pvSpec *api.PersistentVolumeSpec) []string {
var warnings []string
if pvSpec.NodeAffinity != nil && pvSpec.NodeAffinity.Required != nil {
termFldPath := fieldPath.Child("spec", "nodeAffinity", "required", "nodeSelectorTerms")
// use of deprecated node labels in node affinity
for i, term := range pvSpec.NodeAffinity.Required.NodeSelectorTerms {
warnings = append(warnings, nodeapi.GetWarningsForNodeSelectorTerm(term, termFldPath.Index(i))...)
}
}
return warnings
}

View File

@ -22,6 +22,8 @@ import (
"github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core"
@ -108,3 +110,74 @@ func specWithCSISecrets(secret *api.SecretReference) *api.PersistentVolumeSpec {
}
return pvSpec
}
func TestWarnings(t *testing.T) {
testcases := []struct {
name string
template *api.PersistentVolume
expected []string
}{
{
name: "null",
template: nil,
expected: nil,
},
{
name: "no warning",
template: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
},
},
expected: nil,
},
{
name: "warning",
template: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PersistentVolumeSpec{
NodeAffinity: &api.VolumeNodeAffinity{
Required: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "beta.kubernetes.io/os",
Operator: "Equal",
Values: []string{"windows"},
},
},
},
},
},
},
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
},
},
expected: []string{
`spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`,
},
},
}
for _, tc := range testcases {
t.Run("podspec_"+tc.name, func(t *testing.T) {
actual := sets.NewString(GetWarningsForPersistentVolume(tc.template)...)
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

@ -24,6 +24,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
nodeapi "k8s.io/kubernetes/pkg/api/node"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/pods"
)
@ -60,14 +61,6 @@ func GetWarningsForPodTemplate(ctx context.Context, fieldPath *field.Path, podTe
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
@ -92,52 +85,25 @@ func warningsForPodSpecAndMeta(fieldPath *field.Path, podSpec *api.PodSpec, meta
// use of deprecated node labels in selectors/affinity/topology
for k := range podSpec.NodeSelector {
if msg, deprecated := deprecatedNodeLabels[k]; deprecated {
if msg, deprecated := nodeapi.GetNodeLabelDeprecatedMessage(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,
),
)
termFldPath := fieldPath.Child("spec", "affinity", "nodeAffinity", "requiredDuringSchedulingIgnoredDuringExecution", "nodeSelectorTerms")
for i, term := range n.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
warnings = append(warnings, nodeapi.GetWarningsForNodeSelectorTerm(term, termFldPath.Index(i))...)
}
}
preferredFldPath := fieldPath.Child("spec", "affinity", "nodeAffinity", "preferredDuringSchedulingIgnoredDuringExecution")
for i, term := range n.PreferredDuringSchedulingIgnoredDuringExecution {
warnings = append(warnings, nodeapi.GetWarningsForNodeSelectorTerm(term.Preference, preferredFldPath.Index(i).Child("preference"))...)
}
}
for i, t := range podSpec.TopologySpreadConstraints {
if msg, deprecated := deprecatedNodeLabels[t.TopologyKey]; deprecated {
if msg, deprecated := nodeapi.GetNodeLabelDeprecatedMessage(t.TopologyKey); deprecated {
warnings = append(warnings, fmt.Sprintf(
"%s: %s is %s",
fieldPath.Child("spec", "topologySpreadConstraints").Index(i).Child("topologyKey"),

49
pkg/api/storage/util.go Normal file
View File

@ -0,0 +1,49 @@
/*
Copyright 2022 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 storage
import (
"fmt"
"k8s.io/apimachinery/pkg/util/validation/field"
nodeapi "k8s.io/kubernetes/pkg/api/node"
"k8s.io/kubernetes/pkg/apis/storage"
)
func GetWarningsForStorageClass(sc *storage.StorageClass) []string {
var warnings []string
if sc != nil && sc.AllowedTopologies != nil {
// use of deprecated node labels in allowedTopologies's matchLabelExpressions
for i, topo := range sc.AllowedTopologies {
for j, expression := range topo.MatchLabelExpressions {
if msg, deprecated := nodeapi.GetNodeLabelDeprecatedMessage(expression.Key); deprecated {
warnings = append(warnings, fmt.Sprintf("%s: %s", field.NewPath("allowedTopologies").Index(i).Child("matchLabelExpressions").Index(j).Child("key"), msg))
}
}
}
}
return warnings
}
func GetWarningsForCSIStorageCapacity(csc *storage.CSIStorageCapacity) []string {
if csc != nil {
return nodeapi.GetWarningsForNodeSelector(csc.NodeTopology, field.NewPath("nodeTopology"))
}
return nil
}

View File

@ -0,0 +1,171 @@
/*
Copyright 2022 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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/storage"
)
func TestStorageClassWarnings(t *testing.T) {
testcases := []struct {
name string
template *storage.StorageClass
expected []string
}{
{
name: "null",
template: nil,
expected: nil,
},
{
name: "no warning",
template: &storage.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
expected: nil,
},
{
name: "warning",
template: &storage.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
AllowedTopologies: []core.TopologySelectorTerm{
{
MatchLabelExpressions: []core.TopologySelectorLabelRequirement{
{
Key: "beta.kubernetes.io/arch",
Values: []string{"amd64"},
},
{
Key: "beta.kubernetes.io/os",
Values: []string{"linux"},
},
},
},
},
},
expected: []string{
`allowedTopologies[0].matchLabelExpressions[0].key: deprecated since v1.14; use "kubernetes.io/arch" instead`,
`allowedTopologies[0].matchLabelExpressions[1].key: deprecated since v1.14; use "kubernetes.io/os" instead`,
},
},
}
for _, tc := range testcases {
t.Run("podspec_"+tc.name, func(t *testing.T) {
actual := sets.NewString(GetWarningsForStorageClass(tc.template)...)
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)
}
})
}
}
func TestCSIStorageCapacityWarnings(t *testing.T) {
testcases := []struct {
name string
template *storage.CSIStorageCapacity
expected []string
}{
{
name: "null",
template: nil,
expected: nil,
},
{
name: "no warning",
template: &storage.CSIStorageCapacity{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
expected: nil,
},
{
name: "MatchLabels warning",
template: &storage.CSIStorageCapacity{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
NodeTopology: &metav1.LabelSelector{
MatchLabels: map[string]string{
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
},
},
},
expected: []string{
`nodeTopology.matchLabels.beta.kubernetes.io/arch: deprecated since v1.14; use "kubernetes.io/arch" instead`,
`nodeTopology.matchLabels.beta.kubernetes.io/os: deprecated since v1.14; use "kubernetes.io/os" instead`,
},
},
{
name: "MatchExpressions warning",
template: &storage.CSIStorageCapacity{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
NodeTopology: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "beta.kubernetes.io/arch",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"amd64"},
},
{
Key: "beta.kubernetes.io/os",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"linux"},
},
},
},
},
expected: []string{
`nodeTopology.matchExpressions[0].key: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`,
`nodeTopology.matchExpressions[1].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`,
},
},
}
for _, tc := range testcases {
t.Run("podspec_"+tc.name, func(t *testing.T) {
actual := sets.NewString(GetWarningsForCSIStorageCapacity(tc.template)...)
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

@ -78,7 +78,7 @@ func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object
// WarningsOnCreate returns warnings for the creation of the given object.
func (persistentvolumeStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
return pvutil.GetWarningsForPersistentVolume(obj.(*api.PersistentVolume))
}
// Canonicalize normalizes the object after validation.
@ -109,7 +109,7 @@ func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old run
// WarningsOnUpdate returns warnings for the given update.
func (persistentvolumeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
return pvutil.GetWarningsForPersistentVolume(obj.(*api.PersistentVolume))
}
func (persistentvolumeStrategy) AllowUnconditionalUpdate() bool {

View File

@ -24,6 +24,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
nodeapi "k8s.io/kubernetes/pkg/api/node"
"k8s.io/kubernetes/pkg/apis/node"
"k8s.io/kubernetes/pkg/apis/node/validation"
)
@ -73,7 +74,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
}
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nodeapi.GetWarningsForRuntimeClass(obj.(*node.RuntimeClass))
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
@ -89,7 +92,7 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
return nodeapi.GetWarningsForRuntimeClass(obj.(*node.RuntimeClass))
}
// If AllowUnconditionalUpdate() is true and the object specified by

View File

@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
storageutil "k8s.io/kubernetes/pkg/api/storage"
"k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/apis/storage/validation"
)
@ -56,7 +57,7 @@ func (csiStorageCapacityStrategy) Validate(ctx context.Context, obj runtime.Obje
// WarningsOnCreate returns warnings for the creation of the given object.
func (csiStorageCapacityStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
return storageutil.GetWarningsForCSIStorageCapacity(obj.(*storage.CSIStorageCapacity))
}
// Canonicalize normalizes the object after validation.
@ -80,7 +81,7 @@ func (csiStorageCapacityStrategy) ValidateUpdate(ctx context.Context, obj, old r
// WarningsOnUpdate returns warnings for the given update.
func (csiStorageCapacityStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
return storageutil.GetWarningsForCSIStorageCapacity(obj.(*storage.CSIStorageCapacity))
}
func (csiStorageCapacityStrategy) AllowUnconditionalUpdate() bool {

View File

@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
storageutil "k8s.io/kubernetes/pkg/api/storage"
"k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/apis/storage/validation"
)
@ -52,7 +53,7 @@ func (storageClassStrategy) Validate(ctx context.Context, obj runtime.Object) fi
// WarningsOnCreate returns warnings for the creation of the given object.
func (storageClassStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
return storageutil.GetWarningsForStorageClass(obj.(*storage.StorageClass))
}
// Canonicalize normalizes the object after validation.
@ -74,7 +75,7 @@ func (storageClassStrategy) ValidateUpdate(ctx context.Context, obj, old runtime
// WarningsOnUpdate returns warnings for the given update.
func (storageClassStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
return storageutil.GetWarningsForStorageClass(obj.(*storage.StorageClass))
}
func (storageClassStrategy) AllowUnconditionalUpdate() bool {

View File

@ -7,6 +7,7 @@ rules:
- k8s.io/kubernetes/pkg/api/v1/resource
- k8s.io/kubernetes/pkg/api/v1/service
- k8s.io/kubernetes/pkg/api/pod
- k8s.io/kubernetes/pkg/api/node
- k8s.io/kubernetes/pkg/apis/apps
- k8s.io/kubernetes/pkg/apis/apps/validation
- k8s.io/kubernetes/pkg/apis/autoscaling
@ -23,6 +24,7 @@ rules:
- k8s.io/kubernetes/pkg/apis/core/validation
- k8s.io/kubernetes/pkg/apis/extensions
- k8s.io/kubernetes/pkg/apis/networking
- k8s.io/kubernetes/pkg/apis/node
- k8s.io/kubernetes/pkg/apis/policy
- k8s.io/kubernetes/pkg/apis/policy/validation
- k8s.io/kubernetes/pkg/apis/scheduling