CRD v1: switch integration tests with defaulting to v1

This commit is contained in:
Jordan Liggitt 2019-08-07 18:15:23 -04:00
parent 9c3eedea18
commit c8d085c19e
7 changed files with 175 additions and 92 deletions

View File

@ -41,8 +41,8 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/utils/pointer"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
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"
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options" "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
@ -845,8 +845,12 @@ func uidMutatingConverter(desiredAPIVersion string, obj runtime.RawExtension) (r
return runtime.RawExtension{Raw: raw}, nil return runtime.RawExtension{Raw: raw}, nil
} }
func newConversionTestContext(t *testing.T, apiExtensionsClient clientset.Interface, dynamicClient dynamic.Interface, etcdObjectReader *storage.EtcdObjectReader, crd *apiextensionsv1beta1.CustomResourceDefinition) (func(), *conversionTestContext) { func newConversionTestContext(t *testing.T, apiExtensionsClient clientset.Interface, dynamicClient dynamic.Interface, etcdObjectReader *storage.EtcdObjectReader, v1CRD *apiextensionsv1.CustomResourceDefinition) (func(), *conversionTestContext) {
crd, err := fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionsClient, dynamicClient) v1CRD, err := fixtures.CreateNewV1CustomResourceDefinition(v1CRD, apiExtensionsClient, dynamicClient)
if err != nil {
t.Fatal(err)
}
crd, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(v1CRD.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1000,12 +1004,11 @@ func (c *conversionTestContext) waitForServed(t *testing.T, version string, serv
} }
} }
var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{ var multiVersionFixture = &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "multiversion.stable.example.com"}, ObjectMeta: metav1.ObjectMeta{Name: "multiversion.stable.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "stable.example.com", Group: "stable.example.com",
Version: "v1beta1", Names: apiextensionsv1.CustomResourceDefinitionNames{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "multiversion", Plural: "multiversion",
Singular: "multiversion", Singular: "multiversion",
Kind: "MultiVersion", Kind: "MultiVersion",
@ -1013,34 +1016,41 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
ListKind: "MultiVersionList", ListKind: "MultiVersionList",
Categories: []string{"all"}, Categories: []string{"all"},
}, },
Scope: apiextensionsv1beta1.NamespaceScoped, Scope: apiextensionsv1.NamespaceScoped,
PreserveUnknownFields: pointer.BoolPtr(false), PreserveUnknownFields: false,
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{ Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
{ {
// storage version, same schema as v1alpha1 // storage version, same schema as v1alpha1
Name: "v1beta1", Name: "v1beta1",
Served: true, Served: true,
Storage: true, Storage: true,
Schema: &apiextensionsv1beta1.CustomResourceValidation{ Subresources: &apiextensionsv1.CustomResourceSubresources{
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.num.num1",
StatusReplicasPath: ".status.num.num2",
},
},
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"content": { "content": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"key": {Type: "string"}, "key": {Type: "string"},
}, },
}, },
"num": { "num": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"num1": {Type: "integer"}, "num1": {Type: "integer"},
"num2": {Type: "integer"}, "num2": {Type: "integer"},
}, },
}, },
"defaults": { "defaults": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"v1alpha1": {Type: "boolean"}, "v1alpha1": {Type: "boolean"},
"v1beta1": {Type: "boolean", Default: jsonPtr(true)}, "v1beta1": {Type: "boolean", Default: jsonPtr(true)},
"v1beta2": {Type: "boolean"}, "v1beta2": {Type: "boolean"},
@ -1055,26 +1065,33 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
Name: "v1alpha1", Name: "v1alpha1",
Served: true, Served: true,
Storage: false, Storage: false,
Schema: &apiextensionsv1beta1.CustomResourceValidation{ Subresources: &apiextensionsv1.CustomResourceSubresources{
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.num.num1",
StatusReplicasPath: ".status.num.num2",
},
},
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"content": { "content": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"key": {Type: "string"}, "key": {Type: "string"},
}, },
}, },
"num": { "num": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"num1": {Type: "integer"}, "num1": {Type: "integer"},
"num2": {Type: "integer"}, "num2": {Type: "integer"},
}, },
}, },
"defaults": { "defaults": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"v1alpha1": {Type: "boolean", Default: jsonPtr(true)}, "v1alpha1": {Type: "boolean", Default: jsonPtr(true)},
"v1beta1": {Type: "boolean"}, "v1beta1": {Type: "boolean"},
"v1beta2": {Type: "boolean"}, "v1beta2": {Type: "boolean"},
@ -1089,26 +1106,33 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
Name: "v1beta2", Name: "v1beta2",
Served: true, Served: true,
Storage: false, Storage: false,
Schema: &apiextensionsv1beta1.CustomResourceValidation{ Subresources: &apiextensionsv1.CustomResourceSubresources{
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.num.num1",
StatusReplicasPath: ".status.num.num2",
},
},
Schema: &apiextensionsv1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"contentv2": { "contentv2": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"key": {Type: "string"}, "key": {Type: "string"},
}, },
}, },
"numv2": { "numv2": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"num1": {Type: "integer"}, "num1": {Type: "integer"},
"num2": {Type: "integer"}, "num2": {Type: "integer"},
}, },
}, },
"defaults": { "defaults": {
Type: "object", Type: "object",
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1.JSONSchemaProps{
"v1alpha1": {Type: "boolean"}, "v1alpha1": {Type: "boolean"},
"v1beta1": {Type: "boolean"}, "v1beta1": {Type: "boolean"},
"v1beta2": {Type: "boolean", Default: jsonPtr(true)}, "v1beta2": {Type: "boolean", Default: jsonPtr(true)},
@ -1119,13 +1143,6 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
}, },
}, },
}, },
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{
SpecReplicasPath: ".spec.num.num1",
StatusReplicasPath: ".status.num.num2",
},
},
}, },
} }
@ -1234,11 +1251,11 @@ func closeOnCall(h http.Handler) (chan struct{}, http.Handler) {
}) })
} }
func jsonPtr(x interface{}) *apiextensionsv1beta1.JSON { func jsonPtr(x interface{}) *apiextensionsv1.JSON {
bs, err := json.Marshal(x) bs, err := json.Marshal(x)
if err != nil { if err != nil {
panic(err) panic(err)
} }
ret := apiextensionsv1beta1.JSON{Raw: bs} ret := apiextensionsv1.JSON{Raw: bs}
return &ret return &ret
} }

