Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⚠ bump to kubernetes 1.22.1, remove support for legacy v1beta1 CRDs and webhooks #607

Merged
merged 1 commit into from
Sep 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ require (
github.com/spf13/pflag v1.0.5
golang.org/x/tools v0.1.5
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.21.3
k8s.io/apiextensions-apiserver v0.21.3
k8s.io/apimachinery v0.21.3
k8s.io/api v0.22.2
k8s.io/apiextensions-apiserver v0.22.2
k8s.io/apimachinery v0.22.2
sigs.k8s.io/yaml v1.2.0
)
170 changes: 98 additions & 72 deletions go.sum

Large diffs are not rendered by default.

89 changes: 5 additions & 84 deletions pkg/crd/conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
apiextinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
Expand All @@ -22,13 +21,15 @@ func init() {
if err := apiext.AddToScheme(conversionScheme); err != nil {
panic("must be able to add apiextensions/v1 to the CRD conversion Scheme")
}
if err := apiextv1beta1.AddToScheme(conversionScheme); err != nil {
panic("must be able to add apiextensions/v1beta1 to the CRD conversion Scheme")
}
}

// AsVersion converts a CRD from the canonical internal form (currently v1) to some external form.
func AsVersion(original apiext.CustomResourceDefinition, gv schema.GroupVersion) (runtime.Object, error) {
// TODO: Do we need to keep maintaining this conversion function
// post 1.22 when only CRDv1 is served by the apiserver?
if gv == apiextv1beta1.SchemeGroupVersion {
return nil, fmt.Errorf("apiVersion %q is not supported", gv.String())
}
// We can use the internal versions an existing conversions from kubernetes, since they're not in k/k itself.
// This punts the problem of conversion down the road for a future maintainer (or future instance of @directxman12)
// when we have to support older versions that get removed, or when API machinery decides to yell at us for this
Expand All @@ -40,83 +41,3 @@ func AsVersion(original apiext.CustomResourceDefinition, gv schema.GroupVersion)

return conversionScheme.ConvertToVersion(intVer, gv)
}

// mergeIdenticalSubresources checks to see if subresources are identical across
// all versions, and if so, merges them into a top-level version.
//
// This assumes you're not using trivial versions.
func mergeIdenticalSubresources(crd *apiextv1beta1.CustomResourceDefinition) {
subres := crd.Spec.Versions[0].Subresources
for _, ver := range crd.Spec.Versions {
if ver.Subresources == nil || !equality.Semantic.DeepEqual(subres, ver.Subresources) {
// either all nil, or not identical
return
}
}

// things are identical if we've gotten this far, so move the subresources up
// and discard the identical per-version ones
crd.Spec.Subresources = subres
for i := range crd.Spec.Versions {
crd.Spec.Versions[i].Subresources = nil
}
}

// mergeIdenticalSchemata checks to see if schemata are identical across
// all versions, and if so, merges them into a top-level version.
//
// This assumes you're not using trivial versions.
func mergeIdenticalSchemata(crd *apiextv1beta1.CustomResourceDefinition) {
schema := crd.Spec.Versions[0].Schema
for _, ver := range crd.Spec.Versions {
if ver.Schema == nil || !equality.Semantic.DeepEqual(schema, ver.Schema) {
// either all nil, or not identical
return
}
}

// things are identical if we've gotten this far, so move the schemata up
// to a single schema and discard the identical per-version ones
crd.Spec.Validation = schema
for i := range crd.Spec.Versions {
crd.Spec.Versions[i].Schema = nil
}
}

