mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
e2e: cleanup test/util/crd to decouple tests
This commit is contained in:
parent
746404f82a
commit
a90cab07f9
@ -63,7 +63,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||||
|
@ -106,15 +106,20 @@ var _ = SIGDescribe("CustomResourceConversionWebhook [Feature:CustomResourceWebh
|
|||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("Should be able to convert from CR v1 to CR v2", func() {
|
ginkgo.It("Should be able to convert from CR v1 to CR v2", func() {
|
||||||
testcrd, err := crd.CreateMultiVersionTestCRD(f, "stable.example.com", apiVersions,
|
testcrd, err := crd.CreateMultiVersionTestCRD(f, "stable.example.com", func(crd *v1beta1.CustomResourceDefinition) {
|
||||||
&v1beta1.WebhookClientConfig{
|
crd.Spec.Conversion = &v1beta1.CustomResourceConversion{
|
||||||
CABundle: context.signingCert,
|
Strategy: v1beta1.WebhookConverter,
|
||||||
Service: &v1beta1.ServiceReference{
|
WebhookClientConfig: &v1beta1.WebhookClientConfig{
|
||||||
Namespace: f.Namespace.Name,
|
CABundle: context.signingCert,
|
||||||
Name: serviceCRDName,
|
Service: &v1beta1.ServiceReference{
|
||||||
Path: pointer.StringPtr("/crdconvert"),
|
Namespace: f.Namespace.Name,
|
||||||
Port: pointer.Int32Ptr(serviceCRDPort),
|
Name: serviceCRDName,
|
||||||
}})
|
Path: pointer.StringPtr("/crdconvert"),
|
||||||
|
Port: pointer.Int32Ptr(serviceCRDPort),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -123,15 +128,20 @@ var _ = SIGDescribe("CustomResourceConversionWebhook [Feature:CustomResourceWebh
|
|||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("Should be able to convert a non homogeneous list of CRs", func() {
|
ginkgo.It("Should be able to convert a non homogeneous list of CRs", func() {
|
||||||
testcrd, err := crd.CreateMultiVersionTestCRD(f, "stable.example.com", apiVersions,
|
testcrd, err := crd.CreateMultiVersionTestCRD(f, "stable.example.com", func(crd *v1beta1.CustomResourceDefinition) {
|
||||||
&v1beta1.WebhookClientConfig{
|
crd.Spec.Conversion = &v1beta1.CustomResourceConversion{
|
||||||
CABundle: context.signingCert,
|
Strategy: v1beta1.WebhookConverter,
|
||||||
Service: &v1beta1.ServiceReference{
|
WebhookClientConfig: &v1beta1.WebhookClientConfig{
|
||||||
Namespace: f.Namespace.Name,
|
CABundle: context.signingCert,
|
||||||
Name: serviceCRDName,
|
Service: &v1beta1.ServiceReference{
|
||||||
Path: pointer.StringPtr("/crdconvert"),
|
Namespace: f.Namespace.Name,
|
||||||
Port: pointer.Int32Ptr(serviceCRDPort),
|
Name: serviceCRDName,
|
||||||
}})
|
Path: pointer.StringPtr("/crdconvert"),
|
||||||
|
Port: pointer.Int32Ptr(serviceCRDPort),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilversion "k8s.io/apimachinery/pkg/util/version"
|
utilversion "k8s.io/apimachinery/pkg/util/version"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
|
|
||||||
k8sclientset "k8s.io/client-go/kubernetes"
|
k8sclientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
openapiutil "k8s.io/kube-openapi/pkg/util"
|
openapiutil "k8s.io/kube-openapi/pkg/util"
|
||||||
@ -62,7 +61,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := fmt.Sprintf(metaPattern, crd.Kind, crd.APIGroup, crd.Versions[0].Name, "test-foo")
|
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-foo")
|
||||||
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
|
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
|
||||||
|
|
||||||
ginkgo.By("client-side validation (kubectl create and apply) allows request with known and required properties")
|
ginkgo.By("client-side validation (kubectl create and apply) allows request with known and required properties")
|
||||||
@ -70,13 +69,13 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
if _, err := framework.RunKubectlInput(validCR, ns, "create", "-f", "-"); err != nil {
|
if _, err := framework.RunKubectlInput(validCR, ns, "create", "-f", "-"); err != nil {
|
||||||
framework.Failf("failed to create valid CR %s: %v", validCR, err)
|
framework.Failf("failed to create valid CR %s: %v", validCR, err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectl(ns, "delete", crd.GetPluralName(), "test-foo"); err != nil {
|
if _, err := framework.RunKubectl(ns, "delete", crd.Crd.Spec.Names.Plural, "test-foo"); err != nil {
|
||||||
framework.Failf("failed to delete valid CR: %v", err)
|
framework.Failf("failed to delete valid CR: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectlInput(validCR, ns, "apply", "-f", "-"); err != nil {
|
if _, err := framework.RunKubectlInput(validCR, ns, "apply", "-f", "-"); err != nil {
|
||||||
framework.Failf("failed to apply valid CR %s: %v", validCR, err)
|
framework.Failf("failed to apply valid CR %s: %v", validCR, err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectl(ns, "delete", crd.GetPluralName(), "test-foo"); err != nil {
|
if _, err := framework.RunKubectl(ns, "delete", crd.Crd.Spec.Names.Plural, "test-foo"); err != nil {
|
||||||
framework.Failf("failed to delete valid CR: %v", err)
|
framework.Failf("failed to delete valid CR: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,23 +98,23 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("kubectl explain works to explain CR properties")
|
ginkgo.By("kubectl explain works to explain CR properties")
|
||||||
if err := verifyKubectlExplain(crd.GetPluralName(), `(?s)DESCRIPTION:.*Foo CRD for Testing.*FIELDS:.*apiVersion.*<string>.*APIVersion defines.*spec.*<Object>.*Specification of Foo`); err != nil {
|
if err := verifyKubectlExplain(crd.Crd.Spec.Names.Plural, `(?s)DESCRIPTION:.*Foo CRD for Testing.*FIELDS:.*apiVersion.*<string>.*APIVersion defines.*spec.*<Object>.*Specification of Foo`); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("kubectl explain works to explain CR properties recursively")
|
ginkgo.By("kubectl explain works to explain CR properties recursively")
|
||||||
if err := verifyKubectlExplain(crd.GetPluralName()+".metadata", `(?s)DESCRIPTION:.*Standard object's metadata.*FIELDS:.*creationTimestamp.*<string>.*CreationTimestamp is a timestamp`); err != nil {
|
if err := verifyKubectlExplain(crd.Crd.Spec.Names.Plural+".metadata", `(?s)DESCRIPTION:.*Standard object's metadata.*FIELDS:.*creationTimestamp.*<string>.*CreationTimestamp is a timestamp`); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
if err := verifyKubectlExplain(crd.GetPluralName()+".spec", `(?s)DESCRIPTION:.*Specification of Foo.*FIELDS:.*bars.*<\[\]Object>.*List of Bars and their specs`); err != nil {
|
if err := verifyKubectlExplain(crd.Crd.Spec.Names.Plural+".spec", `(?s)DESCRIPTION:.*Specification of Foo.*FIELDS:.*bars.*<\[\]Object>.*List of Bars and their specs`); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
if err := verifyKubectlExplain(crd.GetPluralName()+".spec.bars", `(?s)RESOURCE:.*bars.*<\[\]Object>.*DESCRIPTION:.*List of Bars and their specs.*FIELDS:.*bazs.*<\[\]string>.*List of Bazs.*name.*<string>.*Name of Bar`); err != nil {
|
if err := verifyKubectlExplain(crd.Crd.Spec.Names.Plural+".spec.bars", `(?s)RESOURCE:.*bars.*<\[\]Object>.*DESCRIPTION:.*List of Bars and their specs.*FIELDS:.*bazs.*<\[\]string>.*List of Bazs.*name.*<string>.*Name of Bar`); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("kubectl explain works to return error when explain is called on property that doesn't exist")
|
ginkgo.By("kubectl explain works to return error when explain is called on property that doesn't exist")
|
||||||
if _, err := framework.RunKubectl("explain", crd.GetPluralName()+".spec.bars2"); err == nil || !strings.Contains(err.Error(), `field "bars2" does not exist`) {
|
if _, err := framework.RunKubectl("explain", crd.Crd.Spec.Names.Plural+".spec.bars2"); err == nil || !strings.Contains(err.Error(), `field "bars2" does not exist`) {
|
||||||
framework.Failf("unexpected no error when explaining property that doesn't exist: %v", err)
|
framework.Failf("unexpected no error when explaining property that doesn't exist: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +129,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := fmt.Sprintf(metaPattern, crd.Kind, crd.APIGroup, crd.Versions[0].Name, "test-cr")
|
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
|
||||||
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
|
ns := fmt.Sprintf("--namespace=%v", f.Namespace.Name)
|
||||||
|
|
||||||
ginkgo.By("client-side validation (kubectl create and apply) allows request with any unknown properties")
|
ginkgo.By("client-side validation (kubectl create and apply) allows request with any unknown properties")
|
||||||
@ -138,18 +137,18 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
if _, err := framework.RunKubectlInput(randomCR, ns, "create", "-f", "-"); err != nil {
|
if _, err := framework.RunKubectlInput(randomCR, ns, "create", "-f", "-"); err != nil {
|
||||||
framework.Failf("failed to create random CR %s for CRD without schema: %v", randomCR, err)
|
framework.Failf("failed to create random CR %s for CRD without schema: %v", randomCR, err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectl(ns, "delete", crd.GetPluralName(), "test-cr"); err != nil {
|
if _, err := framework.RunKubectl(ns, "delete", crd.Crd.Spec.Names.Plural, "test-cr"); err != nil {
|
||||||
framework.Failf("failed to delete random CR: %v", err)
|
framework.Failf("failed to delete random CR: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectlInput(randomCR, ns, "apply", "-f", "-"); err != nil {
|
if _, err := framework.RunKubectlInput(randomCR, ns, "apply", "-f", "-"); err != nil {
|
||||||
framework.Failf("failed to apply random CR %s for CRD without schema: %v", randomCR, err)
|
framework.Failf("failed to apply random CR %s for CRD without schema: %v", randomCR, err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectl(ns, "delete", crd.GetPluralName(), "test-cr"); err != nil {
|
if _, err := framework.RunKubectl(ns, "delete", crd.Crd.Spec.Names.Plural, "test-cr"); err != nil {
|
||||||
framework.Failf("failed to delete random CR: %v", err)
|
framework.Failf("failed to delete random CR: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("kubectl explain works to explain CR without validation schema")
|
ginkgo.By("kubectl explain works to explain CR without validation schema")
|
||||||
if err := verifyKubectlExplain(crd.GetPluralName(), `(?s)DESCRIPTION:.*<empty>`); err != nil {
|
if err := verifyKubectlExplain(crd.Crd.Spec.Names.Plural, `(?s)DESCRIPTION:.*<empty>`); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,8 +167,8 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
if crdFoo.APIGroup == crdWaldo.APIGroup {
|
if crdFoo.Crd.Spec.Group == crdWaldo.Crd.Spec.Group {
|
||||||
framework.Failf("unexpected: CRDs should be of different group %v, %v", crdFoo.APIGroup, crdWaldo.APIGroup)
|
framework.Failf("unexpected: CRDs should be of different group %v, %v", crdFoo.Crd.Spec.Group, crdWaldo.Crd.Spec.Group)
|
||||||
}
|
}
|
||||||
if err := waitForDefinition(f.ClientSet, definitionName(crdWaldo, "v1beta1"), schemaWaldo); err != nil {
|
if err := waitForDefinition(f.ClientSet, definitionName(crdWaldo, "v1beta1"), schemaWaldo); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
@ -210,8 +209,8 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
if crdFoo.APIGroup != crdWaldo.APIGroup {
|
if crdFoo.Crd.Spec.Group != crdWaldo.Crd.Spec.Group {
|
||||||
framework.Failf("unexpected: CRDs should be of the same group %v, %v", crdFoo.APIGroup, crdWaldo.APIGroup)
|
framework.Failf("unexpected: CRDs should be of the same group %v, %v", crdFoo.Crd.Spec.Group, crdWaldo.Crd.Spec.Group)
|
||||||
}
|
}
|
||||||
if err := waitForDefinition(f.ClientSet, definitionName(crdWaldo, "v5"), schemaWaldo); err != nil {
|
if err := waitForDefinition(f.ClientSet, definitionName(crdWaldo, "v5"), schemaWaldo); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
@ -237,8 +236,8 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
if crdFoo.APIGroup != crdWaldo.APIGroup {
|
if crdFoo.Crd.Spec.Group != crdWaldo.Crd.Spec.Group {
|
||||||
framework.Failf("unexpected: CRDs should be of the same group %v, %v", crdFoo.APIGroup, crdWaldo.APIGroup)
|
framework.Failf("unexpected: CRDs should be of the same group %v, %v", crdFoo.Crd.Spec.Group, crdWaldo.Crd.Spec.Group)
|
||||||
}
|
}
|
||||||
if err := waitForDefinition(f.ClientSet, definitionName(crdWaldo, "v6"), schemaWaldo); err != nil {
|
if err := waitForDefinition(f.ClientSet, definitionName(crdWaldo, "v6"), schemaWaldo); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
@ -269,7 +268,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
|
|
||||||
ginkgo.By("rename a version")
|
ginkgo.By("rename a version")
|
||||||
patch := []byte(`{"spec":{"versions":[{"name":"v2","served":true,"storage":true},{"name":"v4","served":true,"storage":false}]}}`)
|
patch := []byte(`{"spec":{"versions":[{"name":"v2","served":true,"storage":true},{"name":"v4","served":true,"storage":false}]}}`)
|
||||||
crdMultiVer.Crd, err = crdMultiVer.APIExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crdMultiVer.GetMetaName(), types.MergePatchType, patch)
|
crdMultiVer.Crd, err = crdMultiVer.APIExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crdMultiVer.Crd.Name, types.MergePatchType, patch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
@ -289,7 +288,7 @@ var _ = SIGDescribe("CustomResourcePublishOpenAPI [Feature:CustomResourcePublish
|
|||||||
|
|
||||||
// TestCrd.Versions is different from TestCrd.Crd.Versions, we have to manually
|
// TestCrd.Versions is different from TestCrd.Crd.Versions, we have to manually
|
||||||
// update the name there. Used by cleanupCRD
|
// update the name there. Used by cleanupCRD
|
||||||
crdMultiVer.Versions[1].Name = "v4"
|
crdMultiVer.Crd.Spec.Versions[1].Name = "v4"
|
||||||
if err := cleanupCRD(f, crdMultiVer); err != nil {
|
if err := cleanupCRD(f, crdMultiVer); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
}
|
}
|
||||||
@ -336,33 +335,36 @@ func setupCRD(f *framework.Framework, schema []byte, groupSuffix string, version
|
|||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
return nil, fmt.Errorf("require at least one version for CRD")
|
return nil, fmt.Errorf("require at least one version for CRD")
|
||||||
}
|
}
|
||||||
apiVersions := []v1beta1.CustomResourceDefinitionVersion{}
|
|
||||||
for _, version := range versions {
|
|
||||||
v := v1beta1.CustomResourceDefinitionVersion{
|
|
||||||
Name: version,
|
|
||||||
Served: true,
|
|
||||||
Storage: false,
|
|
||||||
}
|
|
||||||
apiVersions = append(apiVersions, v)
|
|
||||||
}
|
|
||||||
apiVersions[0].Storage = true
|
|
||||||
|
|
||||||
crd, err := crd.CreateMultiVersionTestCRD(f, group, apiVersions, nil)
|
if schema == nil {
|
||||||
|
schema = []byte(`type: object`)
|
||||||
|
}
|
||||||
|
props := &v1beta1.JSONSchemaProps{}
|
||||||
|
if err := yaml.Unmarshal(schema, props); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
crd, err := crd.CreateMultiVersionTestCRD(f, group, func(crd *v1beta1.CustomResourceDefinition) {
|
||||||
|
apiVersions := []v1beta1.CustomResourceDefinitionVersion{}
|
||||||
|
for _, version := range versions {
|
||||||
|
v := v1beta1.CustomResourceDefinitionVersion{
|
||||||
|
Name: version,
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
}
|
||||||
|
apiVersions = append(apiVersions, v)
|
||||||
|
}
|
||||||
|
apiVersions[0].Storage = true
|
||||||
|
|
||||||
|
crd.Spec.Validation = &v1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: props,
|
||||||
|
}
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create CRD: %v", err)
|
return nil, fmt.Errorf("failed to create CRD: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if schema != nil {
|
for _, v := range crd.Crd.Spec.Versions {
|
||||||
// patch validation schema for all versions
|
|
||||||
if err := patchSchema(schema, crd); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to patch schema: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// change expectation if CRD doesn't have schema
|
|
||||||
schema = []byte(`type: object`)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range crd.Versions {
|
|
||||||
if err := waitForDefinition(f.ClientSet, definitionName(crd, v.Name), schema); err != nil {
|
if err := waitForDefinition(f.ClientSet, definitionName(crd, v.Name), schema); err != nil {
|
||||||
return nil, fmt.Errorf("%v", err)
|
return nil, fmt.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
@ -372,7 +374,7 @@ func setupCRD(f *framework.Framework, schema []byte, groupSuffix string, version
|
|||||||
|
|
||||||
func cleanupCRD(f *framework.Framework, crd *crd.TestCrd) error {
|
func cleanupCRD(f *framework.Framework, crd *crd.TestCrd) error {
|
||||||
crd.CleanUp()
|
crd.CleanUp()
|
||||||
for _, v := range crd.Versions {
|
for _, v := range crd.Crd.Spec.Versions {
|
||||||
name := definitionName(crd, v.Name)
|
name := definitionName(crd, v.Name)
|
||||||
if err := waitForDefinitionCleanup(f.ClientSet, name); err != nil {
|
if err := waitForDefinitionCleanup(f.ClientSet, name); err != nil {
|
||||||
return fmt.Errorf("%v", err)
|
return fmt.Errorf("%v", err)
|
||||||
@ -381,17 +383,6 @@ func cleanupCRD(f *framework.Framework, crd *crd.TestCrd) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// patchSchema takes schema in YAML and patches it to given CRD in given version
|
|
||||||
func patchSchema(schema []byte, crd *crd.TestCrd) error {
|
|
||||||
s, err := utilyaml.ToJSON(schema)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create json patch: %v", err)
|
|
||||||
}
|
|
||||||
patch := []byte(fmt.Sprintf(`{"spec":{"validation":{"openAPIV3Schema":%s}}}`, string(s)))
|
|
||||||
crd.Crd, err = crd.APIExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crd.GetMetaName(), types.MergePatchType, patch)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const waitSuccessThreshold = 10
|
const waitSuccessThreshold = 10
|
||||||
|
|
||||||
// mustSucceedMultipleTimes calls f multiple times on success and only returns true if all calls are successful.
|
// mustSucceedMultipleTimes calls f multiple times on success and only returns true if all calls are successful.
|
||||||
@ -530,7 +521,7 @@ func verifyKubectlExplain(name, pattern string) error {
|
|||||||
|
|
||||||
// definitionName returns the openapi definition name for given CRD in given version
|
// definitionName returns the openapi definition name for given CRD in given version
|
||||||
func definitionName(crd *crd.TestCrd, version string) string {
|
func definitionName(crd *crd.TestCrd, version string) string {
|
||||||
return openapiutil.ToRESTFriendlyName(fmt.Sprintf("%s/%s/%s", crd.APIGroup, version, crd.Kind))
|
return openapiutil.ToRESTFriendlyName(fmt.Sprintf("%s/%s/%s", crd.Crd.Spec.Group, version, crd.Crd.Spec.Names.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
var schemaFoo = []byte(`description: Foo CRD for Testing
|
var schemaFoo = []byte(`description: Foo CRD for Testing
|
||||||
|
@ -532,10 +532,10 @@ var _ = SIGDescribe("ResourceQuota", func() {
|
|||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
ginkgo.By("Creating a custom resource")
|
ginkgo.By("Creating a custom resource")
|
||||||
resourceClient := testcrd.GetV1DynamicClient()
|
resourceClient := testcrd.DynamicClients["v1"]
|
||||||
testcr, err := instantiateCustomResource(&unstructured.Unstructured{
|
testcr, err := instantiateCustomResource(&unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]interface{}{
|
||||||
"apiVersion": testcrd.APIGroup + "/" + testcrd.GetAPIVersions()[0],
|
"apiVersion": testcrd.Crd.Spec.Group + "/" + testcrd.Crd.Spec.Versions[0].Name,
|
||||||
"kind": testcrd.Crd.Spec.Names.Kind,
|
"kind": testcrd.Crd.Spec.Names.Kind,
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "test-cr-1",
|
"name": "test-cr-1",
|
||||||
@ -553,7 +553,7 @@ var _ = SIGDescribe("ResourceQuota", func() {
|
|||||||
ginkgo.By("Creating a second custom resource")
|
ginkgo.By("Creating a second custom resource")
|
||||||
_, err = instantiateCustomResource(&unstructured.Unstructured{
|
_, err = instantiateCustomResource(&unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]interface{}{
|
||||||
"apiVersion": testcrd.APIGroup + "/" + testcrd.GetAPIVersions()[0],
|
"apiVersion": testcrd.Crd.Spec.Group + "/" + testcrd.Crd.Spec.Versions[0].Name,
|
||||||
"kind": testcrd.Crd.Spec.Names.Kind,
|
"kind": testcrd.Crd.Spec.Names.Kind,
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "test-cr-2",
|
"name": "test-cr-2",
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
apps "k8s.io/api/apps/v1"
|
apps "k8s.io/api/apps/v1"
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
crdclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
crdclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
@ -46,6 +46,7 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
"github.com/onsi/ginkgo"
|
||||||
"github.com/onsi/gomega"
|
"github.com/onsi/gomega"
|
||||||
|
|
||||||
// ensure libs have a chance to initialize
|
// ensure libs have a chance to initialize
|
||||||
_ "github.com/stretchr/testify/assert"
|
_ "github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -143,7 +144,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
|||||||
defer testcrd.CleanUp()
|
defer testcrd.CleanUp()
|
||||||
webhookCleanup := registerWebhookForCustomResource(f, context, testcrd)
|
webhookCleanup := registerWebhookForCustomResource(f, context, testcrd)
|
||||||
defer webhookCleanup()
|
defer webhookCleanup()
|
||||||
testCustomResourceWebhook(f, testcrd.Crd, testcrd.GetV1DynamicClient())
|
testCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"])
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("Should unconditionally reject operations on fail closed webhook", func() {
|
ginkgo.It("Should unconditionally reject operations on fail closed webhook", func() {
|
||||||
@ -180,7 +181,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
|||||||
defer testcrd.CleanUp()
|
defer testcrd.CleanUp()
|
||||||
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd)
|
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd)
|
||||||
defer webhookCleanup()
|
defer webhookCleanup()
|
||||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.GetV1DynamicClient())
|
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"])
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("Should deny crd creation", func() {
|
ginkgo.It("Should deny crd creation", func() {
|
||||||
@ -191,7 +192,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("Should mutate custom resource with different stored version", func() {
|
ginkgo.It("Should mutate custom resource with different stored version", func() {
|
||||||
testcrd, err := crd.CreateMultiVersionTestCRDWithV1Storage(f)
|
testcrd, err := createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1217,9 +1218,9 @@ func registerWebhookForCustomResource(f *framework.Framework, context *certConte
|
|||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{v1beta1.Create, v1beta1.Update},
|
Operations: []v1beta1.OperationType{v1beta1.Create, v1beta1.Update},
|
||||||
Rule: v1beta1.Rule{
|
Rule: v1beta1.Rule{
|
||||||
APIGroups: []string{testcrd.APIGroup},
|
APIGroups: []string{testcrd.Crd.Spec.Group},
|
||||||
APIVersions: testcrd.GetAPIVersions(),
|
APIVersions: servedAPIVersions(testcrd.Crd),
|
||||||
Resources: []string{testcrd.GetPluralName()},
|
Resources: []string{testcrd.Crd.Spec.Names.Plural},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
ClientConfig: v1beta1.WebhookClientConfig{
|
ClientConfig: v1beta1.WebhookClientConfig{
|
||||||
@ -1259,9 +1260,9 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c
|
|||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{v1beta1.Create, v1beta1.Update},
|
Operations: []v1beta1.OperationType{v1beta1.Create, v1beta1.Update},
|
||||||
Rule: v1beta1.Rule{
|
Rule: v1beta1.Rule{
|
||||||
APIGroups: []string{testcrd.APIGroup},
|
APIGroups: []string{testcrd.Crd.Spec.Group},
|
||||||
APIVersions: testcrd.GetAPIVersions(),
|
APIVersions: servedAPIVersions(testcrd.Crd),
|
||||||
Resources: []string{testcrd.GetPluralName()},
|
Resources: []string{testcrd.Crd.Spec.Names.Plural},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
ClientConfig: v1beta1.WebhookClientConfig{
|
ClientConfig: v1beta1.WebhookClientConfig{
|
||||||
@ -1279,9 +1280,9 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c
|
|||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{v1beta1.Create},
|
Operations: []v1beta1.OperationType{v1beta1.Create},
|
||||||
Rule: v1beta1.Rule{
|
Rule: v1beta1.Rule{
|
||||||
APIGroups: []string{testcrd.APIGroup},
|
APIGroups: []string{testcrd.Crd.Spec.Group},
|
||||||
APIVersions: testcrd.GetAPIVersions(),
|
APIVersions: servedAPIVersions(testcrd.Crd),
|
||||||
Resources: []string{testcrd.GetPluralName()},
|
Resources: []string{testcrd.Crd.Spec.Names.Plural},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
ClientConfig: v1beta1.WebhookClientConfig{
|
ClientConfig: v1beta1.WebhookClientConfig{
|
||||||
@ -1357,7 +1358,7 @@ func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextension
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testMultiVersionCustomResourceWebhook(f *framework.Framework, testcrd *crd.TestCrd) {
|
func testMultiVersionCustomResourceWebhook(f *framework.Framework, testcrd *crd.TestCrd) {
|
||||||
customResourceClient := testcrd.GetV1DynamicClient()
|
customResourceClient := testcrd.DynamicClients["v1"]
|
||||||
ginkgo.By("Creating a custom resource while v1 is storage version")
|
ginkgo.By("Creating a custom resource while v1 is storage version")
|
||||||
crName := "cr-instance-1"
|
crName := "cr-instance-1"
|
||||||
cr := &unstructured.Unstructured{
|
cr := &unstructured.Unstructured{
|
||||||
@ -1446,12 +1447,6 @@ func testCRDDenyWebhook(f *framework.Framework) {
|
|||||||
Storage: true,
|
Storage: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testcrd := &crd.TestCrd{
|
|
||||||
Name: name,
|
|
||||||
Kind: kind,
|
|
||||||
APIGroup: group,
|
|
||||||
Versions: apiVersions,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating a custom resource definition for use by assorted tests.
|
// Creating a custom resource definition for use by assorted tests.
|
||||||
config, err := framework.LoadConfig()
|
config, err := framework.LoadConfig()
|
||||||
@ -1466,19 +1461,19 @@ func testCRDDenyWebhook(f *framework.Framework) {
|
|||||||
}
|
}
|
||||||
crd := &apiextensionsv1beta1.CustomResourceDefinition{
|
crd := &apiextensionsv1beta1.CustomResourceDefinition{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: testcrd.GetMetaName(),
|
Name: name + "s." + group,
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"webhook-e2e-test": "webhook-disallow",
|
"webhook-e2e-test": "webhook-disallow",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
Group: testcrd.APIGroup,
|
Group: group,
|
||||||
Versions: testcrd.Versions,
|
Versions: apiVersions,
|
||||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||||
Plural: testcrd.GetPluralName(),
|
Singular: name,
|
||||||
Singular: testcrd.Name,
|
Kind: kind,
|
||||||
Kind: testcrd.Kind,
|
ListKind: kind + "List",
|
||||||
ListKind: testcrd.GetListName(),
|
Plural: name + "s",
|
||||||
},
|
},
|
||||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||||
},
|
},
|
||||||
@ -1486,7 +1481,7 @@ func testCRDDenyWebhook(f *framework.Framework) {
|
|||||||
|
|
||||||
// create CRD
|
// create CRD
|
||||||
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
||||||
gomega.Expect(err).To(gomega.HaveOccurred(), "create custom resource definition %s should be denied by webhook", testcrd.GetMetaName())
|
gomega.Expect(err).To(gomega.HaveOccurred(), "create custom resource definition %s should be denied by webhook", crd.Name)
|
||||||
expectedErrMsg := "the crd contains unwanted label"
|
expectedErrMsg := "the crd contains unwanted label"
|
||||||
if !strings.Contains(err.Error(), expectedErrMsg) {
|
if !strings.Contains(err.Error(), expectedErrMsg) {
|
||||||
framework.Failf("expect error contains %q, got %q", expectedErrMsg, err.Error())
|
framework.Failf("expect error contains %q, got %q", expectedErrMsg, err.Error())
|
||||||
@ -1573,3 +1568,34 @@ func testSlowWebhookTimeoutNoError(f *framework.Framework) {
|
|||||||
err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(name, &metav1.DeleteOptions{})
|
err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(name, &metav1.DeleteOptions{})
|
||||||
gomega.Expect(err).To(gomega.BeNil())
|
gomega.Expect(err).To(gomega.BeNil())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createAdmissionWebhookMultiVersionTestCRDWithV1Storage creates a new CRD specifically
|
||||||
|
// for the admissin webhook calling test.
|
||||||
|
func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framework) (*crd.TestCrd, error) {
|
||||||
|
group := fmt.Sprintf("%s-multiversion-crd-test.k8s.io", f.BaseName)
|
||||||
|
return crd.CreateMultiVersionTestCRD(f, group, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||||
|
crd.Spec.Versions = []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "v1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// servedAPIVersions returns the API versions served by the CRD.
|
||||||
|
func servedAPIVersions(crd *apiextensionsv1beta1.CustomResourceDefinition) []string {
|
||||||
|
ret := []string{}
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if v.Served {
|
||||||
|
ret = append(ret, v.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ go_library(
|
|||||||
"//pkg/kubectl/polymorphichelpers:go_default_library",
|
"//pkg/kubectl/polymorphichelpers:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/rbac/v1beta1:go_default_library",
|
"//staging/src/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
@ -44,6 +44,7 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -69,6 +70,7 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
"github.com/onsi/ginkgo"
|
||||||
"github.com/onsi/gomega"
|
"github.com/onsi/gomega"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
|
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
)
|
)
|
||||||
@ -861,7 +863,7 @@ metadata:
|
|||||||
ginkgo.By("sleep for 10s to wait for potential crd openapi publishing alpha feature")
|
ginkgo.By("sleep for 10s to wait for potential crd openapi publishing alpha feature")
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
meta := fmt.Sprintf(metaPattern, crd.Kind, crd.APIGroup, crd.Versions[0].Name, "test-cr")
|
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
|
||||||
randomCR := fmt.Sprintf(`{%s,"a":{"b":[{"c":"d"}]}}`, meta)
|
randomCR := fmt.Sprintf(`{%s,"a":{"b":[{"c":"d"}]}}`, meta)
|
||||||
if err := createApplyCustomResource(randomCR, f.Namespace.Name, "test-cr", crd); err != nil {
|
if err := createApplyCustomResource(randomCR, f.Namespace.Name, "test-cr", crd); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
@ -870,19 +872,21 @@ metadata:
|
|||||||
|
|
||||||
ginkgo.It("should create/apply a valid CR for CRD with validation schema", func() {
|
ginkgo.It("should create/apply a valid CR for CRD with validation schema", func() {
|
||||||
ginkgo.By("prepare CRD with validation schema")
|
ginkgo.By("prepare CRD with validation schema")
|
||||||
crd, err := crd.CreateTestCRD(f)
|
crd, err := crd.CreateTestCRD(f, func(crd *v1beta1.CustomResourceDefinition) {
|
||||||
|
props := &v1beta1.JSONSchemaProps{}
|
||||||
|
if err := yaml.Unmarshal(schemaFoo, props); err != nil {
|
||||||
|
framework.Failf("failed to unmarshal schema: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("failed to create test CRD: %v", err)
|
framework.Failf("failed to create test CRD: %v", err)
|
||||||
}
|
}
|
||||||
defer crd.CleanUp()
|
defer crd.CleanUp()
|
||||||
if err := crd.PatchSchema(schemaFoo); err != nil {
|
|
||||||
framework.Failf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.By("sleep for 10s to wait for potential crd openapi publishing alpha feature")
|
ginkgo.By("sleep for 10s to wait for potential crd openapi publishing alpha feature")
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
meta := fmt.Sprintf(metaPattern, crd.Kind, crd.APIGroup, crd.Versions[0].Name, "test-cr")
|
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
|
||||||
validCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}]}}`, meta)
|
validCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}]}}`, meta)
|
||||||
if err := createApplyCustomResource(validCR, f.Namespace.Name, "test-cr", crd); err != nil {
|
if err := createApplyCustomResource(validCR, f.Namespace.Name, "test-cr", crd); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
@ -891,19 +895,21 @@ metadata:
|
|||||||
|
|
||||||
ginkgo.It("should create/apply a valid CR with arbitrary-extra properties for CRD with partially-specified validation schema", func() {
|
ginkgo.It("should create/apply a valid CR with arbitrary-extra properties for CRD with partially-specified validation schema", func() {
|
||||||
ginkgo.By("prepare CRD with partially-specified validation schema")
|
ginkgo.By("prepare CRD with partially-specified validation schema")
|
||||||
crd, err := crd.CreateTestCRD(f)
|
crd, err := crd.CreateTestCRD(f, func(crd *v1beta1.CustomResourceDefinition) {
|
||||||
|
props := &v1beta1.JSONSchemaProps{}
|
||||||
|
if err := yaml.Unmarshal(schemaFoo, props); err != nil {
|
||||||
|
framework.Failf("failed to unmarshal schema: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Failf("failed to create test CRD: %v", err)
|
framework.Failf("failed to create test CRD: %v", err)
|
||||||
}
|
}
|
||||||
defer crd.CleanUp()
|
defer crd.CleanUp()
|
||||||
if err := crd.PatchSchema(schemaFoo); err != nil {
|
|
||||||
framework.Failf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.By("sleep for 10s to wait for potential crd openapi publishing alpha feature")
|
ginkgo.By("sleep for 10s to wait for potential crd openapi publishing alpha feature")
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
meta := fmt.Sprintf(metaPattern, crd.Kind, crd.APIGroup, crd.Versions[0].Name, "test-cr")
|
meta := fmt.Sprintf(metaPattern, crd.Crd.Spec.Names.Kind, crd.Crd.Spec.Group, crd.Crd.Spec.Versions[0].Name, "test-cr")
|
||||||
validArbitraryCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}],"extraProperty":"arbitrary-value"}}`, meta)
|
validArbitraryCR := fmt.Sprintf(`{%s,"spec":{"bars":[{"name":"test-bar"}],"extraProperty":"arbitrary-value"}}`, meta)
|
||||||
if err := createApplyCustomResource(validArbitraryCR, f.Namespace.Name, "test-cr", crd); err != nil {
|
if err := createApplyCustomResource(validArbitraryCR, f.Namespace.Name, "test-cr", crd); err != nil {
|
||||||
framework.Failf("%v", err)
|
framework.Failf("%v", err)
|
||||||
@ -2258,14 +2264,14 @@ func createApplyCustomResource(resource, namespace, name string, crd *crd.TestCr
|
|||||||
if _, err := framework.RunKubectlInput(resource, ns, "create", "-f", "-"); err != nil {
|
if _, err := framework.RunKubectlInput(resource, ns, "create", "-f", "-"); err != nil {
|
||||||
return fmt.Errorf("failed to create CR %s in namespace %s: %v", resource, ns, err)
|
return fmt.Errorf("failed to create CR %s in namespace %s: %v", resource, ns, err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectl(ns, "delete", crd.GetPluralName(), name); err != nil {
|
if _, err := framework.RunKubectl(ns, "delete", crd.Crd.Spec.Names.Plural, name); err != nil {
|
||||||
return fmt.Errorf("failed to delete CR %s: %v", name, err)
|
return fmt.Errorf("failed to delete CR %s: %v", name, err)
|
||||||
}
|
}
|
||||||
ginkgo.By("successfully apply CR")
|
ginkgo.By("successfully apply CR")
|
||||||
if _, err := framework.RunKubectlInput(resource, ns, "apply", "-f", "-"); err != nil {
|
if _, err := framework.RunKubectlInput(resource, ns, "apply", "-f", "-"); err != nil {
|
||||||
return fmt.Errorf("failed to apply CR %s in namespace %s: %v", resource, ns, err)
|
return fmt.Errorf("failed to apply CR %s in namespace %s: %v", resource, ns, err)
|
||||||
}
|
}
|
||||||
if _, err := framework.RunKubectl(ns, "delete", crd.GetPluralName(), name); err != nil {
|
if _, err := framework.RunKubectl(ns, "delete", crd.Crd.Spec.Names.Plural, name); err != nil {
|
||||||
return fmt.Errorf("failed to delete CR %s: %v", name, err)
|
return fmt.Errorf("failed to delete CR %s: %v", name, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -11,8 +11,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apiextensions-apiserver/test/integration/fixtures:go_default_library",
|
"//staging/src/k8s.io/apiextensions-apiserver/test/integration/fixtures:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
|
||||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
||||||
"//test/e2e/framework:go_default_library",
|
"//test/e2e/framework:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -24,8 +24,6 @@ import (
|
|||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
)
|
)
|
||||||
@ -35,27 +33,21 @@ type CleanCrdFn func() error
|
|||||||
|
|
||||||
// TestCrd holds all the pieces needed to test with the CRD
|
// TestCrd holds all the pieces needed to test with the CRD
|
||||||
type TestCrd struct {
|
type TestCrd struct {
|
||||||
Name string
|
|
||||||
Kind string
|
|
||||||
APIGroup string
|
|
||||||
Versions []apiextensionsv1beta1.CustomResourceDefinitionVersion
|
|
||||||
APIExtensionClient *crdclientset.Clientset
|
APIExtensionClient *crdclientset.Clientset
|
||||||
Crd *apiextensionsv1beta1.CustomResourceDefinition
|
Crd *apiextensionsv1beta1.CustomResourceDefinition
|
||||||
DynamicClients map[string]dynamic.ResourceInterface
|
DynamicClients map[string]dynamic.ResourceInterface
|
||||||
CleanUp CleanCrdFn
|
CleanUp CleanCrdFn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Option is a modifier for a CRD object used to customize CreateMultiVersionTestCRD and CreateTestCRD.
|
||||||
|
type Option func(crd *apiextensionsv1beta1.CustomResourceDefinition)
|
||||||
|
|
||||||
// CreateMultiVersionTestCRD creates a new CRD specifically for the calling test.
|
// CreateMultiVersionTestCRD creates a new CRD specifically for the calling test.
|
||||||
func CreateMultiVersionTestCRD(f *framework.Framework, group string, apiVersions []apiextensionsv1beta1.CustomResourceDefinitionVersion, conversionWebhook *apiextensionsv1beta1.WebhookClientConfig) (*TestCrd, error) {
|
func CreateMultiVersionTestCRD(f *framework.Framework, group string, opts ...Option) (*TestCrd, error) {
|
||||||
suffix := framework.RandomSuffix()
|
suffix := framework.RandomSuffix()
|
||||||
name := fmt.Sprintf("e2e-test-%s-%s-crd", f.BaseName, suffix)
|
name := fmt.Sprintf("e2e-test-%s-%s-crd", f.BaseName, suffix)
|
||||||
kind := fmt.Sprintf("E2e-test-%s-%s-crd", f.BaseName, suffix)
|
kind := fmt.Sprintf("E2e-test-%s-%s-crd", f.BaseName, suffix)
|
||||||
testcrd := &TestCrd{
|
testcrd := &TestCrd{}
|
||||||
Name: name,
|
|
||||||
Kind: kind,
|
|
||||||
APIGroup: group,
|
|
||||||
Versions: apiVersions,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating a custom resource definition for use by assorted tests.
|
// Creating a custom resource definition for use by assorted tests.
|
||||||
config, err := framework.LoadConfig()
|
config, err := framework.LoadConfig()
|
||||||
@ -74,13 +66,24 @@ func CreateMultiVersionTestCRD(f *framework.Framework, group string, apiVersions
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
crd := newCRDForTest(testcrd)
|
crd := &apiextensionsv1beta1.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: name + "s." + group},
|
||||||
if conversionWebhook != nil {
|
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||||
crd.Spec.Conversion = &apiextensionsv1beta1.CustomResourceConversion{
|
Group: group,
|
||||||
Strategy: "Webhook",
|
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||||
WebhookClientConfig: conversionWebhook,
|
Plural: name + "s",
|
||||||
}
|
Singular: name,
|
||||||
|
Kind: kind,
|
||||||
|
ListKind: kind + "List",
|
||||||
|
},
|
||||||
|
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(crd)
|
||||||
|
}
|
||||||
|
if len(crd.Spec.Versions) == 0 && len(crd.Spec.Version) == 0 {
|
||||||
|
crd.Spec.Version = "v1"
|
||||||
}
|
}
|
||||||
|
|
||||||
//create CRD and waits for the resource to be recognized and available.
|
//create CRD and waits for the resource to be recognized and available.
|
||||||
@ -112,92 +115,15 @@ func CreateMultiVersionTestCRD(f *framework.Framework, group string, apiVersions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateTestCRD creates a new CRD specifically for the calling test.
|
// CreateTestCRD creates a new CRD specifically for the calling test.
|
||||||
func CreateTestCRD(f *framework.Framework) (*TestCrd, error) {
|
func CreateTestCRD(f *framework.Framework, opts ...Option) (*TestCrd, error) {
|
||||||
group := fmt.Sprintf("%s-crd-test.k8s.io", f.BaseName)
|
group := fmt.Sprintf("%s-crd-test.k8s.io", f.BaseName)
|
||||||
apiVersions := []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
return CreateMultiVersionTestCRD(f, group, append([]Option{func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||||
{
|
crd.Spec.Versions = []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
Name: "v1",
|
{
|
||||||
Served: true,
|
Name: "v1",
|
||||||
Storage: true,
|
Served: true,
|
||||||
},
|
Storage: true,
|
||||||
}
|
|
||||||
return CreateMultiVersionTestCRD(f, group, apiVersions, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMultiVersionTestCRDWithV1Storage creates a new CRD specifically for the calling test.
|
|
||||||
func CreateMultiVersionTestCRDWithV1Storage(f *framework.Framework) (*TestCrd, error) {
|
|
||||||
group := fmt.Sprintf("%s-multiversion-crd-test.k8s.io", f.BaseName)
|
|
||||||
apiVersions := []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
|
||||||
{
|
|
||||||
Name: "v1",
|
|
||||||
Served: true,
|
|
||||||
Storage: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "v2",
|
|
||||||
Served: true,
|
|
||||||
Storage: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return CreateMultiVersionTestCRD(f, group, apiVersions, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newCRDForTest generates a CRD definition for the test
|
|
||||||
func newCRDForTest(testcrd *TestCrd) *apiextensionsv1beta1.CustomResourceDefinition {
|
|
||||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: testcrd.GetMetaName()},
|
|
||||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
|
||||||
Group: testcrd.APIGroup,
|
|
||||||
Versions: testcrd.Versions,
|
|
||||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
|
||||||
Plural: testcrd.GetPluralName(),
|
|
||||||
Singular: testcrd.Name,
|
|
||||||
Kind: testcrd.Kind,
|
|
||||||
ListKind: testcrd.GetListName(),
|
|
||||||
},
|
},
|
||||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMetaName returns the metaname for the CRD.
|
|
||||||
func (c *TestCrd) GetMetaName() string {
|
|
||||||
return c.Name + "s." + c.APIGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPluralName returns the plural form of the CRD name
|
|
||||||
func (c *TestCrd) GetPluralName() string {
|
|
||||||
return c.Name + "s"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetListName returns the name for the CRD list resources
|
|
||||||
func (c *TestCrd) GetListName() string {
|
|
||||||
return c.Name + "List"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAPIVersions returns the API versions served by the CRD.
|
|
||||||
func (c *TestCrd) GetAPIVersions() []string {
|
|
||||||
ret := []string{}
|
|
||||||
for _, v := range c.Versions {
|
|
||||||
if v.Served {
|
|
||||||
ret = append(ret, v.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}}, opts...)...)
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetV1DynamicClient returns the dynamic client for v1.
|
|
||||||
func (c *TestCrd) GetV1DynamicClient() dynamic.ResourceInterface {
|
|
||||||
return c.DynamicClients["v1"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// PatchSchema takes validation schema in YAML and patches it to given CRD
|
|
||||||
func (c *TestCrd) PatchSchema(schema []byte) error {
|
|
||||||
s, err := utilyaml.ToJSON(schema)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create json patch: %v", err)
|
|
||||||
}
|
|
||||||
patch := []byte(fmt.Sprintf(`{"spec":{"validation":{"openAPIV3Schema":%s}}}`, string(s)))
|
|
||||||
c.Crd, err = c.APIExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(c.GetMetaName(), types.MergePatchType, patch)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user