mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
CRD v1: drop top-level version, validation, subresources, additionalPrinterColumns fields
This commit is contained in:
parent
57e5848701
commit
27360db1da
@ -46,6 +46,10 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
|
||||
obj.Names.ListKind = obj.Names.Kind + "List"
|
||||
}
|
||||
if len(obj.Versions) == 0 && len(obj.Version) == 0 {
|
||||
// internal object must have a version to roundtrip all fields
|
||||
obj.Version = "v1"
|
||||
}
|
||||
if len(obj.Versions) == 0 && len(obj.Version) != 0 {
|
||||
obj.Versions = []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
@ -73,6 +77,23 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
if obj.PreserveUnknownFields == nil {
|
||||
obj.PreserveUnknownFields = pointer.BoolPtr(true)
|
||||
}
|
||||
|
||||
// Move per-version schema, subresources, additionalPrinterColumns to the top-level.
|
||||
// This is required by validation in v1beta1, and by round-tripping in v1.
|
||||
if len(obj.Versions) == 1 {
|
||||
if obj.Versions[0].Schema != nil {
|
||||
obj.Validation = obj.Versions[0].Schema
|
||||
obj.Versions[0].Schema = nil
|
||||
}
|
||||
if obj.Versions[0].AdditionalPrinterColumns != nil {
|
||||
obj.AdditionalPrinterColumns = obj.Versions[0].AdditionalPrinterColumns
|
||||
obj.Versions[0].AdditionalPrinterColumns = nil
|
||||
}
|
||||
if obj.Versions[0].Subresources != nil {
|
||||
obj.Subresources = obj.Versions[0].Subresources
|
||||
obj.Versions[0].Subresources = nil
|
||||
}
|
||||
}
|
||||
},
|
||||
func(obj *apiextensions.CustomResourceDefinition, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj)
|
||||
|
@ -17,11 +17,11 @@ limitations under the License.
|
||||
package v1
|
||||
|
||||
import (
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
)
|
||||
|
||||
func addConversionFuncs(scheme *runtime.Scheme) error {
|
||||
@ -71,3 +71,104 @@ func Convert_v1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_apiextensions_CustomResourceDefinitionSpec_To_v1_CustomResourceDefinitionSpec(in *apiextensions.CustomResourceDefinitionSpec, out *CustomResourceDefinitionSpec, s conversion.Scope) error {
|
||||
if err := autoConvert_apiextensions_CustomResourceDefinitionSpec_To_v1_CustomResourceDefinitionSpec(in, out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(out.Versions) == 0 && len(in.Version) > 0 {
|
||||
// no versions were specified, and a version name was specified
|
||||
out.Versions = []CustomResourceDefinitionVersion{{Name: in.Version, Served: true, Storage: true}}
|
||||
}
|
||||
|
||||
// If spec.{subresources,validation,additionalPrinterColumns} exists, move to versions
|
||||
if in.Subresources != nil {
|
||||
subresources := &CustomResourceSubresources{}
|
||||
if err := Convert_apiextensions_CustomResourceSubresources_To_v1_CustomResourceSubresources(in.Subresources, subresources, s); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range out.Versions {
|
||||
out.Versions[i].Subresources = subresources
|
||||
}
|
||||
}
|
||||
if in.Validation != nil {
|
||||
schema := &CustomResourceValidation{}
|
||||
if err := Convert_apiextensions_CustomResourceValidation_To_v1_CustomResourceValidation(in.Validation, schema, s); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range out.Versions {
|
||||
out.Versions[i].Schema = schema
|
||||
}
|
||||
}
|
||||
if in.AdditionalPrinterColumns != nil {
|
||||
additionalPrinterColumns := make([]CustomResourceColumnDefinition, len(in.AdditionalPrinterColumns))
|
||||
for i := range in.AdditionalPrinterColumns {
|
||||
if err := Convert_apiextensions_CustomResourceColumnDefinition_To_v1_CustomResourceColumnDefinition(&in.AdditionalPrinterColumns[i], &additionalPrinterColumns[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range out.Versions {
|
||||
out.Versions[i].AdditionalPrinterColumns = additionalPrinterColumns
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(in *CustomResourceDefinitionSpec, out *apiextensions.CustomResourceDefinitionSpec, s conversion.Scope) error {
|
||||
if err := autoConvert_v1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(in, out, s); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(out.Versions) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy versions[0] to version
|
||||
out.Version = out.Versions[0].Name
|
||||
|
||||
// If versions[*].{subresources,schema,additionalPrinterColumns} are identical, move to spec
|
||||
subresources := out.Versions[0].Subresources
|
||||
subresourcesIdentical := true
|
||||
validation := out.Versions[0].Schema
|
||||
validationIdentical := true
|
||||
additionalPrinterColumns := out.Versions[0].AdditionalPrinterColumns
|
||||
additionalPrinterColumnsIdentical := true
|
||||
|
||||
// Detect if per-version fields are identical
|
||||
for _, v := range out.Versions {
|
||||
if subresourcesIdentical && !apiequality.Semantic.DeepEqual(v.Subresources, subresources) {
|
||||
subresourcesIdentical = false
|
||||
}
|
||||
if validationIdentical && !apiequality.Semantic.DeepEqual(v.Schema, validation) {
|
||||
validationIdentical = false
|
||||
}
|
||||
if additionalPrinterColumnsIdentical && !apiequality.Semantic.DeepEqual(v.AdditionalPrinterColumns, additionalPrinterColumns) {
|
||||
additionalPrinterColumnsIdentical = false
|
||||
}
|
||||
}
|
||||
|
||||
// If they are, set the top-level fields and clear the per-version fields
|
||||
if subresourcesIdentical {
|
||||
out.Subresources = subresources
|
||||
}
|
||||
if validationIdentical {
|
||||
out.Validation = validation
|
||||
}
|
||||
if additionalPrinterColumnsIdentical {
|
||||
out.AdditionalPrinterColumns = additionalPrinterColumns
|
||||
}
|
||||
for i := range out.Versions {
|
||||
if subresourcesIdentical {
|
||||
out.Versions[i].Subresources = nil
|
||||
}
|
||||
if validationIdentical {
|
||||
out.Versions[i].Schema = nil
|
||||
}
|
||||
if additionalPrinterColumnsIdentical {
|
||||
out.Versions[i].AdditionalPrinterColumns = nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -18,13 +18,395 @@ package v1
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestConversion(t *testing.T) {
|
||||
testcases := []struct {
|
||||
Name string
|
||||
In runtime.Object
|
||||
Out runtime.Object
|
||||
ExpectOut runtime.Object
|
||||
ExpectErr string
|
||||
}{
|
||||
// Versions
|
||||
{
|
||||
Name: "internal to v1, no versions",
|
||||
In: &apiextensions.CustomResourceDefinition{},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{},
|
||||
},
|
||||
{
|
||||
Name: "internal to v1, top-level version",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{{Name: "v1", Served: true, Storage: true}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "internal to v1, multiple versions",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: false, Storage: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: false, Storage: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, no versions",
|
||||
In: &CustomResourceDefinition{},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, single version",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{{Name: "v1", Served: true, Storage: true}},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{{Name: "v1", Served: true, Storage: true}},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, multiple versions",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: false, Storage: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: false, Storage: false},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Validation
|
||||
{
|
||||
Name: "internal to v1, top-level validation moves to per-version",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Validation: &apiextensions.CustomResourceValidation{OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object"}},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Type: "object"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "internal to v1, per-version validation is preserved",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Schema: &apiextensions.CustomResourceValidation{OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Description: "v1", Type: "object"}}},
|
||||
{Name: "v2", Served: false, Storage: false, Schema: &apiextensions.CustomResourceValidation{OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Description: "v2", Type: "object"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Description: "v1", Type: "object"}}},
|
||||
{Name: "v2", Served: false, Storage: false, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Description: "v2", Type: "object"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, identical validation moves to top-level",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Type: "object"}}},
|
||||
{Name: "v2", Served: true, Storage: false, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Type: "object"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: true, Storage: false},
|
||||
},
|
||||
Validation: &apiextensions.CustomResourceValidation{OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object"}},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, distinct validation remains per-version",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Description: "v1", Type: "object"}}},
|
||||
{Name: "v2", Served: true, Storage: false, Schema: &CustomResourceValidation{OpenAPIV3Schema: &JSONSchemaProps{Description: "v2", Type: "object"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Schema: &apiextensions.CustomResourceValidation{OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Description: "v1", Type: "object"}}},
|
||||
{Name: "v2", Served: true, Storage: false, Schema: &apiextensions.CustomResourceValidation{OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Description: "v2", Type: "object"}}},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Subresources
|
||||
{
|
||||
Name: "internal to v1, top-level subresources moves to per-version",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Subresources: &apiextensions.CustomResourceSubresources{Scale: &apiextensions.CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas"}},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "internal to v1, per-version subresources is preserved",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Subresources: &apiextensions.CustomResourceSubresources{Scale: &apiextensions.CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas1"}}},
|
||||
{Name: "v2", Served: false, Storage: false, Subresources: &apiextensions.CustomResourceSubresources{Scale: &apiextensions.CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas1"}}},
|
||||
{Name: "v2", Served: false, Storage: false, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, identical subresources moves to top-level",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas"}}},
|
||||
{Name: "v2", Served: true, Storage: false, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: true, Storage: false},
|
||||
},
|
||||
Subresources: &apiextensions.CustomResourceSubresources{Scale: &apiextensions.CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas"}},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, distinct subresources remains per-version",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas1"}}},
|
||||
{Name: "v2", Served: true, Storage: false, Subresources: &CustomResourceSubresources{Scale: &CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, Subresources: &apiextensions.CustomResourceSubresources{Scale: &apiextensions.CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas1"}}},
|
||||
{Name: "v2", Served: true, Storage: false, Subresources: &apiextensions.CustomResourceSubresources{Scale: &apiextensions.CustomResourceSubresourceScale{SpecReplicasPath: "spec.replicas2"}}},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
// Additional Printer Columns
|
||||
{
|
||||
Name: "internal to v1, top-level printer columns moves to per-version",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "column1"}},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "internal to v1, per-version printer columns is preserved",
|
||||
In: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
{Name: "v2", Served: false, Storage: false, AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "column2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &CustomResourceDefinition{},
|
||||
ExpectOut: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
{Name: "v2", Served: false, Storage: false, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, identical printer columns moves to top-level",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
{Name: "v2", Served: true, Storage: false, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true},
|
||||
{Name: "v2", Served: true, Storage: false},
|
||||
},
|
||||
AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "column1"}},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "v1 to internal, distinct printer columns remains per-version",
|
||||
In: &CustomResourceDefinition{
|
||||
Spec: CustomResourceDefinitionSpec{
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
{Name: "v2", Served: true, Storage: false, AdditionalPrinterColumns: []CustomResourceColumnDefinition{{Name: "column2"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Out: &apiextensions.CustomResourceDefinition{},
|
||||
ExpectOut: &apiextensions.CustomResourceDefinition{
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Version: "v1",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Served: true, Storage: true, AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "column1"}}},
|
||||
{Name: "v2", Served: true, Storage: false, AdditionalPrinterColumns: []apiextensions.CustomResourceColumnDefinition{{Name: "column2"}}},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
// add internal and external types
|
||||
if err := apiextensions.AddToScheme(scheme); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := AddToScheme(scheme); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
err := scheme.Convert(tc.In, tc.Out, nil)
|
||||
if err != nil {
|
||||
if len(tc.ExpectErr) == 0 {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.ExpectErr) {
|
||||
t.Fatalf("expected error %s, got %v", tc.ExpectErr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(tc.ExpectErr) > 0 {
|
||||
t.Fatalf("expected error %s, got none", tc.ExpectErr)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.Out, tc.ExpectOut) {
|
||||
t.Fatalf("unexpected result:\n %s", cmp.Diff(tc.ExpectOut, tc.Out))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONConversion(t *testing.T) {
|
||||
nilJSON := apiextensions.JSON(nil)
|
||||
nullJSON := apiextensions.JSON("null")
|
||||
|
@ -49,18 +49,6 @@ func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec)
|
||||
if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
|
||||
obj.Names.ListKind = obj.Names.Kind + "List"
|
||||
}
|
||||
// If there is no list of versions, create on using deprecated Version field.
|
||||
if len(obj.Versions) == 0 && len(obj.Version) != 0 {
|
||||
obj.Versions = []CustomResourceDefinitionVersion{{
|
||||
Name: obj.Version,
|
||||
Storage: true,
|
||||
Served: true,
|
||||
}}
|
||||
}
|
||||
// For backward compatibility set the version field to the first item in versions list.
|
||||
if len(obj.Version) == 0 && len(obj.Versions) != 0 {
|
||||
obj.Version = obj.Versions[0].Name
|
||||
}
|
||||
if obj.Conversion == nil {
|
||||
obj.Conversion = &CustomResourceConversion{
|
||||
Strategy: NoneConverter,
|
||||
|
@ -92,7 +92,6 @@ func TestDefaults(t *testing.T) {
|
||||
Scope: NamespaceScoped,
|
||||
Conversion: &CustomResourceConversion{Strategy: NoneConverter},
|
||||
PreserveUnknownFields: utilpointer.BoolPtr(true),
|
||||
Version: "v1",
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Storage: false, Served: true},
|
||||
{Name: "v2", Storage: true, Served: true},
|
||||
@ -111,7 +110,9 @@ func TestDefaults(t *testing.T) {
|
||||
Scope: NamespaceScoped,
|
||||
Conversion: &CustomResourceConversion{Strategy: NoneConverter},
|
||||
PreserveUnknownFields: utilpointer.BoolPtr(true),
|
||||
Version: "v1",
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Storage: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &CustomResourceDefinition{
|
||||
@ -119,9 +120,8 @@ func TestDefaults(t *testing.T) {
|
||||
Scope: NamespaceScoped,
|
||||
Conversion: &CustomResourceConversion{Strategy: NoneConverter},
|
||||
PreserveUnknownFields: utilpointer.BoolPtr(true),
|
||||
Version: "v1",
|
||||
Versions: []CustomResourceDefinitionVersion{
|
||||
{Name: "v1", Storage: true, Served: true},
|
||||
{Name: "v1", Storage: true},
|
||||
},
|
||||
},
|
||||
Status: CustomResourceDefinitionStatus{
|
||||
|
@ -36,30 +36,11 @@ const (
|
||||
type CustomResourceDefinitionSpec struct {
|
||||
// Group is the group this resource belongs in
|
||||
Group string `json:"group" protobuf:"bytes,1,opt,name=group"`
|
||||
// Version is the version this resource belongs in
|
||||
// Should be always first item in Versions field if provided.
|
||||
// Optional, but at least one of Version or Versions must be set.
|
||||
// Deprecated: Please use `Versions`.
|
||||
// +optional
|
||||
Version string `json:"version,omitempty" protobuf:"bytes,2,opt,name=version"`
|
||||
// Names are the names used to describe this custom resource
|
||||
Names CustomResourceDefinitionNames `json:"names" protobuf:"bytes,3,opt,name=names"`
|
||||
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
|
||||
Scope ResourceScope `json:"scope" protobuf:"bytes,4,opt,name=scope,casttype=ResourceScope"`
|
||||
// Validation describes the validation methods for CustomResources
|
||||
// Optional, the global validation schema for all versions.
|
||||
// Top-level and per-version schemas are mutually exclusive.
|
||||
// +optional
|
||||
Validation *CustomResourceValidation `json:"validation,omitempty" protobuf:"bytes,5,opt,name=validation"`
|
||||
// Subresources describes the subresources for CustomResource
|
||||
// Optional, the global subresources for all versions.
|
||||
// Top-level and per-version subresources are mutually exclusive.
|
||||
// +optional
|
||||
Subresources *CustomResourceSubresources `json:"subresources,omitempty" protobuf:"bytes,6,opt,name=subresources"`
|
||||
// Versions is the list of all supported versions for this resource.
|
||||
// If Version field is provided, this field is optional.
|
||||
// Validation: All versions must use the same validation schema for now. i.e., top
|
||||
// level Validation field is applied to all of these versions.
|
||||
// Order: The version name will be used to compute the order.
|
||||
// If the version string is "kube-like", it will sort above non "kube-like" version strings, which are ordered
|
||||
// lexicographically. "Kube-like" versions start with a "v", then are followed by a number (the major version),
|
||||
@ -67,13 +48,7 @@ type CustomResourceDefinitionSpec struct {
|
||||
// by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing
|
||||
// major version, then minor version. An example sorted list of versions:
|
||||
// v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.
|
||||
// +optional
|
||||
Versions []CustomResourceDefinitionVersion `json:"versions,omitempty" protobuf:"bytes,7,rep,name=versions"`
|
||||
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||
// Optional, the global columns for all versions.
|
||||
// Top-level and per-version columns are mutually exclusive.
|
||||
// +optional
|
||||
AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,8,rep,name=additionalPrinterColumns"`
|
||||
Versions []CustomResourceDefinitionVersion `json:"versions" protobuf:"bytes,7,rep,name=versions"`
|
||||
|
||||
// `conversion` defines conversion settings for the CRD.
|
||||
// +optional
|
||||
@ -187,24 +162,12 @@ type CustomResourceDefinitionVersion struct {
|
||||
// flagged as storage version.
|
||||
Storage bool `json:"storage" protobuf:"varint,3,opt,name=storage"`
|
||||
// Schema describes the schema for CustomResource used in validation, pruning, and defaulting.
|
||||
// Top-level and per-version schemas are mutually exclusive.
|
||||
// Per-version schemas must not all be set to identical values (top-level validation schema should be used instead)
|
||||
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||
// +optional
|
||||
Schema *CustomResourceValidation `json:"schema,omitempty" protobuf:"bytes,4,opt,name=schema"`
|
||||
// Subresources describes the subresources for CustomResource
|
||||
// Top-level and per-version subresources are mutually exclusive.
|
||||
// Per-version subresources must not all be set to identical values (top-level subresources should be used instead)
|
||||
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||
// +optional
|
||||
Subresources *CustomResourceSubresources `json:"subresources,omitempty" protobuf:"bytes,5,opt,name=subresources"`
|
||||
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
|
||||
// Top-level and per-version columns are mutually exclusive.
|
||||
// Per-version columns must not all be set to identical values (top-level columns should be used instead)
|
||||
// This field is alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
|
||||
// NOTE: CRDs created prior to 1.13 populated the top-level additionalPrinterColumns field by default. To apply an
|
||||
// update that changes to per-version additionalPrinterColumns, the top-level additionalPrinterColumns field must
|
||||
// be explicitly set to null
|
||||
// +optional
|
||||
AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,6,rep,name=additionalPrinterColumns"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user