// mergeIdenticalPrinterColumns checks to see if schemata are identical across
// all versions, and if so, merges them into a top-level version.
//
// This assumes you're not using trivial versions.
func mergeIdenticalPrinterColumns(crd *apiextv1beta1.CustomResourceDefinition) {
cols := crd.Spec.Versions[0].AdditionalPrinterColumns
for _, ver := range crd.Spec.Versions {
if len(ver.AdditionalPrinterColumns) == 0 || !equality.Semantic.DeepEqual(cols, ver.AdditionalPrinterColumns) {
// either all nil, or not identical
return
}
}

// things are identical if we've gotten this far, so move the printer columns up
// and discard the identical per-version ones
crd.Spec.AdditionalPrinterColumns = cols
for i := range crd.Spec.Versions {
crd.Spec.Versions[i].AdditionalPrinterColumns = nil
}
}

// MergeIdenticalVersionInfo makes sure that components of the Versions field that are identical
// across all versions get merged into the top-level fields in v1beta1.
//
// This is required by the Kubernetes API server validation.
//
// The reason is that a v1beta1 -> v1 -> v1beta1 conversion cycle would need to
// round-trip identically, v1 doesn't have top-level subresources, and without
// this restriction it would be ambiguous how a v1-with-identical-subresources
// converts into a v1beta1).
func MergeIdenticalVersionInfo(crd *apiextv1beta1.CustomResourceDefinition) {
if len(crd.Spec.Versions) > 0 {
mergeIdenticalSubresources(crd)
mergeIdenticalSchemata(crd)
mergeIdenticalPrinterColumns(crd)
}
}
267 changes: 0 additions & 267 deletions pkg/crd/crd_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,275 +18,8 @@ package crd_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apiextlegacy "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

"sigs.k8s.io/controller-tools/pkg/crd"
)

var _ = Describe("CRD Generation", func() {
Describe("Utilities", func() {
Describe("MergeIdenticalVersionInfo", func() {
It("should replace per-version schemata with a top-level schema if only one version", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Storage: true,
Schema: &apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Required: []string{"foo"},
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
},
},
},
},
}
crd.MergeIdenticalVersionInfo(spec)
Expect(spec.Spec.Validation).To(Equal(&apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Required: []string{"foo"},
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
}))
Expect(spec.Spec.Versions).To(Equal([]apiextlegacy.CustomResourceDefinitionVersion{
{Name: "v1", Storage: true},
}))
})
It("should replace per-version schemata with a top-level schema if all are identical", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Schema: &apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Required: []string{"foo"},
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
},
},
{
Name: "v2",
Storage: true,
Schema: &apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Required: []string{"foo"},
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
},
},
},
},
}
crd.MergeIdenticalVersionInfo(spec)
Expect(spec.Spec.Validation).To(Equal(&apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Required: []string{"foo"},
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
}))
Expect(spec.Spec.Versions).To(Equal([]apiextlegacy.CustomResourceDefinitionVersion{
{Name: "v1"}, {Name: "v2", Storage: true},
}))
})

It("shouldn't merge different schemata", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Schema: &apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
},
},
{
Name: "v2",
Storage: true,
Schema: &apiextlegacy.CustomResourceValidation{
OpenAPIV3Schema: &apiextlegacy.JSONSchemaProps{
Required: []string{"foo"},
Type: "object",
Properties: map[string]apiextlegacy.JSONSchemaProps{"foo": {Type: "string"}},
},
},
},
},
},
}
orig := spec.DeepCopy()
crd.MergeIdenticalVersionInfo(spec)
Expect(spec).To(Equal(orig))
})

It("should replace per-version subresources with top-level subresources if only one version", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Storage: true,
Subresources: &apiextlegacy.CustomResourceSubresources{
Status: &apiextlegacy.CustomResourceSubresourceStatus{},
},
},
},
},
}

crd.MergeIdenticalVersionInfo(spec)
Expect(spec.Spec.Subresources).To(Equal(&apiextlegacy.CustomResourceSubresources{
Status: &apiextlegacy.CustomResourceSubresourceStatus{},
}))
Expect(spec.Spec.Versions).To(Equal([]apiextlegacy.CustomResourceDefinitionVersion{
{Name: "v1", Storage: true},
}))
})

