mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 10:43:56 +00:00
apiextensions: add default integration tests
This commit is contained in:
parent
872e507281
commit
b813780903
@ -59,14 +59,18 @@ func checks(checkers ...Checker) []Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWebhookConverter(t *testing.T) {
|
func TestWebhookConverter(t *testing.T) {
|
||||||
testWebhookConverter(t, false)
|
testWebhookConverter(t, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebhookConverterWithPruning(t *testing.T) {
|
func TestWebhookConverterWithPruning(t *testing.T) {
|
||||||
testWebhookConverter(t, true)
|
testWebhookConverter(t, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testWebhookConverter(t *testing.T, pruning bool) {
|
func TestWebhookConverterWithDefaulting(t *testing.T) {
|
||||||
|
testWebhookConverter(t, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWebhookConverter(t *testing.T, pruning, defaulting bool) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
group string
|
group string
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
@ -80,7 +84,7 @@ func testWebhookConverter(t *testing.T, pruning bool) {
|
|||||||
{
|
{
|
||||||
group: "nontrivial-converter",
|
group: "nontrivial-converter",
|
||||||
handler: NewObjectConverterWebhookHandler(t, nontrivialConverter),
|
handler: NewObjectConverterWebhookHandler(t, nontrivialConverter),
|
||||||
checks: checks(validateStorageVersion, validateServed, validateMixedStorageVersions("v1alpha1", "v1beta1", "v1beta2"), validateNonTrivialConverted, validateNonTrivialConvertedList, validateStoragePruning),
|
checks: checks(validateStorageVersion, validateServed, validateMixedStorageVersions("v1alpha1", "v1beta1", "v1beta2"), validateNonTrivialConverted, validateNonTrivialConvertedList, validateStoragePruning, validateDefaulting),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: "metadata-mutating-converter",
|
group: "metadata-mutating-converter",
|
||||||
@ -110,7 +114,12 @@ func testWebhookConverter(t *testing.T, pruning bool) {
|
|||||||
etcd3watcher.TestOnlySetFatalOnDecodeError(false)
|
etcd3watcher.TestOnlySetFatalOnDecodeError(false)
|
||||||
defer etcd3watcher.TestOnlySetFatalOnDecodeError(true)
|
defer etcd3watcher.TestOnlySetFatalOnDecodeError(true)
|
||||||
|
|
||||||
|
// enable necessary features
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||||
|
if defaulting {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceDefaulting, true)()
|
||||||
|
}
|
||||||
|
|
||||||
tearDown, config, options, err := fixtures.StartDefaultServer(t)
|
tearDown, config, options, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -132,6 +141,12 @@ func testWebhookConverter(t *testing.T, pruning bool) {
|
|||||||
crd := multiVersionFixture.DeepCopy()
|
crd := multiVersionFixture.DeepCopy()
|
||||||
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(!pruning)
|
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(!pruning)
|
||||||
|
|
||||||
|
if !defaulting {
|
||||||
|
for i := range crd.Spec.Versions {
|
||||||
|
delete(crd.Spec.Versions[i].Schema.OpenAPIV3Schema.Properties, "defaults")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RESTOptionsGetter := serveroptions.NewCRDRESTOptionsGetter(*options.RecommendedOptions.Etcd)
|
RESTOptionsGetter := serveroptions.NewCRDRESTOptionsGetter(*options.RecommendedOptions.Etcd)
|
||||||
restOptions, err := RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural})
|
restOptions, err := RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -520,6 +535,91 @@ func validateUIDMutation(t *testing.T, ctc *conversionTestContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateDefaulting(t *testing.T, ctc *conversionTestContext) {
|
||||||
|
if _, defaulting := ctc.crd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["defaults"]; !defaulting {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := ctc.namespace
|
||||||
|
storageVersion := "v1beta1"
|
||||||
|
|
||||||
|
for _, createVersion := range ctc.crd.Spec.Versions {
|
||||||
|
t.Run(fmt.Sprintf("getting objects created as %s", createVersion.Name), func(t *testing.T) {
|
||||||
|
name := "defaulting-" + createVersion.Name
|
||||||
|
client := ctc.versionedClient(ns, createVersion.Name)
|
||||||
|
|
||||||
|
fixture := newConversionMultiVersionFixture(ns, name, createVersion.Name)
|
||||||
|
if err := unstructured.SetNestedField(fixture.Object, map[string]interface{}{}, "defaults"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
created, err := client.Create(fixture, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that defaulting happens
|
||||||
|
// - in the request version when doing no-op conversion when deserializing
|
||||||
|
// - when reading back from storage in the storage version
|
||||||
|
// only the first is persisted.
|
||||||
|
defaults, found, err := unstructured.NestedMap(created.Object, "defaults")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !found {
|
||||||
|
t.Fatalf("expected .defaults to exist")
|
||||||
|
}
|
||||||
|
expectedLen := 1
|
||||||
|
if !createVersion.Storage {
|
||||||
|
expectedLen++
|
||||||
|
}
|
||||||
|
if len(defaults) != expectedLen {
|
||||||
|
t.Fatalf("after %s create expected .defaults to have %d values, but got: %v", createVersion.Name, expectedLen, defaults)
|
||||||
|
}
|
||||||
|
if _, found := defaults[createVersion.Name].(bool); !found {
|
||||||
|
t.Errorf("after %s create expected .defaults[%s] to be true, but .defaults is: %v", createVersion.Name, createVersion.Name, defaults)
|
||||||
|
}
|
||||||
|
if _, found := defaults[storageVersion].(bool); !found {
|
||||||
|
t.Errorf("after %s create expected .defaults[%s] to be true because it is the storage version, but .defaults is: %v", createVersion.Name, storageVersion, defaults)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that only the request version default is persisted
|
||||||
|
persisted, err := ctc.etcdObjectReader.GetStoredCustomResource(ns, name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, found, err := unstructured.NestedBool(persisted.Object, "defaults", storageVersion); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if createVersion.Name != storageVersion && found {
|
||||||
|
t.Errorf("after %s create .defaults[storage version %s] not to be persisted, but got in etcd: %v", createVersion.Name, storageVersion, defaults)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that when reading any other version, we do not default that version, but only the (non-persisted) storage version default
|
||||||
|
for _, v := range ctc.crd.Spec.Versions {
|
||||||
|
if v.Name == createVersion.Name {
|
||||||
|
// create version is persisted anyway, nothing to verify
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := ctc.versionedClient(ns, v.Name).Get(created.GetName(), metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found, err := unstructured.NestedBool(got.Object, "defaults", v.Name); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v.Name != storageVersion && found {
|
||||||
|
t.Errorf("after %s GET expected .defaults[%s] not to be true because only storage version %s is defaulted on read, but .defaults is: %v", v.Name, v.Name, storageVersion, defaults)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found, err := unstructured.NestedBool(got.Object, "defaults", storageVersion); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !found {
|
||||||
|
t.Errorf("after non-create, non-storage %s GET expected .defaults[storage version %s] to be true, but .defaults is: %v", v.Name, storageVersion, defaults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func expectConversionFailureMessage(id, message string) func(t *testing.T, ctc *conversionTestContext) {
|
func expectConversionFailureMessage(id, message string) func(t *testing.T, ctc *conversionTestContext) {
|
||||||
return func(t *testing.T, ctc *conversionTestContext) {
|
return func(t *testing.T, ctc *conversionTestContext) {
|
||||||
ns := ctc.namespace
|
ns := ctc.namespace
|
||||||
@ -918,6 +1018,14 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
|
|||||||
"num2": {Type: "integer"},
|
"num2": {Type: "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"defaults": {
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||||
|
"v1alpha1": {Type: "boolean"},
|
||||||
|
"v1beta1": {Type: "boolean", Default: jsonPtr(true)},
|
||||||
|
"v1beta2": {Type: "boolean"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -944,6 +1052,14 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
|
|||||||
"num2": {Type: "integer"},
|
"num2": {Type: "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"defaults": {
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||||
|
"v1alpha1": {Type: "boolean", Default: jsonPtr(true)},
|
||||||
|
"v1beta1": {Type: "boolean"},
|
||||||
|
"v1beta2": {Type: "boolean"},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -970,6 +1086,14 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
|
|||||||
"num2": {Type: "integer"},
|
"num2": {Type: "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"defaults": {
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||||
|
"v1alpha1": {Type: "boolean"},
|
||||||
|
"v1beta1": {Type: "boolean"},
|
||||||
|
"v1beta2": {Type: "boolean", Default: jsonPtr(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1089,3 +1213,12 @@ func closeOnCall(h http.Handler) (chan struct{}, http.Handler) {
|
|||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func jsonPtr(x interface{}) *apiextensionsv1beta1.JSON {
|
||||||
|
bs, err := json.Marshal(x)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ret := apiextensionsv1beta1.JSON{Raw: bs}
|
||||||
|
return &ret
|
||||||
|
}
|
||||||
|
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
utilfeaturetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
|
|
||||||
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultingFixture = &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 defaultingFooSchema = `
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
a:
|
||||||
|
type: string
|
||||||
|
default: "A"
|
||||||
|
b:
|
||||||
|
type: string
|
||||||
|
default: "B"
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
a:
|
||||||
|
type: string
|
||||||
|
default: "A"
|
||||||
|
b:
|
||||||
|
type: string
|
||||||
|
default: "B"
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestCustomResourceDefaulting(t *testing.T) {
|
||||||
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CustomResourceDefaulting, true)()
|
||||||
|
|
||||||
|
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tearDownFn()
|
||||||
|
|
||||||
|
crd := defaultingFixture.DeepCopy()
|
||||||
|
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||||
|
if err := yaml.Unmarshal([]byte(defaultingFooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mustExist := func(obj map[string]interface{}, pths [][]string) {
|
||||||
|
for _, pth := range pths {
|
||||||
|
if _, found, _ := unstructured.NestedFieldNoCopy(obj, pth...); !found {
|
||||||
|
t.Errorf("Expected '%s' field exist", strings.Join(pth, "."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mustNotExist := func(obj map[string]interface{}, pths [][]string) {
|
||||||
|
for _, pth := range pths {
|
||||||
|
if fld, found, _ := unstructured.NestedFieldNoCopy(obj, pth...); found {
|
||||||
|
t.Errorf("Expected '%s' field to not exist, but it does: %v", strings.Join(pth, "."), fld)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateCRD := func(update func(*apiextensionsv1beta1.CustomResourceDefinition)) {
|
||||||
|
var err error
|
||||||
|
for retry := 0; retry < 10; retry++ {
|
||||||
|
var obj *apiextensionsv1beta1.CustomResourceDefinition
|
||||||
|
obj, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
update(obj)
|
||||||
|
obj, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(obj)
|
||||||
|
if err != nil && apierrors.IsConflict(err) {
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
crd = obj
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addDefault := func(key string, value interface{}) {
|
||||||
|
updateCRD(func(obj *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||||
|
for _, root := range []string{"spec", "status"} {
|
||||||
|
obj.Spec.Validation.OpenAPIV3Schema.Properties[root].Properties[key] = apiextensionsv1beta1.JSONSchemaProps{
|
||||||
|
Type: "string",
|
||||||
|
Default: jsonPtr(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
removeDefault := func(key string) {
|
||||||
|
updateCRD(func(obj *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||||
|
for _, root := range []string{"spec", "status"} {
|
||||||
|
props := obj.Spec.Validation.OpenAPIV3Schema.Properties[root].Properties[key]
|
||||||
|
props.Default = nil
|
||||||
|
obj.Spec.Validation.OpenAPIV3Schema.Properties[root].Properties[key] = props
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Creating CR and expecting defaulted fields in spec, but status does not exist at all")
|
||||||
|
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, "a", "spec", "a")
|
||||||
|
unstructured.SetNestedField(foo.Object, "b", "status", "b")
|
||||||
|
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())
|
||||||
|
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}})
|
||||||
|
mustNotExist(foo.Object, [][]string{{"status"}})
|
||||||
|
|
||||||
|
t.Logf("Updating status and expecting 'a' and 'b' to show up.")
|
||||||
|
unstructured.SetNestedField(foo.Object, map[string]interface{}{}, "status")
|
||||||
|
if foo, err = fooClient.UpdateStatus(foo, metav1.UpdateOptions{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"status", "a"}, {"status", "b"}})
|
||||||
|
|
||||||
|
t.Logf("Add 'c' default and wait until GET sees it in both status and spec")
|
||||||
|
addDefault("c", "C")
|
||||||
|
if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||||
|
obj, err := fooClient.Get(foo.GetName(), metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
_, found, _ := unstructured.NestedString(obj.Object, "spec", "c")
|
||||||
|
foo = obj
|
||||||
|
return found, nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"spec", "c"}, {"status", "a"}, {"status", "b"}, {"status", "c"}})
|
||||||
|
|
||||||
|
t.Logf("Updating status, expecting 'c' to be set in spec and status")
|
||||||
|
if foo, err = fooClient.UpdateStatus(foo, metav1.UpdateOptions{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"spec", "c"}, {"status", "a"}, {"status", "b"}, {"status", "c"}})
|
||||||
|
|
||||||
|
t.Logf("Removing 'a', 'b' and `c` properties. Expecting that 'c' goes away in spec, but not in status. 'a' and 'b' were peristed.")
|
||||||
|
removeDefault("a")
|
||||||
|
removeDefault("b")
|
||||||
|
removeDefault("c")
|
||||||
|
if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||||
|
obj, err := fooClient.Get(foo.GetName(), metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
_, found, _ := unstructured.NestedString(obj.Object, "spec", "c")
|
||||||
|
foo = obj
|
||||||
|
return !found, nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"status", "a"}, {"status", "b"}, {"status", "c"}})
|
||||||
|
mustNotExist(foo.Object, [][]string{{"spec", "c"}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonPtr(x interface{}) *apiextensionsv1beta1.JSON {
|
||||||
|
bs, err := json.Marshal(x)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ret := apiextensionsv1beta1.JSON{Raw: bs}
|
||||||
|
return &ret
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user