mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
apiextensions: add pruning integration tests
This commit is contained in:
parent
3f3ed79484
commit
77bfddacfd
@ -0,0 +1,459 @@
|
||||
/*
|
||||
Copyright 2018 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 integration
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
var pruningFixture = &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.apiextensions.k8s.io"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "tests.apiextensions.k8s.io",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "foos",
|
||||
Singular: "foo",
|
||||
Kind: "Foo",
|
||||
ListKind: "FooList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
fooSchema = `
|
||||
type: object
|
||||
properties:
|
||||
alpha:
|
||||
type: string
|
||||
beta:
|
||||
type: number
|
||||
`
|
||||
|
||||
fooStatusSchema = `
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
alpha:
|
||||
type: string
|
||||
beta:
|
||||
type: number
|
||||
`
|
||||
|
||||
fooSchemaPreservingUnknownFields = `
|
||||
type: object
|
||||
properties:
|
||||
alpha:
|
||||
type: string
|
||||
beta:
|
||||
type: number
|
||||
preserving:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
properties:
|
||||
preserving:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
pruning:
|
||||
type: object
|
||||
pruning:
|
||||
type: object
|
||||
properties:
|
||||
preserving:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
pruning:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
`
|
||||
|
||||
fooInstance = `
|
||||
kind: Foo
|
||||
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: foo
|
||||
`
|
||||
)
|
||||
|
||||
func TestPruningCreate(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating CR and expect 'unspecified' fields to be pruned")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unstructured.SetNestedField(foo.Object, "bar", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "abc", "alpha")
|
||||
unstructured.SetNestedField(foo.Object, float64(42.0), "beta")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "labels", "foo")
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "alpha"); !found {
|
||||
t.Errorf("Expected specified 'alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "beta"); !found {
|
||||
t.Errorf("Expected specified 'beta' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "unspecified"); found {
|
||||
t.Errorf("Expected 'metadata.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "labels", "foo"); !found {
|
||||
t.Errorf("Expected specified 'metadata.labels[foo]' field to stay, but it was pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningStatus(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooStatusSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating CR and expect 'unspecified' fields to be pruned")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
unstructured.SetNestedField(foo.Object, "bar", "status", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "abc", "status", "alpha")
|
||||
unstructured.SetNestedField(foo.Object, float64(42.0), "status", "beta")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "unspecified")
|
||||
|
||||
foo, err = fooClient.UpdateStatus(foo, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to update status: %v", err)
|
||||
}
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'status.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "status", "alpha"); !found {
|
||||
t.Errorf("Expected specified 'status.alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "status", "beta"); !found {
|
||||
t.Errorf("Expected specified 'status.beta' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "unspecified"); found {
|
||||
t.Errorf("Expected 'metadata.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningFromStorage(t *testing.T) {
|
||||
tearDown, config, options, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
serverConfig, err := options.Config()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restOptions, err := serverConfig.GenericConfig.RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlsInfo := transport.TLSInfo{
|
||||
CertFile: restOptions.StorageConfig.Transport.CertFile,
|
||||
KeyFile: restOptions.StorageConfig.Transport.KeyFile,
|
||||
CAFile: restOptions.StorageConfig.Transport.CAFile,
|
||||
}
|
||||
tlsConfig, err := tlsInfo.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
etcdConfig := clientv3.Config{
|
||||
Endpoints: restOptions.StorageConfig.Transport.ServerList,
|
||||
TLS: tlsConfig,
|
||||
}
|
||||
etcdclient, err := clientv3.New(etcdConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating object with unknown field manually in etcd")
|
||||
|
||||
original := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &original.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unstructured.SetNestedField(original.Object, "bar", "unspecified")
|
||||
unstructured.SetNestedField(original.Object, "abc", "alpha")
|
||||
unstructured.SetNestedField(original.Object, float64(42), "beta")
|
||||
unstructured.SetNestedField(original.Object, "bar", "metadata", "labels", "foo")
|
||||
|
||||
// Note: we don't add metadata.unspecified as in the other tests. ObjectMeta pruning is independent of the generic pruning
|
||||
// and we do not guarantee that we prune ObjectMeta on read from etcd.
|
||||
|
||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||
key := path.Join("/", restOptions.StorageConfig.Prefix, crd.Spec.Group, "foos/foo")
|
||||
val, _ := json.Marshal(original.UnstructuredContent())
|
||||
if _, err := etcdclient.Put(ctx, key, string(val)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Checking that CustomResource is pruned from unknown fields")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo, err := fooClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "alpha"); !found {
|
||||
t.Errorf("Expected specified 'alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "beta"); !found {
|
||||
t.Errorf("Expected specified 'beta' field to stay, but it was pruned")
|
||||
}
|
||||
|
||||
// Note: we don't check metadata.foo as in the other tests. ObjectMeta pruning is independent of the generic pruning
|
||||
// and we do not guarantee that we prune ObjectMeta on read from etcd.
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "labels", "foo"); !found {
|
||||
t.Errorf("Expected specified 'metadata.labels[foo]' field to stay, but it was pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningPatch(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
// a patch with a change
|
||||
patch := []byte(`{"alpha": "abc", "beta": 42.0, "unspecified": "bar", "metadata": {"unspecified": "bar", "labels":{"foo":"bar"}}}`)
|
||||
if foo, err = fooClient.Patch("foo", types.MergePatchType, patch, metav1.PatchOptions{}); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "alpha"); !found {
|
||||
t.Errorf("Expected specified 'alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "beta"); !found {
|
||||
t.Errorf("Expected specified 'beta' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "unspecified"); found {
|
||||
t.Errorf("Expected 'metadata.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "labels", "foo"); !found {
|
||||
t.Errorf("Expected specified 'metadata.labels[foo]' field to stay, but it was pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningCreatePreservingUnknownFields(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchemaPreservingUnknownFields), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating CR and expect 'unspecified' field to be pruned")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unstructured.SetNestedField(foo.Object, "bar", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "abc", "alpha")
|
||||
unstructured.SetNestedField(foo.Object, float64(42.0), "beta")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "labels", "foo")
|
||||
unstructured.SetNestedField(foo.Object, map[string]interface{}{
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": map[string]interface{}{"unspecified": "bar"},
|
||||
"pruning": map[string]interface{}{"unspecified": "bar"},
|
||||
"preserving": map[string]interface{}{"unspecified": "bar"},
|
||||
}, "pruning")
|
||||
unstructured.SetNestedField(foo.Object, map[string]interface{}{
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": map[string]interface{}{"unspecified": "bar"},
|
||||
"pruning": map[string]interface{}{"unspecified": "bar"},
|
||||
"preserving": map[string]interface{}{"unspecified": "bar"},
|
||||
}, "preserving")
|
||||
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
for _, pth := range [][]string{
|
||||
{"unspecified"},
|
||||
{"alpha"},
|
||||
{"beta"},
|
||||
{"metadata", "labels", "foo"},
|
||||
|
||||
{"pruning", "pruning"},
|
||||
{"pruning", "preserving"},
|
||||
{"pruning", "preserving", "unspecified"},
|
||||
|
||||
{"preserving", "unspecified"},
|
||||
{"preserving", "unspecifiedObject"},
|
||||
{"preserving", "unspecifiedObject", "unspecified"},
|
||||
{"preserving", "pruning"},
|
||||
{"preserving", "preserving"},
|
||||
{"preserving", "preserving", "unspecified"},
|
||||
} {
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, pth...); !found {
|
||||
t.Errorf("Expected '%s' field to stay, but it was pruned", strings.Join(pth, "."))
|
||||
}
|
||||
}
|
||||
for _, pth := range [][]string{
|
||||
{"metadata", "unspecified"},
|
||||
|
||||
{"pruning", "unspecified"},
|
||||
{"pruning", "unspecifiedObject"},
|
||||
{"pruning", "unspecifiedObject", "unspecified"},
|
||||
{"pruning", "pruning", "unspecified"},
|
||||
|
||||
{"preserving", "pruning", "unspecified"},
|
||||
} {
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, pth...); found {
|
||||
t.Errorf("Expected '%s' field to be pruned, but it was not", strings.Join(pth, "."))
|
||||
}
|
||||
}
|
||||
}
|
@ -181,7 +181,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
||||
defer testcrd.CleanUp()
|
||||
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd)
|
||||
defer webhookCleanup()
|
||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"])
|
||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], false)
|
||||
})
|
||||
|
||||
ginkgo.It("Should deny crd creation", func() {
|
||||
@ -202,6 +202,30 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
||||
testMultiVersionCustomResourceWebhook(f, testcrd)
|
||||
})
|
||||
|
||||
ginkgo.It("Should mutate custom resource with pruning", func() {
|
||||
const prune = true
|
||||
testcrd, err := createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(false)
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"mutation-start": {Type: "string"},
|
||||
"mutation-stage-1": {Type: "string"},
|
||||
// mutation-stage-2 is intentionally missing such that it is pruned
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer testcrd.CleanUp()
|
||||
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd)
|
||||
defer webhookCleanup()
|
||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], prune)
|
||||
})
|
||||
|
||||
ginkgo.It("Should deny crd creation", func() {
|
||||
crdWebhookCleanup := registerValidatingWebhookForCRD(f, context)
|
||||
defer crdWebhookCleanup()
|
||||
@ -1329,7 +1353,7 @@ func testCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1
|
||||
}
|
||||
}
|
||||
|
||||
func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.ResourceInterface) {
|
||||
func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.ResourceInterface, prune bool) {
|
||||
ginkgo.By("Creating a custom resource that should be mutated by the webhook")
|
||||
crName := "cr-instance-1"
|
||||
cr := &unstructured.Unstructured{
|
||||
@ -1350,7 +1374,9 @@ func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextension
|
||||
expectedCRData := map[string]interface{}{
|
||||
"mutation-start": "yes",
|
||||
"mutation-stage-1": "yes",
|
||||
"mutation-stage-2": "yes",
|
||||
}
|
||||
if !prune {
|
||||
expectedCRData["mutation-stage-2"] = "yes"
|
||||
}
|
||||
if !reflect.DeepEqual(expectedCRData, mutatedCR.Object["data"]) {
|
||||
framework.Failf("\nexpected %#v\n, got %#v\n", expectedCRData, mutatedCR.Object["data"])
|
||||
@ -1571,9 +1597,9 @@ func testSlowWebhookTimeoutNoError(f *framework.Framework) {
|
||||
|
||||
// createAdmissionWebhookMultiVersionTestCRDWithV1Storage creates a new CRD specifically
|
||||
// for the admissin webhook calling test.
|
||||
func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framework) (*crd.TestCrd, error) {
|
||||
func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framework, opts ...crd.Option) (*crd.TestCrd, error) {
|
||||
group := fmt.Sprintf("%s-multiversion-crd-test.k8s.io", f.BaseName)
|
||||
return crd.CreateMultiVersionTestCRD(f, group, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
return crd.CreateMultiVersionTestCRD(f, group, append([]crd.Option{func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.Versions = []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1",
|
||||
@ -1586,7 +1612,7 @@ func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framewo
|
||||
Storage: false,
|
||||
},
|
||||
}
|
||||
})
|
||||
}}, opts...)...)
|
||||
}
|
||||
|
||||
// servedAPIVersions returns the API versions served by the CRD.
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
// GetEtcdStorageData returns etcd data for all persisted objects.
|
||||
@ -485,6 +486,10 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
|
||||
ExpectedEtcdPath: "/registry/awesome.bears.com/pandas/cr4panda",
|
||||
ExpectedGVK: gvkP("awesome.bears.com", "v1", "Panda"),
|
||||
},
|
||||
gvr("random.numbers.com", "v1", "integers"): {
|
||||
Stub: `{"kind": "Integer", "apiVersion": "random.numbers.com/v1", "metadata": {"name": "fortytwo"}, "value": 42, "garbage": "oiujnasdf"}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
|
||||
ExpectedEtcdPath: "/registry/random.numbers.com/integers/fortytwo",
|
||||
},
|
||||
// --
|
||||
|
||||
// k8s.io/kubernetes/pkg/apis/auditregistration/v1alpha1
|
||||
@ -580,6 +585,32 @@ func GetCustomResourceDefinitionData() []*apiextensionsv1beta1.CustomResourceDef
|
||||
},
|
||||
},
|
||||
},
|
||||
// cluster scoped with legacy version field and pruning.
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "integers.random.numbers.com",
|
||||
},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "random.numbers.com",
|
||||
Version: "v1",
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "integers",
|
||||
Kind: "Integer",
|
||||
},
|
||||
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"value": {
|
||||
Type: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
// cluster scoped with versions field
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
Loading…
Reference in New Issue
Block a user