github.com/caseydavenport/controller-tools@v0.2.6-0.20200519183242-e8a18b1a6750/pkg/crd/conv.go (about) 1 package crd 2 3 import ( 4 "fmt" 5 6 apiextinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" 7 apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 8 apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 9 "k8s.io/apimachinery/pkg/api/equality" 10 "k8s.io/apimachinery/pkg/runtime" 11 "k8s.io/apimachinery/pkg/runtime/schema" 12 ) 13 14 var ( 15 conversionScheme = runtime.NewScheme() 16 ) 17 18 func init() { 19 if err := apiextinternal.AddToScheme(conversionScheme); err != nil { 20 panic("must be able to add internal apiextensions to the CRD conversion Scheme") 21 } 22 if err := apiext.AddToScheme(conversionScheme); err != nil { 23 panic("must be able to add apiextensions/v1 to the CRD conversion Scheme") 24 } 25 if err := apiextv1beta1.AddToScheme(conversionScheme); err != nil { 26 panic("must be able to add apiextensions/v1beta1 to the CRD conversion Scheme") 27 } 28 } 29 30 // AsVersion converts a CRD from the canonical internal form (currently v1) to some external form. 31 func AsVersion(original apiext.CustomResourceDefinition, gv schema.GroupVersion) (runtime.Object, error) { 32 // We can use the internal versions an existing conversions from kubernetes, since they're not in k/k itself. 33 // This punts the problem of conversion down the road for a future maintainer (or future instance of @directxman12) 34 // when we have to support older versions that get removed, or when API machinery decides to yell at us for this 35 // questionable decision. 36 intVer, err := conversionScheme.ConvertToVersion(&original, apiextinternal.SchemeGroupVersion) 37 if err != nil { 38 return nil, fmt.Errorf("unable to convert to internal CRD version: %w", err) 39 } 40 41 return conversionScheme.ConvertToVersion(intVer, gv) 42 } 43 44 // mergeIdenticalSubresources checks to see if subresources are identical across 45 // all versions, and if so, merges them into a top-level version. 46 // 47 // This assumes you're not using trivial versions. 48 func mergeIdenticalSubresources(crd *apiextv1beta1.CustomResourceDefinition) { 49 subres := crd.Spec.Versions[0].Subresources 50 for _, ver := range crd.Spec.Versions { 51 if ver.Subresources == nil || !equality.Semantic.DeepEqual(subres, ver.Subresources) { 52 // either all nil, or not identical 53 return 54 } 55 } 56 57 // things are identical if we've gotten this far, so move the subresources up 58 // and discard the identical per-version ones 59 crd.Spec.Subresources = subres 60 for i := range crd.Spec.Versions { 61 crd.Spec.Versions[i].Subresources = nil 62 } 63 } 64 65 // mergeIdenticalSchemata checks to see if schemata are identical across 66 // all versions, and if so, merges them into a top-level version. 67 // 68 // This assumes you're not using trivial versions. 69 func mergeIdenticalSchemata(crd *apiextv1beta1.CustomResourceDefinition) { 70 schema := crd.Spec.Versions[0].Schema 71 for _, ver := range crd.Spec.Versions { 72 if ver.Schema == nil || !equality.Semantic.DeepEqual(schema, ver.Schema) { 73 // either all nil, or not identical 74 return 75 } 76 } 77 78 // things are identical if we've gotten this far, so move the schemata up 79 // to a single schema and discard the identical per-version ones 80 crd.Spec.Validation = schema 81 for i := range crd.Spec.Versions { 82 crd.Spec.Versions[i].Schema = nil 83 } 84 } 85 86 // mergeIdenticalPrinterColumns checks to see if schemata are identical across 87 // all versions, and if so, merges them into a top-level version. 88 // 89 // This assumes you're not using trivial versions. 90 func mergeIdenticalPrinterColumns(crd *apiextv1beta1.CustomResourceDefinition) { 91 cols := crd.Spec.Versions[0].AdditionalPrinterColumns 92 for _, ver := range crd.Spec.Versions { 93 if len(ver.AdditionalPrinterColumns) == 0 || !equality.Semantic.DeepEqual(cols, ver.AdditionalPrinterColumns) { 94 // either all nil, or not identical 95 return 96 } 97 } 98 99 // things are identical if we've gotten this far, so move the printer columns up 100 // and discard the identical per-version ones 101 crd.Spec.AdditionalPrinterColumns = cols 102 for i := range crd.Spec.Versions { 103 crd.Spec.Versions[i].AdditionalPrinterColumns = nil 104 } 105 } 106 107 // MergeIdenticalVersionInfo makes sure that components of the Versions field that are identical 108 // across all versions get merged into the top-level fields in v1beta1. 109 // 110 // This is required by the Kubernetes API server validation. 111 // 112 // The reason is that a v1beta1 -> v1 -> v1beta1 conversion cycle would need to 113 // round-trip identically, v1 doesn't have top-level subresources, and without 114 // this restriction it would be ambiguous how a v1-with-identical-subresources 115 // converts into a v1beta1). 116 func MergeIdenticalVersionInfo(crd *apiextv1beta1.CustomResourceDefinition) { 117 if len(crd.Spec.Versions) > 0 { 118 mergeIdenticalSubresources(crd) 119 mergeIdenticalSchemata(crd) 120 mergeIdenticalPrinterColumns(crd) 121 } 122 }