kubernetes strategy_test 源码
kubernetes strategy_test 代码
文件路径:/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/strategy_test.go
/*
Copyright 2017 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 customresourcedefinition
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/utils/pointer"
)
func strPtr(in string) *string {
return &in
}
func TestValidateAPIApproval(t *testing.T) {
okFn := func(t *testing.T, errors field.ErrorList) {
t.Helper()
if len(errors) > 0 {
t.Fatal(errors)
}
}
tests := []struct {
name string
group string
annotationValue string
oldAnnotationValue *string
validateError func(t *testing.T, errors field.ErrorList)
}{
{
name: "ignore non-k8s group",
group: "other.io",
annotationValue: "invalid",
validateError: okFn,
},
{
name: "invalid annotation create",
group: "sigs.k8s.io",
annotationValue: "invalid",
validateError: func(t *testing.T, errors field.ErrorList) {
t.Helper()
if len(errors) == 0 {
t.Fatal("expected errors, got none")
}
if e, a := `metadata.annotations[api-approved.kubernetes.io]: Invalid value: "invalid": protected groups must have approval annotation "api-approved.kubernetes.io" with either a URL or a reason starting with "unapproved", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
t.Fatal(errors)
}
},
},
{
name: "invalid annotation update",
group: "sigs.k8s.io",
annotationValue: "invalid",
oldAnnotationValue: strPtr("invalid"),
validateError: okFn,
},
{
name: "invalid annotation to missing",
group: "sigs.k8s.io",
annotationValue: "",
oldAnnotationValue: strPtr("invalid"),
validateError: func(t *testing.T, errors field.ErrorList) {
t.Helper()
if len(errors) == 0 {
t.Fatal("expected errors, got none")
}
if e, a := `metadata.annotations[api-approved.kubernetes.io]: Required value: protected groups must have approval annotation "api-approved.kubernetes.io", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
t.Fatal(errors)
}
},
},
{
name: "missing to invalid annotation",
group: "sigs.k8s.io",
annotationValue: "invalid",
oldAnnotationValue: strPtr(""),
validateError: func(t *testing.T, errors field.ErrorList) {
t.Helper()
if len(errors) == 0 {
t.Fatal("expected errors, got none")
}
if e, a := `metadata.annotations[api-approved.kubernetes.io]: Invalid value: "invalid": protected groups must have approval annotation "api-approved.kubernetes.io" with either a URL or a reason starting with "unapproved", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
t.Fatal(errors)
}
},
},
{
name: "missing annotation",
group: "sigs.k8s.io",
annotationValue: "",
validateError: func(t *testing.T, errors field.ErrorList) {
t.Helper()
if len(errors) == 0 {
t.Fatal("expected errors, got none")
}
if e, a := `metadata.annotations[api-approved.kubernetes.io]: Required value: protected groups must have approval annotation "api-approved.kubernetes.io", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
t.Fatal(errors)
}
},
},
{
name: "missing annotation update",
group: "sigs.k8s.io",
annotationValue: "",
oldAnnotationValue: strPtr(""),
validateError: okFn,
},
{
name: "url",
group: "sigs.k8s.io",
annotationValue: "https://github.com/kubernetes/kubernetes/pull/79724",
validateError: okFn,
},
{
name: "unapproved",
group: "sigs.k8s.io",
annotationValue: "unapproved, other reason",
validateError: okFn,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
crd := &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos." + test.group, Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: test.annotationValue}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: test.group,
Scope: apiextensions.NamespaceScoped,
Version: "v1",
Versions: []apiextensions.CustomResourceDefinitionVersion{{Name: "v1", Storage: true, Served: true}},
Names: apiextensions.CustomResourceDefinitionNames{Plural: "foos", Singular: "foo", Kind: "Foo", ListKind: "FooList"},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object", XPreserveUnknownFields: pointer.BoolPtr(true)},
},
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"v1"},
},
}
var oldCRD *apiextensions.CustomResourceDefinition
if test.oldAnnotationValue != nil {
oldCRD = &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos." + test.group, Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: *test.oldAnnotationValue}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: test.group,
Scope: apiextensions.NamespaceScoped,
Version: "v1",
Versions: []apiextensions.CustomResourceDefinitionVersion{{Name: "v1", Storage: true, Served: true}},
Names: apiextensions.CustomResourceDefinitionNames{Plural: "foos", Singular: "foo", Kind: "Foo", ListKind: "FooList"},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object", XPreserveUnknownFields: pointer.BoolPtr(true)},
},
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"v1"},
},
}
}
var actual field.ErrorList
ctx := context.TODO()
if oldCRD == nil {
actual = validation.ValidateCustomResourceDefinition(ctx, crd)
} else {
actual = validation.ValidateCustomResourceDefinitionUpdate(ctx, crd, oldCRD)
}
test.validateError(t, actual)
})
}
}
// TestDropDisabledFields tests if the drop functionality is working fine or not with feature gate switch
func TestDropDisabledFields(t *testing.T) {
testCases := []struct {
name string
enableXValidations bool
crd *apiextensions.CustomResourceDefinition
oldCRD *apiextensions.CustomResourceDefinition
expectedCRD *apiextensions.CustomResourceDefinition
}{
{
name: "For creation, FG disabled, no XValidations, no field drop",
enableXValidations: false,
crd: &apiextensions.CustomResourceDefinition{},
oldCRD: nil,
expectedCRD: &apiextensions.CustomResourceDefinition{},
},
{
name: "For creation, FG disabled, empty XValidations, no field drop",
enableXValidations: false,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{},
},
},
oldCRD: nil,
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{},
},
},
},
{
name: "For creation, FG disabled, set XValidations, drop XValidations",
enableXValidations: false,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "size of scoped field should be greater than 0.",
},
},
},
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"subRule": {
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "isTest == true",
Message: "isTest should be true.",
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"isTest": {
Type: "boolean",
},
},
},
},
},
},
},
},
oldCRD: nil,
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
},
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"subRule": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"isTest": {
Type: "boolean",
},
},
},
},
},
},
},
},
},
{
name: "For creation, FG enabled, set XValidations, update with XValidations",
enableXValidations: true,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "size of scoped field should be greater than 0.",
},
},
},
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"subRule": {
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "isTest == true",
Message: "isTest should be true.",
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"isTest": {
Type: "boolean",
},
},
},
},
},
},
},
},
oldCRD: nil,
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "size of scoped field should be greater than 0.",
},
},
},
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"subRule": {
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "isTest == true",
Message: "isTest should be true.",
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"isTest": {
Type: "boolean",
},
},
},
},
},
},
},
},
},
{
name: "For update, FG disabled, oldCRD XValidation in use, don't drop XValidations",
enableXValidations: false,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "size of scoped field should be greater than 0.",
},
},
},
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"subRule": {
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "isTest == true",
Message: "isTest should be true.",
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"isTest": {
Type: "boolean",
},
},
},
},
},
},
},
},
oldCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "size of scoped field should be greater than 0.",
},
},
},
},
},
},
},
},
},
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
Dependencies: apiextensions.JSONSchemaDependencies{
"test": apiextensions.JSONSchemaPropsOrStringArray{
Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "size of scoped field should be greater than 0.",
},
},
},
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"subRule": {
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "isTest == true",
Message: "isTest should be true.",
},
},
Properties: map[string]apiextensions.JSONSchemaProps{
"isTest": {
Type: "boolean",
},
},
},
},
},
},
},
},
},
{
name: "For update, FG disabled, oldCRD has no XValidations, drop XValidations",
enableXValidations: false,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
},
},
},
},
oldCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
},
},
},
},
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
},
},
},
},
},
{
name: "For update, FG enabled, oldCRD has XValidations, updated to newCRD",
enableXValidations: true,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
},
},
},
},
oldCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "old data",
Message: "old data",
},
},
},
},
},
},
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
},
},
},
},
},
{
name: "For update, FG enabled, oldCRD has no XValidations, updated to newCRD",
enableXValidations: true,
crd: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
},
},
},
},
oldCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
},
},
},
},
expectedCRD: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
XValidations: apiextensions.ValidationRules{
{
Rule: "size(self) > 0",
Message: "openAPIV3Schema should contain more than 0 element.",
},
},
},
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomResourceValidationExpressions, tc.enableXValidations)()
old := tc.oldCRD.DeepCopy()
dropDisabledFields(tc.crd, tc.oldCRD)
// old crd should never be changed
if diff := cmp.Diff(tc.oldCRD, old); diff != "" {
t.Fatalf("old crd changed from %v to %v", tc.oldCRD, old)
}
if diff := cmp.Diff(tc.expectedCRD, tc.crd); diff != "" {
t.Fatalf("unexpected crd: %v", tc.crd)
}
})
}
}
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
7、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