It("should replace per-version subresources with top-level subresources if all are identical", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Subresources: &apiextlegacy.CustomResourceSubresources{
Status: &apiextlegacy.CustomResourceSubresourceStatus{},
},
},
{
Name: "v2",
Storage: true,
Subresources: &apiextlegacy.CustomResourceSubresources{
Status: &apiextlegacy.CustomResourceSubresourceStatus{},
},
},
},
},
}

crd.MergeIdenticalVersionInfo(spec)
Expect(spec.Spec.Subresources).To(Equal(&apiextlegacy.CustomResourceSubresources{
Status: &apiextlegacy.CustomResourceSubresourceStatus{},
}))
Expect(spec.Spec.Versions).To(Equal([]apiextlegacy.CustomResourceDefinitionVersion{
{Name: "v1"}, {Name: "v2", Storage: true},
}))
})

It("shouldn't merge different subresources", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Subresources: &apiextlegacy.CustomResourceSubresources{
Status: &apiextlegacy.CustomResourceSubresourceStatus{},
},
},
{
Name: "v2",
Storage: true,
},
},
},
}
orig := spec.DeepCopy()
crd.MergeIdenticalVersionInfo(spec)
Expect(spec).To(Equal(orig))
})

It("should replace per-version printer columns with top-level printer columns if only one version", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
Storage: true,
AdditionalPrinterColumns: []apiextlegacy.CustomResourceColumnDefinition{
{Name: "Cheddar", JSONPath: ".spec.cheddar"},
{Name: "Parmesan", JSONPath: ".status.parmesan"},
},
},
},
},
}

crd.MergeIdenticalVersionInfo(spec)
Expect(spec.Spec.AdditionalPrinterColumns).To(Equal([]apiextlegacy.CustomResourceColumnDefinition{
{Name: "Cheddar", JSONPath: ".spec.cheddar"},
{Name: "Parmesan", JSONPath: ".status.parmesan"},
}))
Expect(spec.Spec.Versions).To(Equal([]apiextlegacy.CustomResourceDefinitionVersion{
{Name: "v1", Storage: true},
}))
})

It("should replace per-version printer columns with top-level printer columns if all are identical", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
AdditionalPrinterColumns: []apiextlegacy.CustomResourceColumnDefinition{
{Name: "Cheddar", JSONPath: ".spec.cheddar"},
{Name: "Parmesan", JSONPath: ".status.parmesan"},
},
},
{
Name: "v2",
Storage: true,
AdditionalPrinterColumns: []apiextlegacy.CustomResourceColumnDefinition{
{Name: "Cheddar", JSONPath: ".spec.cheddar"},
{Name: "Parmesan", JSONPath: ".status.parmesan"},
},
},
},
},
}

crd.MergeIdenticalVersionInfo(spec)
Expect(spec.Spec.AdditionalPrinterColumns).To(Equal([]apiextlegacy.CustomResourceColumnDefinition{
{Name: "Cheddar", JSONPath: ".spec.cheddar"},
{Name: "Parmesan", JSONPath: ".status.parmesan"},
}))
Expect(spec.Spec.Versions).To(Equal([]apiextlegacy.CustomResourceDefinitionVersion{
{Name: "v1"}, {Name: "v2", Storage: true},
}))
})

It("shouldn't merge different printer columns", func() {
spec := &apiextlegacy.CustomResourceDefinition{
Spec: apiextlegacy.CustomResourceDefinitionSpec{
Versions: []apiextlegacy.CustomResourceDefinitionVersion{
{
Name: "v1",
AdditionalPrinterColumns: []apiextlegacy.CustomResourceColumnDefinition{
{Name: "Cheddar", JSONPath: ".spec.cheddar"},
{Name: "Parmesan", JSONPath: ".status.parmesan"},
},
},
{
Name: "v2",
Storage: true,
},
},
},
}
orig := spec.DeepCopy()
crd.MergeIdenticalVersionInfo(spec)
Expect(spec).To(Equal(orig))
})
})
})
})
Loading