Created
January 18, 2025 22:27
-
-
Save aaron-prindle/b106b24f74770218b3e84005ddeb1bca to your computer and use it in GitHub Desktop.
Declarative Validation: Example of migration changes for pkg/apis/core/validation/validation_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go | |
index 295f8425eab..8a9fdb68661 100644 | |
--- a/pkg/apis/core/validation/validation_test.go | |
+++ b/pkg/apis/core/validation/validation_test.go | |
@@ -1,27 +1,32 @@ | |
/* | |
-Copyright 2014 The Kubernetes Authors. | |
+ Copyright 2014 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 | |
+ 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 | |
+ 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. | |
+ 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 validation | |
import ( | |
"bytes" | |
+ "context" | |
"fmt" | |
"math" | |
+ "math/rand" | |
"reflect" | |
+ "regexp" | |
"runtime" | |
+ "sort" | |
+ "strconv" | |
"strings" | |
"testing" | |
"time" | |
@@ -31,21 +36,31 @@ import ( | |
"github.com/stretchr/testify/assert" | |
"github.com/stretchr/testify/require" | |
"google.golang.org/protobuf/proto" | |
- | |
v1 "k8s.io/api/core/v1" | |
+ "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" | |
+ "k8s.io/apimachinery/pkg/api/apitesting/roundtrip" | |
"k8s.io/apimachinery/pkg/api/resource" | |
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | |
+ k8sruntime "k8s.io/apimachinery/pkg/runtime" | |
+ "k8s.io/apimachinery/pkg/runtime/schema" | |
+ runtimetest "k8s.io/apimachinery/pkg/runtime/testing" | |
"k8s.io/apimachinery/pkg/util/intstr" | |
"k8s.io/apimachinery/pkg/util/sets" | |
"k8s.io/apimachinery/pkg/util/validation" | |
"k8s.io/apimachinery/pkg/util/validation/field" | |
"k8s.io/apimachinery/pkg/util/version" | |
+ "k8s.io/apiserver/pkg/endpoints/request" | |
+ "k8s.io/apiserver/pkg/registry/rest" | |
utilfeature "k8s.io/apiserver/pkg/util/feature" | |
"k8s.io/component-base/featuregate" | |
featuregatetesting "k8s.io/component-base/featuregate/testing" | |
kubeletapis "k8s.io/kubelet/pkg/apis" | |
+ "k8s.io/kubernetes/pkg/api/legacyscheme" | |
podtest "k8s.io/kubernetes/pkg/api/pod/testing" | |
+ apitest "k8s.io/kubernetes/pkg/api/testing" | |
"k8s.io/kubernetes/pkg/apis/core" | |
+ _ "k8s.io/kubernetes/pkg/apis/core/install" // register types with scheme for GVK discovery | |
+ v1util "k8s.io/kubernetes/pkg/apis/core/v1" | |
"k8s.io/kubernetes/pkg/capabilities" | |
"k8s.io/kubernetes/pkg/features" | |
utilpointer "k8s.io/utils/pointer" | |
@@ -53,7 +68,7 @@ import ( | |
) | |
const ( | |
- dnsLabelErrMsg = "must consist of lower-case alphanumeric characters or '-'" | |
+ dnsLabelErrMsg = "must contain only lower-case alphanumeric characters or '-'" | |
dnsSubdomainLabelErrMsg = "a lowercase RFC 1123 subdomain" | |
envVarNameErrMsg = "a valid environment variable name must consist of" | |
relaxedEnvVarNameFmtErrMsg string = "a valid environment variable name must consist only of printable ASCII characters other than '='" | |
@@ -3770,7 +3785,7 @@ func TestValidateVolumes(t *testing.T) { | |
errs: []verr{{ | |
etype: field.ErrorTypeInvalid, | |
field: "name", | |
- detail: "must consist of lower-case alphanumeric characters or '-'", | |
+ detail: "must contain only lower-case alphanumeric characters or '-'", | |
}}, | |
}, { | |
name: "name not a DNS label", | |
@@ -12280,7 +12295,7 @@ func TestValidatePod(t *testing.T) { | |
), | |
}, | |
"final PVC name for ephemeral volume must be valid": { | |
- expectedError: "spec.volumes[1].name: Invalid value: \"" + longVolName + "\": PVC name \"" + longPodName + "-" + longVolName + "\": must be no more than 253 characters", | |
+ expectedError: "spec.volumes[1].name: Invalid value: \"" + longVolName + "\": PVC name \"" + longPodName + "-" + longVolName + "\": must be no more than 253 bytes", | |
spec: *podtest.MakePod(longPodName, | |
podtest.SetVolumes( | |
core.Volume{Name: "pvc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "my-pvc"}}}, | |
@@ -16529,7 +16544,7 @@ func TestValidateReplicationControllerStatusUpdate(t *testing.T) { | |
update: core.ReplicationController{ | |
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
Spec: core.ReplicationControllerSpec{ | |
- Replicas: 3, | |
+ Replicas: ptr.To[int32](3), | |
Selector: validSelector, | |
Template: &validPodTemplate.Template, | |
}, | |
@@ -16561,7 +16576,7 @@ func TestValidateReplicationControllerStatusUpdate(t *testing.T) { | |
update: core.ReplicationController{ | |
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
Spec: core.ReplicationControllerSpec{ | |
- Replicas: 2, | |
+ Replicas: ptr.To[int32](2), | |
Selector: validSelector, | |
Template: &validPodTemplate.Template, | |
}, | |
@@ -16580,391 +16595,488 @@ func TestValidateReplicationControllerStatusUpdate(t *testing.T) { | |
} | |
-func TestValidateReplicationControllerUpdate(t *testing.T) { | |
- validSelector := map[string]string{"a": "b"} | |
- validPodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- Spec: podtest.MakePodSpec(), | |
- }, | |
- } | |
- readWriteVolumePodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
+// Helper function for RC tests. | |
+func mkValidReplicationController(tweaks ...func(rc *core.ReplicationController)) core.ReplicationController { | |
+ rc := core.ReplicationController{ | |
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
+ Spec: core.ReplicationControllerSpec{ | |
+ Replicas: ptr.To[int32](1), | |
+ Selector: map[string]string{"a": "b"}, | |
+ Template: &core.PodTemplateSpec{ | |
+ ObjectMeta: metav1.ObjectMeta{ | |
+ Labels: map[string]string{"a": "b"}, | |
+ }, | |
+ Spec: podtest.MakePodSpec(), | |
}, | |
- Spec: podtest.MakePodSpec( | |
- podtest.SetVolumes(core.Volume{Name: "gcepd", VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}), | |
- ), | |
}, | |
} | |
- invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} | |
- invalidPodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: invalidSelector, | |
- }, | |
- Spec: podtest.MakePodSpec(), | |
- }, | |
+ for _, tweak := range tweaks { | |
+ tweak(&rc) | |
} | |
- type rcUpdateTest struct { | |
+ return rc | |
+} | |
+ | |
+func TestValidateReplicationControllerUpdate(t *testing.T) { | |
+ successCases := []struct { | |
old core.ReplicationController | |
update core.ReplicationController | |
- } | |
- successCases := []rcUpdateTest{{ | |
- old: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
- update: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 3, | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
+ }{{ | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Replicas = ptr.To[int32](0) | |
+ }), | |
}, { | |
- old: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
- update: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 1, | |
- Selector: validSelector, | |
- Template: &readWriteVolumePodTemplate.Template, | |
- }, | |
- }, | |
- }, | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Replicas = ptr.To[int32](3) | |
+ }), | |
+ }, { | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.MinReadySeconds = 0 | |
+ }), | |
+ }, { | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.MinReadySeconds = 3 | |
+ }), | |
+ }, { | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Replicas = ptr.To[int32](2) | |
+ rc.Spec.Template.Spec = podtest.MakePodSpec( | |
+ podtest.SetVolumes( | |
+ core.Volume{ | |
+ Name: "gcepd", | |
+ VolumeSource: core.VolumeSource{ | |
+ GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ | |
+ PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false, | |
+ }, | |
+ }, | |
+ })) | |
+ }), | |
+ }} | |
+ for _, tc := range successCases { | |
+ tc.old.ObjectMeta.ResourceVersion = "1" | |
+ tc.update.ObjectMeta.ResourceVersion = "1" | |
+ for _, gateVal := range []bool{false, true} { | |
+ gate := features.DeclarativeValidation | |
+ featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, gate, gateVal) | |
+ | |
+ errs := ValidateReplicationControllerUpdate(&tc.update, &tc.old, PodValidationOptions{}) | |
+ if utilfeature.DefaultFeatureGate.Enabled(gate) { | |
+ // If declarative validation is enabled, it's the union of | |
+ // managed and declarative validation that we are testing. | |
+ newVersioned := v1.ReplicationController{} | |
+ if err := v1util.Convert_core_ReplicationController_To_v1_ReplicationController(&tc.update, &newVersioned, nil); err != nil { | |
+ t.Fatalf("failed to convert to v1: %v", err) | |
+ } | |
+ oldVersioned := v1.ReplicationController{} | |
+ if err := v1util.Convert_core_ReplicationController_To_v1_ReplicationController(&tc.old, &oldVersioned, nil); err != nil { | |
+ t.Fatalf("failed to convert to v1: %v", err) | |
+ } | |
+ ctx := request.WithRequestInfo(context.Background(), &request.RequestInfo{ | |
+ APIGroup: "", | |
+ APIVersion: "v1", | |
+ }) | |
+ errs = append(errs, rest.ValidateUpdateDeclaratively(ctx, nil, legacyscheme.Scheme, &newVersioned, &oldVersioned)...) | |
+ } | |
+ if len(errs) != 0 { | |
+ t.Errorf("expected success: %v", errs) | |
+ } | |
+ } | |
+ | |
+ verifyVersionedValidationEquivalence(t, &tc.update, &tc.old) | |
} | |
- for _, successCase := range successCases { | |
- successCase.old.ObjectMeta.ResourceVersion = "1" | |
- successCase.update.ObjectMeta.ResourceVersion = "1" | |
- if errs := ValidateReplicationControllerUpdate(&successCase.update, &successCase.old, PodValidationOptions{}); len(errs) != 0 { | |
- t.Errorf("expected success: %v", errs) | |
+ | |
+ mkErrs := func(strs ...string) []*regexp.Regexp { | |
+ out := make([]*regexp.Regexp, 0, len(strs)) | |
+ for _, s := range strs { | |
+ out = append(out, regexp.MustCompile(s)) | |
} | |
+ return out | |
} | |
- errorCases := map[string]rcUpdateTest{ | |
- "more than one read/write": { | |
- old: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
- update: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 2, | |
- Selector: validSelector, | |
- Template: &readWriteVolumePodTemplate.Template, | |
- }, | |
- }, | |
+ | |
+ errorCases := map[string]struct { | |
+ old core.ReplicationController | |
+ update core.ReplicationController | |
+ errs []*regexp.Regexp | |
+ }{ | |
+ "unmatched selector": { | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Selector["another"] = "value" | |
+ }), | |
+ errs: mkErrs("^spec.template.metadata.labels: Invalid value.*does not match template"), | |
}, | |
"invalid selector": { | |
- old: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
- update: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 2, | |
- Selector: invalidSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ invalid := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} | |
+ rc.Spec.Template.Labels = invalid | |
+ rc.Spec.Selector = invalid | |
+ }), | |
+ errs: mkErrs("^spec.template.labels: Invalid value.*name part must consist of"), | |
}, | |
"invalid pod": { | |
- old: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
- update: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 2, | |
- Selector: validSelector, | |
- Template: &invalidPodTemplate.Template, | |
- }, | |
- }, | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Template = nil | |
+ }), | |
+ errs: mkErrs("^spec.template: Required value"), | |
}, | |
"negative replicas": { | |
- old: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
- update: core.ReplicationController{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: -1, | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Replicas = ptr.To[int32](-1) | |
+ }), | |
+ errs: mkErrs("^spec.replicas: Invalid value"), | |
}, | |
- } | |
- for testName, errorCase := range errorCases { | |
- // TODO: migrate | |
- if errs := ValidateReplicationControllerUpdate(&errorCase.update, &errorCase.old, PodValidationOptions{}); len(errs) == 0 { | |
- t.Errorf("expected failure: %s", testName) | |
+ "nil replicas": { | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Replicas = nil | |
+ }), | |
+ errs: mkErrs("^spec.replicas: Required value"), | |
+ }, | |
+ "negative minReadySeconds": { | |
+ old: mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ update: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.MinReadySeconds = -1 | |
+ }), | |
+ errs: mkErrs("^spec.minReadySeconds: Invalid value"), | |
+ }, | |
+ } | |
+ for k, tc := range errorCases { | |
+ tc.old.ObjectMeta.ResourceVersion = "1" | |
+ tc.update.ObjectMeta.ResourceVersion = "1" | |
+ for _, gateVal := range []bool{false, true} { | |
+ gate := features.DeclarativeValidation | |
+ featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, gate, gateVal) | |
+ | |
+ errs := ValidateReplicationControllerUpdate(&tc.update, &tc.old, PodValidationOptions{}) | |
+ if utilfeature.DefaultFeatureGate.Enabled(gate) { | |
+ // If declarative validation is enabled, it's the union of | |
+ // managed and declarative validation that we are testing. | |
+ newVersioned := v1.ReplicationController{} | |
+ if err := v1util.Convert_core_ReplicationController_To_v1_ReplicationController(&tc.update, &newVersioned, nil); err != nil { | |
+ t.Fatalf("failed to convert to v1: %v", err) | |
+ } | |
+ oldVersioned := v1.ReplicationController{} | |
+ if err := v1util.Convert_core_ReplicationController_To_v1_ReplicationController(&tc.old, &oldVersioned, nil); err != nil { | |
+ t.Fatalf("failed to convert to v1: %v", err) | |
+ } | |
+ ctx := request.WithRequestInfo(context.Background(), &request.RequestInfo{ | |
+ APIGroup: "", | |
+ APIVersion: "v1", | |
+ }) | |
+ errs = append(errs, rest.ValidateUpdateDeclaratively(ctx, nil, legacyscheme.Scheme, &newVersioned, &oldVersioned)...) | |
+ } | |
+ if len(errs) == 0 { | |
+ t.Errorf("case %q(gate=%v): expected failure", k, gateVal) | |
+ } else if len(errs) != len(tc.errs) { | |
+ t.Errorf("case %q(gate=%v): expected %d failures, got %d:\n%v", k, gateVal, len(tc.errs), len(errs), fmtErrs(errs)) | |
+ } else { | |
+ for i, re := range tc.errs { | |
+ if want, got := re.String(), errs[i].Error(); !re.MatchString(got) { | |
+ t.Errorf("case %q(gate=%v): wrong error:\n\texpected: %q\n\t got: %q", k, gateVal, want, got) | |
+ } | |
+ } | |
+ } | |
} | |
+ | |
+ verifyVersionedValidationEquivalence(t, &tc.update, &tc.old) | |
} | |
} | |
func TestValidateReplicationController(t *testing.T) { | |
- validSelector := map[string]string{"a": "b"} | |
- validPodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- Spec: podtest.MakePodSpec(), | |
- }, | |
- } | |
- readWriteVolumePodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- Spec: podtest.MakePodSpec( | |
- podtest.SetVolumes(core.Volume{Name: "gcepd", VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}), | |
- ), | |
- }, | |
- } | |
- hostnetPodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- Spec: podtest.MakePodSpec( | |
- podtest.SetSecurityContext(&core.PodSecurityContext{ | |
- HostNetwork: true, | |
- }), | |
- podtest.SetContainers(podtest.MakeContainer("abc", podtest.SetContainerPorts( | |
- core.ContainerPort{ | |
- ContainerPort: 12345, | |
- Protocol: core.ProtocolTCP, | |
+ successCases := []core.ReplicationController{ | |
+ mkValidReplicationController(func(rc *core.ReplicationController) {}), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Name = "abc-123" }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Replicas = ptr.To[int32](2) | |
+ rc.Spec.Template.Spec = podtest.MakePodSpec( | |
+ podtest.SetVolumes( | |
+ core.Volume{ | |
+ Name: "gcepd", | |
+ VolumeSource: core.VolumeSource{ | |
+ GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ | |
+ PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false, | |
+ }, | |
+ }, | |
+ })) | |
+ }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Template.Spec = podtest.MakePodSpec( | |
+ podtest.SetSecurityContext(&core.PodSecurityContext{HostNetwork: true}), | |
+ podtest.SetContainers(podtest.MakeContainer("abc", | |
+ podtest.SetContainerPorts(core.ContainerPort{ | |
+ ContainerPort: 12345, Protocol: core.ProtocolTCP, | |
}))), | |
- ), | |
- }, | |
+ ) | |
+ }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Replicas = ptr.To[int32](0) }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Replicas = ptr.To[int32](1) }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Replicas = ptr.To[int32](100) }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.MinReadySeconds = 0 }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.MinReadySeconds = 1 }), | |
+ mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.MinReadySeconds = 100 }), | |
} | |
- invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} | |
- invalidPodTemplate := core.PodTemplate{ | |
- Template: core.PodTemplateSpec{ | |
- Spec: podtest.MakePodSpec(), | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: invalidSelector, | |
- }, | |
- }, | |
+ for _, tc := range successCases { | |
+ for _, gateVal := range []bool{false, true} { | |
+ gate := features.DeclarativeValidation | |
+ featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, gate, gateVal) | |
+ | |
+ errs := ValidateReplicationController(&tc, PodValidationOptions{}) | |
+ if utilfeature.DefaultFeatureGate.Enabled(gate) { | |
+ // If declarative validation is enabled, it's the union of | |
+ // managed and declarative validation that we are testing. | |
+ versioned := v1.ReplicationController{} | |
+ if err := v1util.Convert_core_ReplicationController_To_v1_ReplicationController(&tc, &versioned, nil); err != nil { | |
+ t.Fatalf("failed to convert to v1: %v", err) | |
+ } | |
+ ctx := request.WithRequestInfo(context.Background(), &request.RequestInfo{ | |
+ APIGroup: "", | |
+ APIVersion: "v1", | |
+ }) | |
+ errs = append(errs, rest.ValidateDeclaratively(ctx, nil, legacyscheme.Scheme, &versioned)...) | |
+ } | |
+ if len(errs) != 0 { | |
+ t.Errorf("expected success: %v", errs) | |
+ } | |
+ } | |
+ | |
+ verifyVersionedValidationEquivalence(t, &tc, nil) | |
} | |
- successCases := []core.ReplicationController{{ | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
- }, { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 1, | |
- Selector: validSelector, | |
- Template: &readWriteVolumePodTemplate.Template, | |
- }, | |
- }, { | |
- ObjectMeta: metav1.ObjectMeta{Name: "hostnet", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 1, | |
- Selector: validSelector, | |
- Template: &hostnetPodTemplate.Template, | |
- }, | |
- }} | |
- for _, successCase := range successCases { | |
- if errs := ValidateReplicationController(&successCase, PodValidationOptions{}); len(errs) != 0 { | |
- t.Errorf("expected success: %v", errs) | |
+ | |
+ mkErrs := func(strs ...string) []*regexp.Regexp { | |
+ out := make([]*regexp.Regexp, 0, len(strs)) | |
+ for _, s := range strs { | |
+ out = append(out, regexp.MustCompile(s)) | |
} | |
+ return out | |
} | |
- errorCases := map[string]core.ReplicationController{ | |
- "zero-length ID": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
+ errorCases := map[string]struct { | |
+ input core.ReplicationController | |
+ errs []*regexp.Regexp | |
+ }{ | |
+ "missing name": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Name = "" }), | |
+ errs: mkErrs("^metadata.name: Required value"), | |
}, | |
- "missing-namespace": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123"}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
+ "missing namespace": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Namespace = "" }), | |
+ errs: mkErrs("^metadata.namespace: Required value"), | |
}, | |
"empty selector": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Template: &validPodTemplate.Template, | |
- }, | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Selector = nil }), | |
+ errs: mkErrs("^spec.selector: Required value"), | |
}, | |
- "selector_doesnt_match": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: map[string]string{"foo": "bar"}, | |
- Template: &validPodTemplate.Template, | |
- }, | |
+ "selector doesnt match": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Selector = map[string]string{"foo": "bar"} }), | |
+ errs: mkErrs("^spec.template.metadata.labels: Invalid value"), | |
}, | |
"invalid manifest": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- }, | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Template = nil }), | |
+ errs: mkErrs("^spec.template: Required value"), | |
}, | |
- "read-write persistent disk with > 1 pod": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc"}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 2, | |
- Selector: validSelector, | |
- Template: &readWriteVolumePodTemplate.Template, | |
- }, | |
+ "negative replicas": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Replicas = ptr.To[int32](-1) }), | |
+ errs: mkErrs("^spec.replicas: Invalid value"), | |
}, | |
- "negative_replicas": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: -1, | |
- Selector: validSelector, | |
- }, | |
+ "negative minReadySeconds": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.MinReadySeconds = -1 }), | |
+ errs: mkErrs("^spec.minReadySeconds: Invalid value"), | |
}, | |
- "invalid_label": { | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Name: "abc-123", | |
- Namespace: metav1.NamespaceDefault, | |
- Labels: map[string]string{ | |
+ "nil replicas": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { rc.Spec.Replicas = nil }), | |
+ errs: mkErrs("^spec.replicas: Required value"), | |
+ }, | |
+ "invalid label": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Labels = map[string]string{ | |
"NoUppercaseOrSpecialCharsLike=Equals": "bar", | |
- }, | |
- }, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
+ } | |
+ }), | |
+ errs: mkErrs("^metadata.labels: Invalid value.*name part must consist of"), | |
}, | |
- "invalid_label 2": { | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Name: "abc-123", | |
- Namespace: metav1.NamespaceDefault, | |
- Labels: map[string]string{ | |
+ "invalid label 2": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Template.Labels = map[string]string{ | |
"NoUppercaseOrSpecialCharsLike=Equals": "bar", | |
- }, | |
- }, | |
- Spec: core.ReplicationControllerSpec{ | |
- Template: &invalidPodTemplate.Template, | |
- }, | |
+ } | |
+ }), | |
+ errs: mkErrs("^spec.template.metadata.labels: Invalid value.*does not match template", | |
+ "^spec.template.labels: Invalid value.*name part must consist of"), | |
}, | |
- "invalid_annotation": { | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Name: "abc-123", | |
- Namespace: metav1.NamespaceDefault, | |
- Annotations: map[string]string{ | |
+ "invalid annotation": { | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Annotations = map[string]string{ | |
"NoUppercaseOrSpecialCharsLike=Equals": "bar", | |
- }, | |
- }, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &validPodTemplate.Template, | |
- }, | |
+ } | |
+ }), | |
+ errs: mkErrs("^metadata.annotations: Invalid value.*name part must consist of"), | |
}, | |
"invalid restart policy 1": { | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Name: "abc-123", | |
- Namespace: metav1.NamespaceDefault, | |
- }, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &core.PodTemplateSpec{ | |
- Spec: podtest.MakePodSpec(podtest.SetRestartPolicy(core.RestartPolicyOnFailure)), | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- }, | |
- }, | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Template.Spec.RestartPolicy = core.RestartPolicyOnFailure | |
+ }), | |
+ errs: mkErrs("^spec.template.spec.restartPolicy: Unsupported value"), | |
}, | |
"invalid restart policy 2": { | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Name: "abc-123", | |
- Namespace: metav1.NamespaceDefault, | |
- }, | |
- Spec: core.ReplicationControllerSpec{ | |
- Selector: validSelector, | |
- Template: &core.PodTemplateSpec{ | |
- Spec: podtest.MakePodSpec(podtest.SetRestartPolicy(core.RestartPolicyNever)), | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- }, | |
- }, | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Template.Spec.RestartPolicy = core.RestartPolicyNever | |
+ }), | |
+ errs: mkErrs("^spec.template.spec.restartPolicy: Unsupported value"), | |
}, | |
"template may not contain ephemeral containers": { | |
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, | |
- Spec: core.ReplicationControllerSpec{ | |
- Replicas: 1, | |
- Selector: validSelector, | |
- Template: &core.PodTemplateSpec{ | |
- ObjectMeta: metav1.ObjectMeta{ | |
- Labels: validSelector, | |
- }, | |
- Spec: podtest.MakePodSpec( | |
- podtest.SetEphemeralContainers(core.EphemeralContainer{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}), | |
- ), | |
- }, | |
- }, | |
+ input: mkValidReplicationController(func(rc *core.ReplicationController) { | |
+ rc.Spec.Template.Spec = podtest.MakePodSpec( | |
+ podtest.SetEphemeralContainers( | |
+ core.EphemeralContainer{ | |
+ EphemeralContainerCommon: core.EphemeralContainerCommon{ | |
+ Name: "debug", | |
+ Image: "image", | |
+ ImagePullPolicy: "IfNotPresent", | |
+ TerminationMessagePolicy: "File", | |
+ }, | |
+ })) | |
+ }), | |
+ errs: mkErrs("^spec.template.spec.ephemeralContainers: Forbidden: ephemeral containers not allowed in pod template"), | |
}, | |
} | |
- for k, v := range errorCases { | |
- // TODO: migrate | |
- errs := ValidateReplicationController(&v, PodValidationOptions{}) | |
- if len(errs) == 0 { | |
- t.Errorf("expected failure for %s", k) | |
+ for k, tc := range errorCases { | |
+ for _, gateVal := range []bool{false, true} { | |
+ gate := features.DeclarativeValidation | |
+ featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, gate, gateVal) | |
+ | |
+ errs := ValidateReplicationController(&tc.input, PodValidationOptions{}) | |
+ if utilfeature.DefaultFeatureGate.Enabled(gate) { | |
+ // If declarative validation is enabled, it's the union of | |
+ // managed and declarative validation that we are testing. | |
+ versioned := v1.ReplicationController{} | |
+ if err := v1util.Convert_core_ReplicationController_To_v1_ReplicationController(&tc.input, &versioned, nil); err != nil { | |
+ t.Fatalf("failed to convert to v1: %v", err) | |
+ } | |
+ ctx := request.WithRequestInfo(context.Background(), &request.RequestInfo{ | |
+ APIGroup: "", | |
+ APIVersion: "v1", | |
+ }) | |
+ errs = append(errs, rest.ValidateDeclaratively(ctx, nil, legacyscheme.Scheme, &versioned)...) | |
+ } | |
+ if len(errs) == 0 { | |
+ t.Errorf("case %q(gate=%v): expected failure", k, gateVal) | |
+ } else if len(errs) != len(tc.errs) { | |
+ t.Errorf("case %q(gate=%v): expected %d failures, got %d:\n%v", k, gateVal, len(tc.errs), len(errs), fmtErrs(errs)) | |
+ } else { | |
+ for i, re := range tc.errs { | |
+ if want, got := re.String(), errs[i].Error(); !re.MatchString(got) { | |
+ t.Errorf("case %q(gate=%v): wrong error:\n\texpected: %q\n\t got: %q", k, gateVal, want, got) | |
+ } | |
+ } | |
+ } | |
} | |
- for i := range errs { | |
- field := errs[i].Field | |
- if !strings.HasPrefix(field, "spec.template.") && | |
- field != "metadata.name" && | |
- field != "metadata.namespace" && | |
- field != "spec.selector" && | |
- field != "spec.template" && | |
- field != "GCEPersistentDisk.ReadOnly" && | |
- field != "spec.replicas" && | |
- field != "spec.template.labels" && | |
- field != "metadata.annotations" && | |
- field != "metadata.labels" && | |
- field != "status.replicas" { | |
- t.Errorf("%s: missing prefix for: %v", k, errs[i]) | |
+ | |
+ verifyVersionedValidationEquivalence(t, &tc.input, nil) | |
+ } | |
+} | |
+ | |
+// helper for nicer output | |
+func fmtErrs(errs field.ErrorList) string { | |
+ if len(errs) == 0 { | |
+ return "<no errors>" | |
+ } | |
+ if len(errs) == 1 { | |
+ return strconv.Quote(errs[0].Error()) | |
+ } | |
+ buf := bytes.Buffer{} | |
+ for _, e := range errs { | |
+ buf.WriteString("\n") | |
+ buf.WriteString(strconv.Quote(e.Error())) | |
+ } | |
+ | |
+ return buf.String() | |
+} | |
+ | |
+func verifyVersionedValidationEquivalence(t *testing.T, obj, old k8sruntime.Object) { | |
+ t.Helper() | |
+ | |
+ // Accumulate errors from all versioned validation, per version. | |
+ all := map[string]field.ErrorList{} | |
+ accumulate := func(t *testing.T, gv string, errs field.ErrorList) { | |
+ all[gv] = errs | |
+ } | |
+ if old == nil { | |
+ runtimetest.RunValidationForEachVersion(t, legacyscheme.Scheme, sets.Set[string]{}, obj, accumulate) | |
+ } else { | |
+ runtimetest.RunUpdateValidationForEachVersion(t, legacyscheme.Scheme, sets.Set[string]{}, obj, old, accumulate) | |
+ } | |
+ | |
+ // Make a copy so we can modify it. | |
+ other := map[string]field.ErrorList{} | |
+ // Index for nicer output. | |
+ keys := []string{} | |
+ for k, v := range all { | |
+ other[k] = v | |
+ keys = append(keys, k) | |
+ } | |
+ sort.Strings(keys) | |
+ | |
+ // Compare each lhs to each rhs. | |
+ for _, lk := range keys { | |
+ lv := all[lk] | |
+ // remove lk since to prevent comparison to itself and because this | |
+ // iteration will compare it to any version it has not yet been | |
+ // compared to. e.g. [1, 2, 3] vs. [1, 2, 3] yields: | |
+ // 1 vs. 2 | |
+ // 1 vs. 3 | |
+ // 2 vs. 3 | |
+ delete(other, lk) | |
+ // don't compare to ourself | |
+ for _, rk := range keys { | |
+ rv, found := other[rk] | |
+ if !found { | |
+ continue // done already | |
+ } | |
+ if len(lv) != len(rv) { | |
+ t.Errorf("different error count (%d vs. %d)\n%s: %v\n%s: %v", len(lv), len(rv), lk, fmtErrs(lv), rk, fmtErrs(rv)) | |
+ continue | |
+ } | |
+ next := false | |
+ for i := range lv { | |
+ if l, r := lv[i], rv[i]; l.Type != r.Type || l.Detail != r.Detail { | |
+ t.Errorf("different errors\n%s: %v\n%s: %v", lk, fmtErrs(lv), rk, fmtErrs(rv)) | |
+ next = true | |
+ break | |
+ } | |
+ } | |
+ if next { | |
+ continue | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+// FIXME: move somewhere generic - pkg/api/testing? | |
+func TestVersionedValidationByFuzzing(t *testing.T) { | |
+ for i := 0; i < *roundtrip.FuzzIters; i++ { | |
+ gv := schema.GroupVersion{Group: "", Version: "v1"} | |
+ f := fuzzer.FuzzerFor(apitest.FuzzerFuncs, rand.NewSource(rand.Int63()), legacyscheme.Codecs) | |
+ for kind := range legacyscheme.Scheme.KnownTypes(gv) { | |
+ obj, err := legacyscheme.Scheme.New(gv.WithKind(kind)) | |
+ if err != nil { | |
+ t.Fatalf("could not create a %v: %s", kind, err) | |
} | |
+ f.Fuzz(obj) | |
+ verifyVersionedValidationEquivalence(t, obj, nil) | |
+ | |
+ old, err := legacyscheme.Scheme.New(gv.WithKind(kind)) | |
+ if err != nil { | |
+ t.Fatalf("could not create a %v: %s", kind, err) | |
+ } | |
+ f.Fuzz(old) | |
+ verifyVersionedValidationEquivalence(t, obj, old) | |
} | |
} | |
} | |
@@ -17041,23 +17153,23 @@ func TestValidateNode(t *testing.T) { | |
Name: "abc", | |
Annotations: map[string]string{ | |
core.PreferAvoidPodsAnnotationKey: ` | |
- { | |
- "preferAvoidPods": [ | |
- { | |
- "podSignature": { | |
- "podController": { | |
- "apiVersion": "v1", | |
- "kind": "ReplicationController", | |
- "name": "foo", | |
- "uid": "abcdef123456", | |
- "controller": true | |
- } | |
- }, | |
- "reason": "some reason", | |
- "message": "some message" | |
- } | |
- ] | |
- }`, | |
+ { | |
+ "preferAvoidPods": [ | |
+ { | |
+ "podSignature": { | |
+ "podController": { | |
+ "apiVersion": "v1", | |
+ "kind": "ReplicationController", | |
+ "name": "foo", | |
+ "uid": "abcdef123456", | |
+ "controller": true | |
+ } | |
+ }, | |
+ "reason": "some reason", | |
+ "message": "some message" | |
+ } | |
+ ] | |
+ }`, | |
}, | |
}, | |
Status: core.NodeStatus{ | |
@@ -17208,14 +17320,14 @@ func TestValidateNode(t *testing.T) { | |
Name: "abc-123", | |
Annotations: map[string]string{ | |
core.PreferAvoidPodsAnnotationKey: ` | |
- { | |
- "preferAvoidPods": [ | |
- { | |
- "reason": "some reason", | |
- "message": "some message" | |
- } | |
- ] | |
- }`, | |
+ { | |
+ "preferAvoidPods": [ | |
+ { | |
+ "reason": "some reason", | |
+ "message": "some message" | |
+ } | |
+ ] | |
+ }`, | |
}, | |
}, | |
Status: core.NodeStatus{ | |
@@ -17231,23 +17343,23 @@ func TestValidateNode(t *testing.T) { | |
Name: "abc-123", | |
Annotations: map[string]string{ | |
core.PreferAvoidPodsAnnotationKey: ` | |
- { | |
- "preferAvoidPods": [ | |
- { | |
- "podSignature": { | |
- "podController": { | |
- "apiVersion": "v1", | |
- "kind": "ReplicationController", | |
- "name": "foo", | |
- "uid": "abcdef123456", | |
- "controller": false | |
- } | |
- }, | |
- "reason": "some reason", | |
- "message": "some message" | |
- } | |
- ] | |
- }`, | |
+ { | |
+ "preferAvoidPods": [ | |
+ { | |
+ "podSignature": { | |
+ "podController": { | |
+ "apiVersion": "v1", | |
+ "kind": "ReplicationController", | |
+ "name": "foo", | |
+ "uid": "abcdef123456", | |
+ "controller": false | |
+ } | |
+ }, | |
+ "reason": "some reason", | |
+ "message": "some message" | |
+ } | |
+ ] | |
+ }`, | |
}, | |
}, | |
Status: core.NodeStatus{ | |
@@ -17529,23 +17641,23 @@ func TestValidateNodeUpdate(t *testing.T) { | |
Name: "foo", | |
Annotations: map[string]string{ | |
core.PreferAvoidPodsAnnotationKey: ` | |
- { | |
- "preferAvoidPods": [ | |
- { | |
- "podSignature": { | |
- "podController": { | |
- "apiVersion": "v1", | |
- "kind": "ReplicationController", | |
- "name": "foo", | |
- "uid": "abcdef123456", | |
- "controller": true | |
- } | |
- }, | |
- "reason": "some reason", | |
- "message": "some message" | |
- } | |
- ] | |
- }`, | |
+ { | |
+ "preferAvoidPods": [ | |
+ { | |
+ "podSignature": { | |
+ "podController": { | |
+ "apiVersion": "v1", | |
+ "kind": "ReplicationController", | |
+ "name": "foo", | |
+ "uid": "abcdef123456", | |
+ "controller": true | |
+ } | |
+ }, | |
+ "reason": "some reason", | |
+ "message": "some message" | |
+ } | |
+ ] | |
+ }`, | |
}, | |
}, | |
Spec: core.NodeSpec{ | |
@@ -17561,14 +17673,14 @@ func TestValidateNodeUpdate(t *testing.T) { | |
Name: "foo", | |
Annotations: map[string]string{ | |
core.PreferAvoidPodsAnnotationKey: ` | |
- { | |
- "preferAvoidPods": [ | |
- { | |
- "reason": "some reason", | |
- "message": "some message" | |
- } | |
- ] | |
- }`, | |
+ { | |
+ "preferAvoidPods": [ | |
+ { | |
+ "reason": "some reason", | |
+ "message": "some message" | |
+ } | |
+ ] | |
+ }`, | |
}, | |
}, | |
}, false}, | |
@@ -17581,23 +17693,23 @@ func TestValidateNodeUpdate(t *testing.T) { | |
Name: "foo", | |
Annotations: map[string]string{ | |
core.PreferAvoidPodsAnnotationKey: ` | |
- { | |
- "preferAvoidPods": [ | |
- { | |
- "podSignature": { | |
- "podController": { | |
- "apiVersion": "v1", | |
- "kind": "ReplicationController", | |
- "name": "foo", | |
- "uid": "abcdef123456", | |
- "controller": false | |
- } | |
- }, | |
- "reason": "some reason", | |
- "message": "some message" | |
- } | |
- ] | |
- }`, | |
+ { | |
+ "preferAvoidPods": [ | |
+ { | |
+ "podSignature": { | |
+ "podController": { | |
+ "apiVersion": "v1", | |
+ "kind": "ReplicationController", | |
+ "name": "foo", | |
+ "uid": "abcdef123456", | |
+ "controller": false | |
+ } | |
+ }, | |
+ "reason": "some reason", | |
+ "message": "some message" | |
+ } | |
+ ] | |
+ }`, | |
}, | |
}, | |
}, false}, | |
@@ -20860,7 +20972,7 @@ func TestValidateEndpointsCreate(t *testing.T) { | |
for k, v := range errorCases { | |
t.Run(k, func(t *testing.T) { | |
if errs := ValidateEndpointsCreate(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) { | |
- t.Errorf("Expected error type %s with detail %q, got %v", v.errorType, v.errorDetail, errs) | |
+ t.Errorf("expected error type %q with detail %q\ngot %v", v.errorType, v.errorDetail, errs) | |
} | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment