Bumping k8s version to 1.13.0-beta.1
This commit is contained in:
12
vendor/k8s.io/apiextensions-apiserver/test/integration/fixtures/resources.go
generated
vendored
12
vendor/k8s.io/apiextensions-apiserver/test/integration/fixtures/resources.go
generated
vendored
@@ -370,13 +370,13 @@ func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefi
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateNewScaleClient returns a scale client.
|
||||
func CreateNewScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config) (scale.ScalesGetter, error) {
|
||||
// CreateNewVersionedScaleClient returns a scale client.
|
||||
func CreateNewVersionedScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, config *rest.Config, version string) (scale.ScalesGetter, error) {
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupResource, err := discoveryClient.ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
|
||||
groupResource, err := discoveryClient.ServerResourcesForGroupVersion(crd.Spec.Group + "/" + version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -386,12 +386,12 @@ func CreateNewScaleClient(crd *apiextensionsv1beta1.CustomResourceDefinition, co
|
||||
Group: metav1.APIGroup{
|
||||
Name: crd.Spec.Group,
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{Version: crd.Spec.Version},
|
||||
{Version: version},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: crd.Spec.Version},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: version},
|
||||
},
|
||||
VersionedResources: map[string][]metav1.APIResource{
|
||||
crd.Spec.Version: groupResource.APIResources,
|
||||
version: groupResource.APIResources,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
100
vendor/k8s.io/apiextensions-apiserver/test/integration/helpers.go
generated
vendored
100
vendor/k8s.io/apiextensions-apiserver/test/integration/helpers.go
generated
vendored
@@ -30,6 +30,8 @@ import (
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
|
||||
|
||||
func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) {
|
||||
return instantiateVersionedCustomResource(t, instanceToCreate, client, definition, definition.Spec.Versions[0].Name)
|
||||
}
|
||||
@@ -74,8 +76,8 @@ func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd
|
||||
return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name)
|
||||
}
|
||||
|
||||
// updateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors.
|
||||
func updateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1beta1.CustomResourceDefinition)) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
// UpdateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors.
|
||||
func UpdateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1beta1.CustomResourceDefinition)) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
for i := 0; i < 5; i++ {
|
||||
crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
@@ -92,3 +94,97 @@ func updateCustomResourceDefinitionWithRetry(client clientset.Interface, name st
|
||||
}
|
||||
return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
|
||||
}
|
||||
|
||||
// getSchemaForVersion returns the validation schema for given version in given CRD.
|
||||
func getSchemaForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) (*apiextensionsv1beta1.CustomResourceValidation, error) {
|
||||
if !hasPerVersionSchema(crd.Spec.Versions) {
|
||||
return crd.Spec.Validation, nil
|
||||
}
|
||||
if crd.Spec.Validation != nil {
|
||||
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive", crd.Name, version)
|
||||
}
|
||||
for _, v := range crd.Spec.Versions {
|
||||
if version == v.Name {
|
||||
return v.Schema, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||
}
|
||||
|
||||
// getSubresourcesForVersion returns the subresources for given version in given CRD.
|
||||
func getSubresourcesForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) (*apiextensionsv1beta1.CustomResourceSubresources, error) {
|
||||
if !hasPerVersionSubresources(crd.Spec.Versions) {
|
||||
return crd.Spec.Subresources, nil
|
||||
}
|
||||
if crd.Spec.Subresources != nil {
|
||||
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive", crd.Name, version)
|
||||
}
|
||||
for _, v := range crd.Spec.Versions {
|
||||
if version == v.Name {
|
||||
return v.Subresources, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||
}
|
||||
|
||||
// getColumnsForVersion returns the columns for given version in given CRD.
|
||||
// NOTE: the newly logically-defaulted columns is not pointing to the original CRD object.
|
||||
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
|
||||
// the original CRD object instead.
|
||||
func getColumnsForVersion(crd *apiextensionsv1beta1.CustomResourceDefinition, version string) ([]apiextensionsv1beta1.CustomResourceColumnDefinition, error) {
|
||||
if !hasPerVersionColumns(crd.Spec.Versions) {
|
||||
return serveDefaultColumnsIfEmpty(crd.Spec.AdditionalPrinterColumns), nil
|
||||
}
|
||||
if len(crd.Spec.AdditionalPrinterColumns) > 0 {
|
||||
return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive", crd.Name, version)
|
||||
}
|
||||
for _, v := range crd.Spec.Versions {
|
||||
if version == v.Name {
|
||||
return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
|
||||
}
|
||||
|
||||
// serveDefaultColumnsIfEmpty applies logically defaulting to columns, if the input columns is empty.
|
||||
// NOTE: in this way, the newly logically-defaulted columns is not pointing to the original CRD object.
|
||||
// One cannot mutate the original CRD columns using the logically-defaulted columns. Please iterate through
|
||||
// the original CRD object instead.
|
||||
func serveDefaultColumnsIfEmpty(columns []apiextensionsv1beta1.CustomResourceColumnDefinition) []apiextensionsv1beta1.CustomResourceColumnDefinition {
|
||||
if len(columns) > 0 {
|
||||
return columns
|
||||
}
|
||||
return []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
|
||||
}
|
||||
}
|
||||
|
||||
// hasPerVersionSchema returns true if a CRD uses per-version schema.
|
||||
func hasPerVersionSchema(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
|
||||
for _, v := range versions {
|
||||
if v.Schema != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasPerVersionSubresources returns true if a CRD uses per-version subresources.
|
||||
func hasPerVersionSubresources(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
|
||||
for _, v := range versions {
|
||||
if v.Subresources != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasPerVersionColumns returns true if a CRD uses per-version columns.
|
||||
func hasPerVersionColumns(versions []apiextensionsv1beta1.CustomResourceDefinitionVersion) bool {
|
||||
for _, v := range versions {
|
||||
if len(v.AdditionalPrinterColumns) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
1078
vendor/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go
generated
vendored
1078
vendor/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
314
vendor/k8s.io/apiextensions-apiserver/test/integration/table_test.go
generated
vendored
314
vendor/k8s.io/apiextensions-apiserver/test/integration/table_test.go
generated
vendored
@@ -28,10 +28,14 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
@@ -48,12 +52,32 @@ func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
ListKind: "TablemList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
||||
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
||||
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"},
|
||||
{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"},
|
||||
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
|
||||
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
||||
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
||||
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"},
|
||||
{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"},
|
||||
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||
{Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"},
|
||||
{Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"},
|
||||
{Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"},
|
||||
{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"},
|
||||
{Name: "Epsilon", Type: "string", Description: "an array of integers as string", JSONPath: ".spec.epsilon"},
|
||||
{Name: "Zeta", Type: "integer", Description: "the zeta field", Format: "int64", Priority: 42, JSONPath: ".spec.zeta"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -62,7 +86,7 @@ func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
func newTableInstance(name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"apiVersion": "mygroup.example.com/v1",
|
||||
"kind": "Table",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
@@ -73,12 +97,14 @@ func newTableInstance(name string) *unstructured.Unstructured {
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
"epsilon": []int64{1, 2, 3},
|
||||
"zeta": 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableGet(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -107,107 +133,219 @@ func TestTableGet(t *testing.T) {
|
||||
}
|
||||
t.Logf("table crd created: %#v", crd)
|
||||
|
||||
crClient := newNamespacedCustomResourceClient("", dynamicClient, crd)
|
||||
crClient := newNamespacedCustomResourceVersionedClient("", dynamicClient, crd, "v1")
|
||||
foo, err := crClient.Create(newTableInstance("foo"), metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
t.Logf("foo created: %#v", foo.UnstructuredContent())
|
||||
|
||||
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version}
|
||||
gvk := gv.WithKind(crd.Spec.Names.Kind)
|
||||
for i, v := range crd.Spec.Versions {
|
||||
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name}
|
||||
gvk := gv.WithKind(crd.Spec.Names.Kind)
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
parameterCodec := runtime.NewParameterCodec(scheme)
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
parameterCodec := runtime.NewParameterCodec(scheme)
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
|
||||
|
||||
crConfig := *config
|
||||
crConfig.GroupVersion = &gv
|
||||
crConfig.APIPath = "/apis"
|
||||
crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs}
|
||||
crRestClient, err := rest.RESTClientFor(&crConfig)
|
||||
crConfig := *config
|
||||
crConfig.GroupVersion = &gv
|
||||
crConfig.APIPath = "/apis"
|
||||
crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs}
|
||||
crRestClient, err := rest.RESTClientFor(&crConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ret, err := crRestClient.Get().
|
||||
Resource(crd.Spec.Names.Plural).
|
||||
SetHeader("Accept", fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)).
|
||||
VersionedParams(&metav1beta1.TableOptions{}, parameterCodec).
|
||||
Do().
|
||||
Get()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list %v resources: %v", gvk, err)
|
||||
}
|
||||
|
||||
tbl, ok := ret.(*metav1beta1.Table)
|
||||
if !ok {
|
||||
t.Fatalf("expected metav1beta1.Table, got %T", ret)
|
||||
}
|
||||
t.Logf("%v table list: %#v", gvk, tbl)
|
||||
|
||||
columns, err := getColumnsForVersion(crd, v.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectColumnNum := len(columns) + 1
|
||||
if got, expected := len(tbl.ColumnDefinitions), expectColumnNum; got != expected {
|
||||
t.Errorf("expected %d headers, got %d", expected, got)
|
||||
} else {
|
||||
age := metav1beta1.TableColumnDefinition{Name: "Age", Type: "date", Format: "", Description: "Custom resource definition column (in JSONPath format): .metadata.creationTimestamp", Priority: 0}
|
||||
if got, expected := tbl.ColumnDefinitions[1], age; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
alpha := metav1beta1.TableColumnDefinition{Name: "Alpha", Type: "string", Format: "", Description: "Custom resource definition column (in JSONPath format): .spec.alpha", Priority: 0}
|
||||
if got, expected := tbl.ColumnDefinitions[2], alpha; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
beta := metav1beta1.TableColumnDefinition{Name: "Beta", Type: "integer", Format: "int64", Description: "the beta field", Priority: 42}
|
||||
if got, expected := tbl.ColumnDefinitions[3], beta; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
gamma := metav1beta1.TableColumnDefinition{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values"}
|
||||
if got, expected := tbl.ColumnDefinitions[4], gamma; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
epsilon := metav1beta1.TableColumnDefinition{Name: "Epsilon", Type: "string", Description: "an array of integers as string"}
|
||||
if got, expected := tbl.ColumnDefinitions[5], epsilon; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
// Validate extra column for v1
|
||||
if i == 1 {
|
||||
zeta := metav1beta1.TableColumnDefinition{Name: "Zeta", Type: "integer", Format: "int64", Description: "the zeta field", Priority: 42}
|
||||
if got, expected := tbl.ColumnDefinitions[6], zeta; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
if got, expected := len(tbl.Rows), 1; got != expected {
|
||||
t.Errorf("expected %d rows, got %d", expected, got)
|
||||
} else if got, expected := len(tbl.Rows[0].Cells), expectColumnNum; got != expected {
|
||||
t.Errorf("expected %d cells, got %d", expected, got)
|
||||
} else {
|
||||
if got, expected := tbl.Rows[0].Cells[0], "foo"; got != expected {
|
||||
t.Errorf("expected cell[0] to equal %q, got %q", expected, got)
|
||||
}
|
||||
if s, ok := tbl.Rows[0].Cells[1].(string); !ok {
|
||||
t.Errorf("expected cell[1] to be a string, got: %#v", tbl.Rows[0].Cells[1])
|
||||
} else {
|
||||
dur, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
t.Errorf("expected cell[1] to be a duration: %v", err)
|
||||
} else if abs(dur.Seconds()) > 30.0 {
|
||||
t.Errorf("expected cell[1] to be a small age, but got: %v", dur)
|
||||
}
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[2], "foo_123"; got != expected {
|
||||
t.Errorf("expected cell[2] to equal %q, got %q", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[3], int64(10); got != expected {
|
||||
t.Errorf("expected cell[3] to equal %#v, got %#v", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[4], interface{}(nil); got != expected {
|
||||
t.Errorf("expected cell[4] to equal %#v although the type does not match the column, got %#v", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[5], "[1 2 3]"; got != expected {
|
||||
t.Errorf("expected cell[5] to equal %q, got %q", expected, got)
|
||||
}
|
||||
// Validate extra column for v1
|
||||
if i == 1 {
|
||||
if got, expected := tbl.Rows[0].Cells[6], int64(5); got != expected {
|
||||
t.Errorf("expected cell[6] to equal %q, got %q", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestColumnsPatch tests the case that a CRD was created with no top-level or
|
||||
// per-version columns. One should be able to PATCH the CRD setting per-version columns.
|
||||
func TestColumnsPatch(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ret, err := crRestClient.Get().
|
||||
Resource(crd.Spec.Names.Plural).
|
||||
SetHeader("Accept", fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)).
|
||||
VersionedParams(&metav1beta1.TableOptions{}, parameterCodec).
|
||||
Do().
|
||||
Get()
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list %v resources: %v", gvk, err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tbl, ok := ret.(*metav1beta1.Table)
|
||||
if !ok {
|
||||
t.Fatalf("expected metav1beta1.Table, got %T", ret)
|
||||
// CRD with no top-level and per-version columns should be created successfully
|
||||
crd := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)[0]
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%v table list: %#v", gvk, tbl)
|
||||
|
||||
if got, expected := len(tbl.ColumnDefinitions), 6; got != expected {
|
||||
t.Errorf("expected %d headers, got %d", expected, got)
|
||||
} else {
|
||||
age := metav1beta1.TableColumnDefinition{Name: "Age", Type: "date", Format: "", Description: "Custom resource definition column (in JSONPath format): .metadata.creationTimestamp", Priority: 0}
|
||||
if got, expected := tbl.ColumnDefinitions[1], age; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
// One should be able to patch the CRD to use per-version columns. The top-level columns
|
||||
// should not be defaulted during creation, and apiserver should not return validation
|
||||
// error about top-level and per-version columns being mutual exclusive.
|
||||
patch := []byte(`{"spec":{"versions":[{"name":"v1beta1","served":true,"storage":true,"additionalPrinterColumns":[{"name":"Age","type":"date","JSONPath":".metadata.creationTimestamp"}]},{"name":"v1","served":true,"storage":false,"additionalPrinterColumns":[{"name":"Age2","type":"date","JSONPath":".metadata.creationTimestamp"}]}]}}`)
|
||||
|
||||
alpha := metav1beta1.TableColumnDefinition{Name: "Alpha", Type: "string", Format: "", Description: "Custom resource definition column (in JSONPath format): .spec.alpha", Priority: 0}
|
||||
if got, expected := tbl.ColumnDefinitions[2], alpha; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
beta := metav1beta1.TableColumnDefinition{Name: "Beta", Type: "integer", Format: "int64", Description: "the beta field", Priority: 42}
|
||||
if got, expected := tbl.ColumnDefinitions[3], beta; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
gamma := metav1beta1.TableColumnDefinition{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values"}
|
||||
if got, expected := tbl.ColumnDefinitions[4], gamma; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
|
||||
epsilon := metav1beta1.TableColumnDefinition{Name: "Epsilon", Type: "string", Description: "an array of integers as string"}
|
||||
if got, expected := tbl.ColumnDefinitions[5], epsilon; got != expected {
|
||||
t.Errorf("expected column definition %#v, got %#v", expected, got)
|
||||
}
|
||||
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crd.Name, types.MergePatchType, patch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, expected := len(tbl.Rows), 1; got != expected {
|
||||
t.Errorf("expected %d rows, got %d", expected, got)
|
||||
} else if got, expected := len(tbl.Rows[0].Cells), 6; got != expected {
|
||||
t.Errorf("expected %d cells, got %d", expected, got)
|
||||
} else {
|
||||
if got, expected := tbl.Rows[0].Cells[0], "foo"; got != expected {
|
||||
t.Errorf("expected cell[0] to equal %q, got %q", expected, got)
|
||||
}
|
||||
if s, ok := tbl.Rows[0].Cells[1].(string); !ok {
|
||||
t.Errorf("expected cell[1] to be a string, got: %#v", tbl.Rows[0].Cells[1])
|
||||
} else {
|
||||
dur, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
t.Errorf("expected cell[1] to be a duration: %v", err)
|
||||
} else if abs(dur.Seconds()) > 30.0 {
|
||||
t.Errorf("expected cell[1] to be a small age, but got: %v", dur)
|
||||
}
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[2], "foo_123"; got != expected {
|
||||
t.Errorf("expected cell[2] to equal %q, got %q", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[3], int64(10); got != expected {
|
||||
t.Errorf("expected cell[3] to equal %#v, got %#v", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[4], interface{}(nil); got != expected {
|
||||
t.Errorf("expected cell[4] to equal %#v although the type does not match the column, got %#v", expected, got)
|
||||
}
|
||||
if got, expected := tbl.Rows[0].Cells[5], "[1 2 3]"; got != expected {
|
||||
t.Errorf("expected cell[5] to equal %q, got %q", expected, got)
|
||||
}
|
||||
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("columns crd patched: %#v", crd)
|
||||
}
|
||||
|
||||
// TestPatchCleanTopLevelColumns tests the case that a CRD was created with top-level columns.
|
||||
// One should be able to PATCH the CRD cleaning the top-level columns and setting per-version
|
||||
// columns.
|
||||
func TestPatchCleanTopLevelColumns(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, config, _, 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)
|
||||
}
|
||||
|
||||
crd := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.NamespaceScoped)[0]
|
||||
crd.Spec.AdditionalPrinterColumns = []apiextensionsv1beta1.CustomResourceColumnDefinition{
|
||||
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
|
||||
}
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("columns crd created: %#v", crd)
|
||||
|
||||
// One should be able to patch the CRD to use per-version columns by cleaning
|
||||
// the top-level columns.
|
||||
patch := []byte(`{"spec":{"additionalPrinterColumns":null,"versions":[{"name":"v1beta1","served":true,"storage":true,"additionalPrinterColumns":[{"name":"Age","type":"date","JSONPath":".metadata.creationTimestamp"}]},{"name":"v1","served":true,"storage":false,"additionalPrinterColumns":[{"name":"Age2","type":"date","JSONPath":".metadata.creationTimestamp"}]}]}}`)
|
||||
|
||||
_, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Patch(crd.Name, types.MergePatchType, patch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("columns crd patched: %#v", crd)
|
||||
}
|
||||
|
||||
func abs(x float64) float64 {
|
||||
|
591
vendor/k8s.io/apiextensions-apiserver/test/integration/validation_test.go
generated
vendored
591
vendor/k8s.io/apiextensions-apiserver/test/integration/validation_test.go
generated
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -25,8 +26,11 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
@@ -84,64 +88,114 @@ func TestForProperValidationErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newNoxuValidationCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition {
|
||||
return &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
func newNoxuValidationCRDs(scope apiextensionsv1beta1.ResourceScope) []*apiextensionsv1beta1.CustomResourceDefinition {
|
||||
validationSchema := &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Required: []string{"alpha", "beta"},
|
||||
AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{
|
||||
Allows: true,
|
||||
},
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"alpha": {
|
||||
Description: "Alpha is an alphanumeric string with underscores",
|
||||
Type: "string",
|
||||
Pattern: "^[a-zA-Z0-9_]*$",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Required: []string{"alpha", "beta"},
|
||||
AdditionalProperties: &apiextensionsv1beta1.JSONSchemaPropsOrBool{
|
||||
Allows: true,
|
||||
"beta": {
|
||||
Description: "Minimum value of beta is 10",
|
||||
Type: "number",
|
||||
Minimum: float64Ptr(10),
|
||||
},
|
||||
"gamma": {
|
||||
Description: "Gamma is restricted to foo, bar and baz",
|
||||
Type: "string",
|
||||
Enum: []apiextensionsv1beta1.JSON{
|
||||
{
|
||||
Raw: []byte(`"foo"`),
|
||||
},
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"alpha": {
|
||||
Description: "Alpha is an alphanumeric string with underscores",
|
||||
Type: "string",
|
||||
Pattern: "^[a-zA-Z0-9_]*$",
|
||||
{
|
||||
Raw: []byte(`"bar"`),
|
||||
},
|
||||
{
|
||||
Raw: []byte(`"baz"`),
|
||||
},
|
||||
},
|
||||
},
|
||||
"delta": {
|
||||
Description: "Delta is a string with a maximum length of 5 or a number with a minimum value of 0",
|
||||
AnyOf: []apiextensionsv1beta1.JSONSchemaProps{
|
||||
{
|
||||
Type: "string",
|
||||
MaxLength: int64Ptr(5),
|
||||
},
|
||||
{
|
||||
Type: "number",
|
||||
Minimum: float64Ptr(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
validationSchemaWithDescription := validationSchema.DeepCopy()
|
||||
validationSchemaWithDescription.Description = "test"
|
||||
return []*apiextensionsv1beta1.CustomResourceDefinition{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validationSchema,
|
||||
},
|
||||
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
{
|
||||
Name: "v1",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "mygroup.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "noxus",
|
||||
Singular: "nonenglishnoxu",
|
||||
Kind: "WishIHadChosenNoxu",
|
||||
ShortNames: []string{"foo", "bar", "abc", "def"},
|
||||
ListKind: "NoxuItemList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
Schema: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validationSchema,
|
||||
},
|
||||
"beta": {
|
||||
Description: "Minimum value of beta is 10",
|
||||
Type: "number",
|
||||
Minimum: float64Ptr(10),
|
||||
},
|
||||
"gamma": {
|
||||
Description: "Gamma is restricted to foo, bar and baz",
|
||||
Type: "string",
|
||||
Enum: []apiextensionsv1beta1.JSON{
|
||||
{
|
||||
Raw: []byte(`"foo"`),
|
||||
},
|
||||
{
|
||||
Raw: []byte(`"bar"`),
|
||||
},
|
||||
{
|
||||
Raw: []byte(`"baz"`),
|
||||
},
|
||||
},
|
||||
},
|
||||
"delta": {
|
||||
Description: "Delta is a string with a maximum length of 5 or a number with a minimum value of 0",
|
||||
AnyOf: []apiextensionsv1beta1.JSONSchemaProps{
|
||||
{
|
||||
Type: "string",
|
||||
MaxLength: int64Ptr(5),
|
||||
},
|
||||
{
|
||||
Type: "number",
|
||||
Minimum: float64Ptr(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Schema: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validationSchemaWithDescription,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -168,253 +222,320 @@ func newNoxuValidationInstance(namespace, name string) *unstructured.Unstructure
|
||||
}
|
||||
|
||||
func TestCustomResourceValidation(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||
for _, noxuDefinition := range noxuDefinitions {
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
ns := "not-the-default"
|
||||
for _, v := range noxuDefinition.Spec.Versions {
|
||||
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||
instanceToCreate := newNoxuValidationInstance(ns, "foo")
|
||||
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||
_, err = instantiateVersionedCustomResource(t, instanceToCreate, noxuResourceClient, noxuDefinition, v.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||
}
|
||||
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomResourceUpdateValidation(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||
for _, noxuDefinition := range noxuDefinitions {
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
ns := "not-the-default"
|
||||
for _, v := range noxuDefinition.Spec.Versions {
|
||||
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||
instanceToCreate := newNoxuValidationInstance(ns, "foo")
|
||||
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||
_, err = instantiateVersionedCustomResource(t, instanceToCreate, noxuResourceClient, noxuDefinition, v.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create noxu instance: %v", err)
|
||||
}
|
||||
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gottenNoxuInstance, err := noxuResourceClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// invalidate the instance
|
||||
gottenNoxuInstance.Object = map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "not-the-default",
|
||||
"name": "foo",
|
||||
},
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
}
|
||||
// invalidate the instance
|
||||
gottenNoxuInstance.Object = map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "not-the-default",
|
||||
"name": "foo",
|
||||
},
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
}
|
||||
|
||||
_, err = noxuResourceClient.Update(gottenNoxuInstance, metav1.UpdateOptions{})
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: alpha and beta should be present while updating %v", gottenNoxuInstance)
|
||||
_, err = noxuResourceClient.Update(gottenNoxuInstance, metav1.UpdateOptions{})
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: alpha and beta should be present while updating %v", gottenNoxuInstance)
|
||||
}
|
||||
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||
}
|
||||
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomResourceValidationErrors(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
instanceFn func() *unstructured.Unstructured
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "bad alpha",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["alpha"] = "foo_123!"
|
||||
return instance
|
||||
},
|
||||
expectedError: "alpha in body should match '^[a-zA-Z0-9_]*$'",
|
||||
},
|
||||
{
|
||||
name: "bad beta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["beta"] = 5
|
||||
return instance
|
||||
},
|
||||
expectedError: "beta in body should be greater than or equal to 10",
|
||||
},
|
||||
{
|
||||
name: "bad gamma",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["gamma"] = "qux"
|
||||
return instance
|
||||
},
|
||||
expectedError: "gamma in body should be one of [foo bar baz]",
|
||||
},
|
||||
{
|
||||
name: "bad delta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["delta"] = "foobarbaz"
|
||||
return instance
|
||||
},
|
||||
expectedError: "must validate at least one schema (anyOf)\ndelta in body should be at most 5 chars long",
|
||||
},
|
||||
{
|
||||
name: "absent alpha and beta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object = map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "not-the-default",
|
||||
"name": "foo",
|
||||
},
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
}
|
||||
return instance
|
||||
},
|
||||
expectedError: ".alpha in body is required\n.beta in body is required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
_, err := noxuResourceClient.Create(tc.instanceFn(), metav1.CreateOptions{})
|
||||
if err == nil {
|
||||
t.Errorf("%v: expected %v", tc.name, tc.expectedError)
|
||||
continue
|
||||
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||
for _, noxuDefinition := range noxuDefinitions {
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// this only works when status errors contain the expect kind and version, so this effectively tests serializations too
|
||||
if !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("%v: expected %v, got %v", tc.name, tc.expectedError, err)
|
||||
continue
|
||||
|
||||
ns := "not-the-default"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
instanceFn func() *unstructured.Unstructured
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "bad alpha",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["alpha"] = "foo_123!"
|
||||
return instance
|
||||
},
|
||||
expectedError: "alpha in body should match '^[a-zA-Z0-9_]*$'",
|
||||
},
|
||||
{
|
||||
name: "bad beta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["beta"] = 5
|
||||
return instance
|
||||
},
|
||||
expectedError: "beta in body should be greater than or equal to 10",
|
||||
},
|
||||
{
|
||||
name: "bad gamma",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["gamma"] = "qux"
|
||||
return instance
|
||||
},
|
||||
expectedError: "gamma in body should be one of [foo bar baz]",
|
||||
},
|
||||
{
|
||||
name: "bad delta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object["delta"] = "foobarbaz"
|
||||
return instance
|
||||
},
|
||||
expectedError: "must validate at least one schema (anyOf)\ndelta in body should be at most 5 chars long",
|
||||
},
|
||||
{
|
||||
name: "absent alpha and beta",
|
||||
instanceFn: func() *unstructured.Unstructured {
|
||||
instance := newNoxuValidationInstance(ns, "foo")
|
||||
instance.Object = map[string]interface{}{
|
||||
"apiVersion": "mygroup.example.com/v1beta1",
|
||||
"kind": "WishIHadChosenNoxu",
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "not-the-default",
|
||||
"name": "foo",
|
||||
},
|
||||
"gamma": "bar",
|
||||
"delta": "hello",
|
||||
}
|
||||
return instance
|
||||
},
|
||||
expectedError: ".alpha in body is required\n.beta in body is required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
for _, v := range noxuDefinition.Spec.Versions {
|
||||
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||
instanceToCreate := tc.instanceFn()
|
||||
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||
_, err := noxuResourceClient.Create(instanceToCreate, metav1.CreateOptions{})
|
||||
if err == nil {
|
||||
t.Errorf("%v: expected %v", tc.name, tc.expectedError)
|
||||
continue
|
||||
}
|
||||
// this only works when status errors contain the expect kind and version, so this effectively tests serializations too
|
||||
if !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("%v: expected %v, got %v", tc.name, tc.expectedError, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCRValidationOnCRDUpdate(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||
for i, noxuDefinition := range noxuDefinitions {
|
||||
for _, v := range noxuDefinition.Spec.Versions {
|
||||
// Re-define the CRD to make sure we start with a clean CRD
|
||||
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[i]
|
||||
validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// set stricter schema
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta", "epsilon"}
|
||||
// set stricter schema
|
||||
validationSchema.OpenAPIV3Schema.Required = []string{"alpha", "beta", "epsilon"}
|
||||
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ns := "not-the-default"
|
||||
noxuResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, v.Name)
|
||||
instanceToCreate := newNoxuValidationInstance(ns, "foo")
|
||||
instanceToCreate.Object["apiVersion"] = fmt.Sprintf("%s/%s", noxuDefinition.Spec.Group, v.Name)
|
||||
|
||||
// CR is rejected
|
||||
_, err = instantiateCustomResource(t, newNoxuValidationInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: CR should be rejected")
|
||||
}
|
||||
// CR is rejected
|
||||
_, err = instantiateVersionedCustomResource(t, instanceToCreate, noxuResourceClient, noxuDefinition, v.Name)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: CR should be rejected")
|
||||
}
|
||||
|
||||
// update the CRD to a less stricter schema
|
||||
_, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// update the CRD to a less stricter schema
|
||||
_, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
validationSchema, err := getSchemaForVersion(crd, v.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
validationSchema.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// CR is now accepted
|
||||
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
_, err := noxuResourceClient.Create(newNoxuValidationInstance(ns, "foo"), metav1.CreateOptions{})
|
||||
if statusError, isStatus := err.(*apierrors.StatusError); isStatus {
|
||||
if strings.Contains(statusError.Error(), "is invalid") {
|
||||
return false, nil
|
||||
// CR is now accepted
|
||||
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
_, err := noxuResourceClient.Create(instanceToCreate, metav1.CreateOptions{})
|
||||
if _, isStatus := err.(*apierrors.StatusError); isStatus {
|
||||
if apierrors.IsInvalid(err) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
noxuResourceClient.Delete("foo", &metav1.DeleteOptions{})
|
||||
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForbiddenFieldsInSchema(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
noxuDefinition := newNoxuValidationCRD(apiextensionsv1beta1.NamespaceScoped)
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.AdditionalProperties.Allows = false
|
||||
noxuDefinitions := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)
|
||||
for i, noxuDefinition := range noxuDefinitions {
|
||||
for _, v := range noxuDefinition.Spec.Versions {
|
||||
// Re-define the CRD to make sure we start with a clean CRD
|
||||
noxuDefinition := newNoxuValidationCRDs(apiextensionsv1beta1.NamespaceScoped)[i]
|
||||
validationSchema, err := getSchemaForVersion(noxuDefinition, v.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
validationSchema.OpenAPIV3Schema.AdditionalProperties.Allows = false
|
||||
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: additionalProperties cannot be set to false")
|
||||
}
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: additionalProperties cannot be set to false")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
UniqueItems: true,
|
||||
}
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.AdditionalProperties.Allows = true
|
||||
validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
UniqueItems: true,
|
||||
}
|
||||
validationSchema.OpenAPIV3Schema.AdditionalProperties.Allows = true
|
||||
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: uniqueItems cannot be set to true")
|
||||
}
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: uniqueItems cannot be set to true")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta")
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
UniqueItems: false,
|
||||
}
|
||||
validationSchema.OpenAPIV3Schema.Ref = strPtr("#/definition/zeta")
|
||||
validationSchema.OpenAPIV3Schema.Properties["zeta"] = apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
UniqueItems: false,
|
||||
}
|
||||
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatal("unexpected non-error: $ref cannot be non-empty string")
|
||||
}
|
||||
_, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err == nil {
|
||||
t.Fatal("unexpected non-error: $ref cannot be non-empty string")
|
||||
}
|
||||
|
||||
noxuDefinition.Spec.Validation.OpenAPIV3Schema.Ref = nil
|
||||
validationSchema.OpenAPIV3Schema.Ref = nil
|
||||
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := fixtures.DeleteCustomResourceDefinition(noxuDefinition, apiExtensionClient); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/k8s.io/apiextensions-apiserver/test/integration/versioning_test.go
generated
vendored
2
vendor/k8s.io/apiextensions-apiserver/test/integration/versioning_test.go
generated
vendored
@@ -64,7 +64,7 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) {
|
||||
|
||||
// update validation via update because the cache priming in CreateNewCustomResourceDefinition will fail otherwise
|
||||
t.Logf("Updating CRD to validate apiVersion")
|
||||
noxuDefinition, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, noxuDefinition.Name, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
noxuDefinition, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, noxuDefinition.Name, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
|
8
vendor/k8s.io/apiextensions-apiserver/test/integration/yaml_test.go
generated
vendored
8
vendor/k8s.io/apiextensions-apiserver/test/integration/yaml_test.go
generated
vendored
@@ -22,15 +22,18 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
@@ -354,6 +357,7 @@ values:
|
||||
}
|
||||
|
||||
func TestYAMLSubresource(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -369,7 +373,7 @@ func TestYAMLSubresource(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
noxuDefinition := NewNoxuSubresourcesCRD(apiextensionsv1beta1.ClusterScoped)
|
||||
noxuDefinition := NewNoxuSubresourcesCRDs(apiextensionsv1beta1.ClusterScoped)[0]
|
||||
noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
Reference in New Issue
Block a user