View File

@ -33,41 +33,42 @@ import (
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/component-base/featuregate/testing" utilfeaturetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/utils/pointer"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/features" "k8s.io/apiextensions-apiserver/pkg/features"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
) )
var defaultingFixture = &apiextensionsv1beta1.CustomResourceDefinition{ var defaultingFixture = &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.apiextensions.k8s.io"}, ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "tests.apiextensions.k8s.io", Group: "tests.example.com",
Version: "v1beta1", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{ {
Name: "v1beta1", Name: "v1beta1",
Storage: false, Storage: false,
Served: true, Served: true,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
}, },
{ {
Name: "v1beta2", Name: "v1beta2",
Storage: true, Storage: true,
Served: false, Served: false,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
}, },
}, },
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Singular: "foo", Singular: "foo",
Kind: "Foo", Kind: "Foo",
ListKind: "FooList", ListKind: "FooList",
}, },
Scope: apiextensionsv1beta1.ClusterScoped, Scope: apiextensionsv1.ClusterScoped,
PreserveUnknownFields: pointer.BoolPtr(false), PreserveUnknownFields: false,
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
},
}, },
} }
@ -163,16 +164,16 @@ func testDefaulting(t *testing.T, watchCache bool) {
defer tearDownFn() defer tearDownFn()
crd := defaultingFixture.DeepCopy() crd := defaultingFixture.DeepCopy()
crd.Spec.Versions[0].Schema = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(defaultingFooV1beta1Schema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(defaultingFooV1beta1Schema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd.Spec.Versions[1].Schema = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[1].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(defaultingFooV1beta2Schema), &crd.Spec.Versions[1].Schema.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(defaultingFooV1beta2Schema), &crd.Spec.Versions[1].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -193,17 +194,17 @@ func testDefaulting(t *testing.T, watchCache bool) {
} }
} }
} }
updateCRD := func(update func(*apiextensionsv1beta1.CustomResourceDefinition)) { updateCRD := func(update func(*apiextensionsv1.CustomResourceDefinition)) {
t.Helper() t.Helper()
var err error var err error
for retry := 0; retry < 10; retry++ { for retry := 0; retry < 10; retry++ {
var obj *apiextensionsv1beta1.CustomResourceDefinition var obj *apiextensionsv1.CustomResourceDefinition
obj, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{}) obj, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
update(obj) update(obj)
obj, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(obj) obj, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Update(obj)
if err != nil && apierrors.IsConflict(err) { if err != nil && apierrors.IsConflict(err) {
continue continue
} else if err != nil { } else if err != nil {
@ -218,13 +219,13 @@ func testDefaulting(t *testing.T, watchCache bool) {
} }
addDefault := func(version string, key string, value interface{}) { addDefault := func(version string, key string, value interface{}) {
t.Helper() t.Helper()
updateCRD(func(obj *apiextensionsv1beta1.CustomResourceDefinition) { updateCRD(func(obj *apiextensionsv1.CustomResourceDefinition) {
for _, root := range []string{"spec", "status"} { for _, root := range []string{"spec", "status"} {
for i := range obj.Spec.Versions { for i := range obj.Spec.Versions {
if obj.Spec.Versions[i].Name != version { if obj.Spec.Versions[i].Name != version {
continue continue
} }
obj.Spec.Versions[i].Schema.OpenAPIV3Schema.Properties[root].Properties[key] = apiextensionsv1beta1.JSONSchemaProps{ obj.Spec.Versions[i].Schema.OpenAPIV3Schema.Properties[root].Properties[key] = apiextensionsv1.JSONSchemaProps{
Type: "string", Type: "string",
Default: jsonPtr(value), Default: jsonPtr(value),
} }
@ -234,7 +235,7 @@ func testDefaulting(t *testing.T, watchCache bool) {
} }
removeDefault := func(version string, key string) { removeDefault := func(version string, key string) {
t.Helper() t.Helper()
updateCRD(func(obj *apiextensionsv1beta1.CustomResourceDefinition) { updateCRD(func(obj *apiextensionsv1.CustomResourceDefinition) {
for _, root := range []string{"spec", "status"} { for _, root := range []string{"spec", "status"} {
for i := range obj.Spec.Versions { for i := range obj.Spec.Versions {
if obj.Spec.Versions[i].Name != version { if obj.Spec.Versions[i].Name != version {
@ -249,7 +250,7 @@ func testDefaulting(t *testing.T, watchCache bool) {
} }
t.Logf("Creating CR and expecting defaulted fields in spec, but status does not exist at all") 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}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)
@ -399,11 +400,11 @@ func testDefaulting(t *testing.T, watchCache bool) {
mustNotExist(foo.Object, [][]string{{"spec", "c"}}) mustNotExist(foo.Object, [][]string{{"spec", "c"}})
} }
func jsonPtr(x interface{}) *apiextensionsv1beta1.JSON { func jsonPtr(x interface{}) *apiextensionsv1.JSON {
bs, err := json.Marshal(x) bs, err := json.Marshal(x)
if err != nil { if err != nil {
panic(err) panic(err)
} }
ret := apiextensionsv1beta1.JSON{Raw: bs} ret := apiextensionsv1.JSON{Raw: bs}
return &ret return &ret
} }

View File

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"time" "time"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
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"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
@ -275,6 +276,63 @@ func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceD
return crd, nil return crd, nil
} }
// CreateNewV1CustomResourceDefinitionWatchUnsafe creates the CRD and makes sure
// the apiextension apiserver has installed the CRD. But it's not safe to watch
// the created CR. Please call CreateNewV1CustomResourceDefinition if you need to
// watch the CR.
func CreateNewV1CustomResourceDefinitionWatchUnsafe(v1CRD *apiextensionsv1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) (*apiextensionsv1.CustomResourceDefinition, error) {
v1CRD, err := apiExtensionsClient.ApiextensionsV1().CustomResourceDefinitions().Create(v1CRD)
if err != nil {
return nil, err
}
crd, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(v1CRD.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
// wait until all resources appears in discovery
for _, version := range servedVersions(crd) {
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
return existsInDiscovery(crd, apiExtensionsClient, version)
})
if err != nil {
return nil, err
}
}
return v1CRD, err
}
// CreateNewV1CustomResourceDefinition creates the given CRD and makes sure its watch cache is primed on the server.
func CreateNewV1CustomResourceDefinition(v1CRD *apiextensionsv1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, dynamicClientSet dynamic.Interface) (*apiextensionsv1.CustomResourceDefinition, error) {
v1CRD, err := CreateNewV1CustomResourceDefinitionWatchUnsafe(v1CRD, apiExtensionsClient)
if err != nil {
return nil, err
}
crd, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(v1CRD.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
// This is only for a test. We need the watch cache to have a resource version that works for the test.
// When new REST storage is created, the storage cacher for the CR starts asynchronously.
// REST API operations return like list use the RV of etcd, but the storage cacher's reflector's list
// can get a different RV because etcd can be touched in between the initial list operation (if that's what you're doing first)
// and the storage cache reflector starting.
// Later, you can issue a watch with the REST apis list.RV and end up earlier than the storage cacher.
// The general working model is that if you get a "resourceVersion too old" message, you re-list and rewatch.
// For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error.
// This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen
// before like the created RV could be too old to watch.
err = wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
return isWatchCachePrimed(crd, dynamicClientSet)
})
if err != nil {
return nil, err
}
return v1CRD, nil
}
func resourceClientForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.Interface, namespace, version string) dynamic.ResourceInterface { func resourceClientForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.Interface, namespace, version string) dynamic.ResourceInterface {
gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural} gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: version, Resource: crd.Spec.Names.Plural}
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {

View File

@ -26,6 +26,7 @@ import (
"github.com/coreos/etcd/pkg/transport" "github.com/coreos/etcd/pkg/transport"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
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"
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options" serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
@ -253,22 +254,28 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
} }
} }
var embeddedResourceFixture = &apiextensionsv1beta1.CustomResourceDefinition{ var embeddedResourceFixture = &apiextensionsv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.apiextensions.k8s.io"}, ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1.CustomResourceDefinitionSpec{
Group: "tests.apiextensions.k8s.io", Group: "tests.example.com",
Version: "v1beta1", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ {
Name: "v1beta1",
Storage: true,
Served: true,
Subresources: &apiextensionsv1.CustomResourceSubresources{
Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
},
},
},
Names: apiextensionsv1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
Singular: "foo", Singular: "foo",
Kind: "Foo", Kind: "Foo",
ListKind: "FooList", ListKind: "FooList",
}, },
Scope: apiextensionsv1beta1.ClusterScoped, Scope: apiextensionsv1.ClusterScoped,
PreserveUnknownFields: pointer.BoolPtr(false), PreserveUnknownFields: false,
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
},
}, },
} }
@ -319,7 +326,7 @@ properties:
embeddedResourceInstance = ` embeddedResourceInstance = `
kind: Foo kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1 apiVersion: tests.example.com/v1beta1
embedded: embedded:
apiVersion: foo/v1 apiVersion: foo/v1
kind: Foo kind: Foo
@ -348,7 +355,7 @@ embeddedNested:
expectedEmbeddedResourceInstance = ` expectedEmbeddedResourceInstance = `
kind: Foo kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1 apiVersion: tests.example.com/v1beta1
embedded: embedded:
apiVersion: foo/v1 apiVersion: foo/v1
kind: Foo kind: Foo
@ -379,7 +386,7 @@ defaults:
wronglyTypedEmbeddedResourceInstance = ` wronglyTypedEmbeddedResourceInstance = `
kind: Foo kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1 apiVersion: tests.example.com/v1beta1
embedded: embedded:
apiVersion: foo/v1 apiVersion: foo/v1
kind: Foo kind: Foo
@ -390,7 +397,7 @@ embedded:
invalidEmbeddedResourceInstance = ` invalidEmbeddedResourceInstance = `
kind: Foo kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1 apiVersion: tests.example.com/v1beta1
embedded: embedded:
apiVersion: foo/v1 apiVersion: foo/v1
kind: "%" kind: "%"
@ -420,18 +427,18 @@ func TestEmbeddedResources(t *testing.T) {
defer tearDownFn() defer tearDownFn()
crd := embeddedResourceFixture.DeepCopy() crd := embeddedResourceFixture.DeepCopy()
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{} crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
if err := yaml.Unmarshal([]byte(embeddedResourceSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil { if err := yaml.Unmarshal([]byte(embeddedResourceSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
t.Fatal(err) t.Fatal(err)
} }
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) crd, err = fixtures.CreateNewV1CustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Creating CR and expect 'unspecified' fields to be pruned inside ObjectMetas") 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}) fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Versions[0].Name, crd.Spec.Names.Plural})
foo := &unstructured.Unstructured{} foo := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(embeddedResourceInstance), &foo.Object); err != nil { if err := yaml.Unmarshal([]byte(embeddedResourceInstance), &foo.Object); err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -44,9 +44,9 @@ import (
) )
var pruningFixture = &apiextensionsv1beta1.CustomResourceDefinition{ var pruningFixture = &apiextensionsv1beta1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.apiextensions.k8s.io"}, ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.example.com"},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: "tests.apiextensions.k8s.io", Group: "tests.example.com",
Version: "v1beta1", Version: "v1beta1",
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "foos", Plural: "foos",
@ -172,7 +172,7 @@ embeddedNested:
fooInstance = ` fooInstance = `
kind: Foo kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1 apiVersion: tests.example.com/v1beta1
metadata: metadata:
name: foo name: foo
` `

View File

@ -25,7 +25,7 @@ import (
"github.com/coreos/etcd/clientv3" "github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/pkg/transport" "github.com/coreos/etcd/pkg/transport"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage/storagebackend" "k8s.io/apiserver/pkg/storage/storagebackend"
@ -35,11 +35,11 @@ import (
type EtcdObjectReader struct { type EtcdObjectReader struct {
etcdClient *clientv3.Client etcdClient *clientv3.Client
storagePrefix string storagePrefix string
crd *apiextensionsv1beta1.CustomResourceDefinition crd *apiextensionsv1.CustomResourceDefinition
} }
// NewEtcdObjectReader creates a reader for accessing custom resource objects directly from etcd. // NewEtcdObjectReader creates a reader for accessing custom resource objects directly from etcd.
func NewEtcdObjectReader(etcdClient *clientv3.Client, restOptions *generic.RESTOptions, crd *apiextensionsv1beta1.CustomResourceDefinition) *EtcdObjectReader { func NewEtcdObjectReader(etcdClient *clientv3.Client, restOptions *generic.RESTOptions, crd *apiextensionsv1.CustomResourceDefinition) *EtcdObjectReader {
return &EtcdObjectReader{etcdClient, restOptions.StorageConfig.Prefix, crd} return &EtcdObjectReader{etcdClient, restOptions.StorageConfig.Prefix, crd}
} }

View File

@ -548,9 +548,9 @@ func TestNonStructuralSchemaConditionUpdate(t *testing.T) {
apiVersion: apiextensions.k8s.io/v1beta1 apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata: metadata:
name: foos.tests.apiextensions.k8s.io name: foos.tests.example.com
spec: spec:
group: tests.apiextensions.k8s.io group: tests.example.com
version: v1beta1 version: v1beta1
names: names:
plural: foos plural: foos