mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
apiextensions: add x-kubernetes-embedded-resource integration tests
This commit is contained in:
parent
35054fa7ec
commit
e69f44e28b
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/clientv3"
|
"github.com/coreos/etcd/clientv3"
|
||||||
"github.com/coreos/etcd/pkg/transport"
|
"github.com/coreos/etcd/pkg/transport"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
@ -33,9 +34,11 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
"k8s.io/apimachinery/pkg/util/json"
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPostInvalidObjectMeta(t *testing.T) {
|
func TestPostInvalidObjectMeta(t *testing.T) {
|
||||||
@ -99,6 +102,17 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
|
||||||
|
noxuDefinition.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||||
|
"embedded": {
|
||||||
|
Type: "object",
|
||||||
|
XEmbeddedResource: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -127,11 +141,17 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Creating object with invalid labels manually in etcd")
|
t.Logf("Creating object with wrongly typed annotations and non-validating labels manually in etcd")
|
||||||
|
|
||||||
original := fixtures.NewNoxuInstance("default", "foo")
|
original := fixtures.NewNoxuInstance("default", "foo")
|
||||||
unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "metadata", "unknown")
|
unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "metadata", "unknown")
|
||||||
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "labels")
|
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "annotations")
|
||||||
|
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"invalid": "x y"}, "metadata", "labels")
|
||||||
|
unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "embedded", "metadata", "unknown")
|
||||||
|
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "embedded", "metadata", "annotations")
|
||||||
|
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"invalid": "x y"}, "embedded", "metadata", "labels")
|
||||||
|
unstructured.SetNestedField(original.UnstructuredContent(), "Foo", "embedded", "kind")
|
||||||
|
unstructured.SetNestedField(original.UnstructuredContent(), "foo/v1", "embedded", "apiVersion")
|
||||||
|
|
||||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||||
key := path.Join("/", restOptions.StorageConfig.Prefix, noxuDefinition.Spec.Group, "noxus/default/foo")
|
key := path.Join("/", restOptions.StorageConfig.Prefix, noxuDefinition.Spec.Group, "noxus/default/foo")
|
||||||
@ -140,25 +160,305 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Checking that ObjectMeta is pruned from unknown fields")
|
t.Logf("Checking that invalid objects can be deleted")
|
||||||
|
|
||||||
noxuResourceClient := newNamespacedCustomResourceClient("default", dynamicClient, noxuDefinition)
|
noxuResourceClient := newNamespacedCustomResourceClient("default", dynamicClient, noxuDefinition)
|
||||||
obj, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
if err := noxuResourceClient.Delete("foo", &metav1.DeleteOptions{}); err != nil {
|
||||||
if err != nil {
|
t.Fatalf("Unexpected delete error %v", err)
|
||||||
|
}
|
||||||
|
if _, err := etcdclient.Put(ctx, key, string(val)); err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Logf("Checking that ObjectMeta is pruned from unknown fields")
|
||||||
|
obj, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
objJSON, _ := json.Marshal(obj.Object)
|
||||||
|
t.Logf("Got object: %v", string(objJSON))
|
||||||
|
|
||||||
if unknown, found, err := unstructured.NestedFieldNoCopy(obj.UnstructuredContent(), "metadata", "unknown"); err != nil {
|
if unknown, found, err := unstructured.NestedFieldNoCopy(obj.UnstructuredContent(), "metadata", "unknown"); err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
} else if found {
|
} else if found {
|
||||||
t.Errorf("unexpected to find metadata.unknown=%#v", unknown)
|
t.Errorf("Unexpected to find metadata.unknown=%#v", unknown)
|
||||||
|
}
|
||||||
|
if unknown, found, err := unstructured.NestedFieldNoCopy(obj.UnstructuredContent(), "embedded", "metadata", "unknown"); err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
} else if found {
|
||||||
|
t.Errorf("Unexpected to find embedded.metadata.unknown=%#v", unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Checking that ObjectMeta is pruned from invalid typed fields")
|
t.Logf("Checking that ObjectMeta is pruned from wrongly-typed annotations")
|
||||||
|
|
||||||
|
if annotations, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "annotations"); err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
} else if found {
|
||||||
|
t.Errorf("Unexpected to find metadata.annotations: %#v", annotations)
|
||||||
|
}
|
||||||
|
if annotations, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "embedded", "metadata", "annotations"); err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
} else if found {
|
||||||
|
t.Errorf("Unexpected to find embedded.metadata.annotations: %#v", annotations)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Checking that ObjectMeta still has the non-validating labels")
|
||||||
|
|
||||||
if labels, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "labels"); err != nil {
|
if labels, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "labels"); err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
} else if found && !reflect.DeepEqual(labels, map[string]string{"bar": "abc"}) {
|
} else if !found {
|
||||||
t.Errorf("unexpected to find metadata.lables=%#v", labels)
|
t.Errorf("Expected to find metadata.labels, but didn't")
|
||||||
|
} else if expected := map[string]string{"invalid": "x y"}; !reflect.DeepEqual(labels, expected) {
|
||||||
|
t.Errorf("Expected metadata.labels to be %#v, got: %#v", expected, labels)
|
||||||
|
}
|
||||||
|
if labels, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "embedded", "metadata", "labels"); err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
} else if !found {
|
||||||
|
t.Errorf("Expected to find embedded.metadata.labels, but didn't")
|
||||||
|
} else if expected := map[string]string{"invalid": "x y"}; !reflect.DeepEqual(labels, expected) {
|
||||||
|
t.Errorf("Expected embedded.metadata.labels to be %#v, got: %#v", expected, labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Trying to fail on updating with invalid labels")
|
||||||
|
unstructured.SetNestedField(obj.Object, "changed", "metadata", "labels", "something")
|
||||||
|
if got, err := noxuResourceClient.Update(obj, metav1.UpdateOptions{}); err == nil {
|
||||||
|
objJSON, _ := json.Marshal(obj.Object)
|
||||||
|
gotJSON, _ := json.Marshal(got.Object)
|
||||||
|
t.Fatalf("Expected update error, but didn't get one\nin: %s\nresponse: %v", string(objJSON), string(gotJSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Trying to fail on updating with invalid embedded label")
|
||||||
|
unstructured.SetNestedField(obj.Object, "fixed", "metadata", "labels", "invalid")
|
||||||
|
if got, err := noxuResourceClient.Update(obj, metav1.UpdateOptions{}); err == nil {
|
||||||
|
objJSON, _ := json.Marshal(obj.Object)
|
||||||
|
gotJSON, _ := json.Marshal(got.Object)
|
||||||
|
t.Fatalf("Expected update error, but didn't get one\nin: %s\nresponse: %v", string(objJSON), string(gotJSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Fixed all labels and update should work")
|
||||||
|
unstructured.SetNestedField(obj.Object, "fixed", "embedded", "metadata", "labels", "invalid")
|
||||||
|
if _, err := noxuResourceClient.Update(obj, metav1.UpdateOptions{}); err != nil {
|
||||||
|
t.Errorf("Unexpected update error with fixed labels: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Trying to fail on updating with wrongly-typed embedded label")
|
||||||
|
unstructured.SetNestedField(obj.Object, int64(42), "embedded", "metadata", "labels", "invalid")
|
||||||
|
if got, err := noxuResourceClient.Update(obj, metav1.UpdateOptions{}); err == nil {
|
||||||
|
objJSON, _ := json.Marshal(obj.Object)
|
||||||
|
gotJSON, _ := json.Marshal(got.Object)
|
||||||
|
t.Fatalf("Expected update error, but didn't get one\nin: %s\nresponse: %v", string(objJSON), string(gotJSON))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var embeddedResourceFixture = &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(true),
|
||||||
|
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||||
|
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
embeddedResourceSchema = `
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
embedded:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
noEmbeddedObject:
|
||||||
|
type: object
|
||||||
|
embeddedNested:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
properties:
|
||||||
|
embedded:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
`
|
||||||
|
|
||||||
|
embeddedResourceInstance = `
|
||||||
|
kind: Foo
|
||||||
|
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
noEmbeddedObject:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
embeddedNested:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
`
|
||||||
|
|
||||||
|
expectedEmbeddedResourceInstance = `
|
||||||
|
kind: Foo
|
||||||
|
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
noEmbeddedObject:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
embeddedNested:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`
|
||||||
|
|
||||||
|
wronglyTypedEmbeddedResourceInstance = `
|
||||||
|
kind: Foo
|
||||||
|
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: invalid
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: instance
|
||||||
|
namespace: 42
|
||||||
|
`
|
||||||
|
|
||||||
|
invalidEmbeddedResourceInstance = `
|
||||||
|
kind: Foo
|
||||||
|
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: invalid
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: "%"
|
||||||
|
metadata:
|
||||||
|
name: ..
|
||||||
|
embeddedNested:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: "%"
|
||||||
|
metadata:
|
||||||
|
name: ..
|
||||||
|
embedded:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: "%"
|
||||||
|
metadata:
|
||||||
|
name: ..
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmbeddedResources(t *testing.T) {
|
||||||
|
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tearDownFn()
|
||||||
|
|
||||||
|
crd := embeddedResourceFixture.DeepCopy()
|
||||||
|
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||||
|
if err := yaml.Unmarshal([]byte(embeddedResourceSchema), &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 inside ObjectMetas")
|
||||||
|
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||||
|
foo := &unstructured.Unstructured{}
|
||||||
|
if err := yaml.Unmarshal([]byte(embeddedResourceInstance), &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())
|
||||||
|
|
||||||
|
t.Logf("Checking that everything unknown inside ObjectMeta is gone")
|
||||||
|
delete(foo.Object, "metadata")
|
||||||
|
var expected map[string]interface{}
|
||||||
|
if err := yaml.Unmarshal([]byte(expectedEmbeddedResourceInstance), &expected); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected, foo.Object) {
|
||||||
|
t.Errorf("unexpected diff: %s", diff.ObjectDiff(expected, foo.Object))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Trying to create wrongly typed CR")
|
||||||
|
invalid := &unstructured.Unstructured{}
|
||||||
|
if err := yaml.Unmarshal([]byte(wronglyTypedEmbeddedResourceInstance), &invalid.Object); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = fooClient.Create(invalid, metav1.CreateOptions{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected creation to fail, but didn't")
|
||||||
|
}
|
||||||
|
t.Logf("Creation of wrongly typed object failed with: %v", err)
|
||||||
|
|
||||||
|
for _, s := range []string{
|
||||||
|
`embedded.metadata: Invalid value`,
|
||||||
|
} {
|
||||||
|
if !strings.Contains(err.Error(), s) {
|
||||||
|
t.Errorf("missing error: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Trying to create invalid CR")
|
||||||
|
wronglyTyped := &unstructured.Unstructured{}
|
||||||
|
if err := yaml.Unmarshal([]byte(invalidEmbeddedResourceInstance), &wronglyTyped.Object); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = fooClient.Create(wronglyTyped, metav1.CreateOptions{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected creation to fail, but didn't")
|
||||||
|
}
|
||||||
|
t.Logf("Creation of invalid object failed with: %v", err)
|
||||||
|
|
||||||
|
for _, s := range []string{
|
||||||
|
`embedded.kind: Invalid value: "%"`,
|
||||||
|
`embedded.metadata.name: Invalid value: ".."`,
|
||||||
|
`embeddedNested.kind: Invalid value: "%"`,
|
||||||
|
`embeddedNested.metadata.name: Invalid value: ".."`,
|
||||||
|
`embeddedNested.embedded.kind: Invalid value: "%"`,
|
||||||
|
`embeddedNested.embedded.metadata.name: Invalid value: ".."`,
|
||||||
|
} {
|
||||||
|
if !strings.Contains(err.Error(), s) {
|
||||||
|
t.Errorf("missing error: %s", s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,21 @@ package integration
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/etcd/clientv3"
|
"github.com/coreos/etcd/clientv3"
|
||||||
"github.com/coreos/etcd/pkg/transport"
|
"github.com/coreos/etcd/pkg/transport"
|
||||||
|
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
types "k8s.io/apimachinery/pkg/types"
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
"k8s.io/apimachinery/pkg/util/json"
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
@ -107,6 +111,65 @@ properties:
|
|||||||
x-kubernetes-preserve-unknown-fields: true
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
`
|
`
|
||||||
|
|
||||||
|
fooSchemaEmbeddedResource = `
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
embeddedPruning:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
properties:
|
||||||
|
specified:
|
||||||
|
type: string
|
||||||
|
embeddedPreserving:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
embeddedNested:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
properties:
|
||||||
|
embeddedPruning:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-embedded-resource: true
|
||||||
|
properties:
|
||||||
|
specified:
|
||||||
|
type: string
|
||||||
|
`
|
||||||
|
|
||||||
|
fooSchemaEmbeddedResourceInstance = fooInstance + `
|
||||||
|
embeddedPruning:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
unspecified: bar
|
||||||
|
specified: bar
|
||||||
|
embeddedPreserving:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
unspecified: bar
|
||||||
|
embeddedNested:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
unspecified: bar
|
||||||
|
embeddedPruning:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
unspecified: bar
|
||||||
|
specified: bar
|
||||||
|
`
|
||||||
|
|
||||||
fooInstance = `
|
fooInstance = `
|
||||||
kind: Foo
|
kind: Foo
|
||||||
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||||
@ -457,3 +520,72 @@ func TestPruningCreatePreservingUnknownFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPruningEmbeddedResources(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(fooSchemaEmbeddedResource), &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(fooSchemaEmbeddedResourceInstance), &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())
|
||||||
|
|
||||||
|
t.Logf("Comparing with expected, pruned value")
|
||||||
|
x := runtime.DeepCopyJSON(foo.Object)
|
||||||
|
delete(x, "apiVersion")
|
||||||
|
delete(x, "kind")
|
||||||
|
delete(x, "metadata")
|
||||||
|
var expected map[string]interface{}
|
||||||
|
if err := yaml.Unmarshal([]byte(`
|
||||||
|
embeddedPruning:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
specified: bar
|
||||||
|
embeddedPreserving:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
unspecified: bar
|
||||||
|
embeddedNested:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
embeddedPruning:
|
||||||
|
apiVersion: foo/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
specified: bar
|
||||||
|
unspecified: bar
|
||||||
|
`), &expected); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected, x) {
|
||||||
|
t.Errorf("unexpected diff: %s", diff.ObjectDiff(expected, x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user