diff --git a/Makefile b/Makefile index 61fb506..e50feb9 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,11 @@ endif HACK_BIN=$(shell pwd)/hack/bin +# Set --output-base for conversion-gen if we are not within GOPATH +ifneq ($(abspath $(ROOT_DIR)),$(shell go env GOPATH)/src/sigs.k8s.io/cluster-api-ipam-provider-in-cluster) + OUTPUT_BASE := --output-base=$(ROOT_DIR) +endif + # Setting SHELL to bash allows bash commands to be executed by recipes. # This is a requirement for 'setup-envtest.sh' in the test target. # Options are set to exit when a recipe line exits non-zero or a piped command fails. @@ -59,8 +64,11 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +generate: controller-gen conversion-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + $(CONVERSION_GEN) --input-dirs=./api/v1alpha1 \ + --output-file-base=zz_generated.conversion $(OUTPUT_BASE) \ + --go-header-file=./hack/boilerplate.go.txt .PHONY: fmt fmt: ## Run go fmt against code. @@ -192,4 +200,9 @@ go-licenses: .PHONY: verify-boilerplate verify-boilerplate: ## Verifies all sources have appropriate boilerplate - ./hack/verify-boilerplate.sh \ No newline at end of file + ./hack/verify-boilerplate.sh + +CONVERSION_GEN = $(HACK_BIN)/conversion-gen +.PHONY: conversion-gen +conversion-gen: ## Download conversion-gen locally if necessary. + env GOBIN=$(HACK_BIN) go install k8s.io/code-generator/cmd/conversion-gen@latest diff --git a/PROJECT b/PROJECT index ac8e391..12e1891 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: cluster.x-k8s.io layout: - go.kubebuilder.io/v3 @@ -12,4 +16,37 @@ resources: kind: InClusterIPPool path: sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: ipam + kind: InClusterIPPool + path: sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: ipam + kind: GlobalInClusterIPPool + path: sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1 + version: v1alpha1 + webhooks: + conversion: true + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: ipam + kind: GlobalInClusterIPPool + path: sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + webhookVersion: v1 version: "3" diff --git a/README.md b/README.md index a1963e7..24808fa 100644 --- a/README.md +++ b/README.md @@ -4,32 +4,10 @@ This is a IPAM provider for Cluster API that manages pools of IP addresses using ## Usage -This provider comes with a `InClusterIPPool` resource to specify the pools from which addresses should be assigned. You can provide a subnet in CIDR notation, or specify an address range using start and end addresses, as well as a prefix length, or a set of addresses with the prefix and gateway. +This provider comes with a `InClusterIPPool` resource to specify the pools from which addresses should be assigned. You can provide an address range using start and end addresses, as well as a prefix length, or a set of addresses with the prefix and gateway. ```yaml -apiVersion: ipam.cluster.x-k8s.io/v1alpha1 -kind: InClusterIPPool -metadata: - name: inclusterippool-sample -spec: - subnet: 10.0.0.0/24 - gateway: 10.0.0.1 -``` - -```yaml -apiVersion: ipam.cluster.x-k8s.io/v1alpha1 -kind: InClusterIPPool -metadata: - name: inclusterippool-sample -spec: - first: 10.0.0.10 - last: 10.10.0.42 - prefix: 24 - gateway: 10.0.0.1 -``` - -```yaml -apiVersion: ipam.cluster.x-k8s.io/v1alpha1 +apiVersion: ipam.cluster.x-k8s.io/v1alpha2 kind: InClusterIPPool metadata: name: inclusterippool-sample diff --git a/api/v1alpha1/conversion.go b/api/v1alpha1/conversion.go new file mode 100644 index 0000000..b3b5f51 --- /dev/null +++ b/api/v1alpha1/conversion.go @@ -0,0 +1,59 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + "net/netip" + + "go4.org/netipx" + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/util/validation/field" + + v1alpha2 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" +) + +func Convert_v1alpha1_InClusterIPPoolSpec_To_v1alpha2_InClusterIPPoolSpec(in *InClusterIPPoolSpec, out *v1alpha2.InClusterIPPoolSpec, scope conversion.Scope) error { + out.Gateway = in.Gateway + out.Prefix = in.Prefix + out.Addresses = in.Addresses + + if in.Subnet != "" { + prefix, err := netip.ParsePrefix(in.Subnet) + if err != nil { + return field.Invalid(field.NewPath("spec", "subnet"), in.Subnet, err.Error()) + } + + prefixRange := netipx.RangeOfPrefix(prefix) + if in.First == "" { + in.First = prefixRange.From().Next().String() // omits the first address, the assumed network address + } + if in.Last == "" { + in.Last = prefixRange.To().Prev().String() // omits the last address, the assumed broadcast + } + if in.Prefix == 0 { + in.Prefix = prefix.Bits() + out.Prefix = prefix.Bits() + } + } + + if in.First != "" && in.Last != "" { + out.Addresses = append(out.Addresses, fmt.Sprintf("%s-%s", in.First, in.Last)) + } + + return nil +} diff --git a/api/v1alpha1/conversion_test.go b/api/v1alpha1/conversion_test.go new file mode 100644 index 0000000..a9d4405 --- /dev/null +++ b/api/v1alpha1/conversion_test.go @@ -0,0 +1,249 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/runtime" + v1alpha2 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" +) + +// IgnoreAnnotations will skip comparing annotations. +// This is useful as the old version data will be loaded into an annotation +// when upconverting to allow downconverting later. +var IgnoreAnnotations = IgnorePaths{"ObjectMeta.Annotations"} + +var _ = Describe("v1alpha1 to v1alpha2 conversions", func() { + DescribeTable("InClusterIPPoolSpec", + func(specIn *InClusterIPPoolSpec, expectedSpecOut *v1alpha2.InClusterIPPoolSpec) { + in := &InClusterIPPool{Spec: *specIn} + spokeBefore := in.DeepCopyObject().(conversion.Convertible) + + dst := &v1alpha2.InClusterIPPool{} + dstHub := dst.DeepCopyObject().(conversion.Hub) + Expect(spokeBefore.ConvertTo(dstHub)).To(Succeed()) + expectedOut := &v1alpha2.InClusterIPPool{Spec: *expectedSpecOut} + Expect(dstHub).To(EqualObject(expectedOut, IgnoreAnnotations)) + + gin := &GlobalInClusterIPPool{Spec: *specIn} + spokeBefore = gin.DeepCopyObject().(conversion.Convertible) + + gdst := &v1alpha2.GlobalInClusterIPPool{} + dstHub = gdst.DeepCopyObject().(conversion.Hub) + Expect(spokeBefore.ConvertTo(dstHub)).To(Succeed()) + gexpectedOut := &v1alpha2.GlobalInClusterIPPool{Spec: *expectedSpecOut} + Expect(dstHub).To(EqualObject(gexpectedOut, IgnoreAnnotations)) + }, + Entry("converts with First/Last/Subnet/Prefix", + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Subnet: "1.1.1.1/28", + First: "1.1.1.1", + Last: "1.1.1.10", + Prefix: 28, + }, + &v1alpha2.InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 28, + }, + ), + Entry("converts with only Address", + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 28, + }, + &v1alpha2.InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 28, + }, + ), + Entry("defaults Prefix from Subnet", + &InClusterIPPoolSpec{ + Subnet: "1.1.1.0/24", + First: "1.1.1.10", + Last: "1.1.1.15", + Gateway: "1.1.1.1", + }, + &v1alpha2.InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.10-1.1.1.15"}, + Prefix: 24, + }, + ), + Entry("defaults First and Last from Subnet", + &InClusterIPPoolSpec{ + Subnet: "1.1.1.0/24", + Gateway: "1.1.1.1", + }, + &v1alpha2.InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.1-1.1.1.254"}, + Prefix: 24, + }, + ), + ) +}) + +var _ = Describe("v1alpha2 to v1alpha1 conversions", func() { + DescribeTable("InClusterIPPool", + func(specIn *v1alpha2.InClusterIPPoolSpec, expectedSpecOut *InClusterIPPoolSpec) { + in := &v1alpha2.InClusterIPPool{Spec: *specIn} + hub := in.DeepCopyObject().(conversion.Hub) + + dst := &InClusterIPPool{} + spoke := dst.DeepCopyObject().(conversion.Convertible) + Expect(spoke.ConvertFrom(hub)).To(Succeed()) + expectedOut := &InClusterIPPool{Spec: *expectedSpecOut} + Expect(spoke).To(EqualObject(expectedOut, IgnoreAnnotations)) + + gin := &v1alpha2.GlobalInClusterIPPool{Spec: *specIn} + hub = gin.DeepCopyObject().(conversion.Hub) + + gdst := &GlobalInClusterIPPool{} + spoke = gdst.DeepCopyObject().(conversion.Convertible) + Expect(spoke.ConvertFrom(hub)).To(Succeed()) + gexpectedOut := &GlobalInClusterIPPool{Spec: *expectedSpecOut} + Expect(spoke).To(EqualObject(gexpectedOut, IgnoreAnnotations)) + }, + Entry("Addresses are unmodified", + &v1alpha2.InClusterIPPoolSpec{ + Gateway: "1.2.3.5", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 10, + }, + &InClusterIPPoolSpec{ + Gateway: "1.2.3.5", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 10, + }, + ), + ) +}) + +var _ = Describe("v1alpha1 -> v1alpha2 -> v1alpha1 conversions", func() { + DescribeTable("InClusterIPPool", + func(specIn *InClusterIPPoolSpec, expectedSpecOut *InClusterIPPoolSpec) { + in := &InClusterIPPool{Spec: *specIn} + expectedOut := &InClusterIPPool{Spec: *expectedSpecOut} + + spokeBefore := in.DeepCopyObject().(conversion.Convertible) + + dst := &v1alpha2.InClusterIPPool{} + dstHub := dst.DeepCopyObject().(conversion.Hub) + Expect(spokeBefore.ConvertTo(dstHub)).To(Succeed()) + + alpha1 := &InClusterIPPool{} + spokeAfter := alpha1.DeepCopyObject().(conversion.Convertible) + Expect(spokeAfter.ConvertFrom(dstHub)).To(Succeed()) + + Expect(spokeAfter).To(EqualObject(expectedOut, IgnoreAnnotations)) + + gin := &GlobalInClusterIPPool{Spec: *specIn} + spokeBefore = gin.DeepCopyObject().(conversion.Convertible) + + gdst := &v1alpha2.GlobalInClusterIPPool{} + dstHub = gdst.DeepCopyObject().(conversion.Hub) + Expect(spokeBefore.ConvertTo(dstHub)).To(Succeed()) + + galpha1 := &GlobalInClusterIPPool{} + spokeAfter = galpha1.DeepCopyObject().(conversion.Convertible) + Expect(spokeAfter.ConvertFrom(dstHub)).To(Succeed()) + + gexpectedOut := &GlobalInClusterIPPool{Spec: *expectedSpecOut} + Expect(spokeAfter).To(EqualObject(gexpectedOut, IgnoreAnnotations)) + }, + Entry("Fields are set back as expected when specs have Addresses, Gateway, Prefix", + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 16, + }, + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Addresses: []string{"1.1.1.1-1.1.1.10"}, + Prefix: 16, + }, + ), + Entry("Fields are set back as expected when specs have First, Last, Subnet", + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Subnet: "1.1.1.1/16", + First: "1.1.1.2", + Last: "1.1.1.10", + Prefix: 16, + }, + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Subnet: "1.1.1.1/16", + First: "1.1.1.2", + Last: "1.1.1.10", + Prefix: 16, + }, + ), + Entry("Fields are defaulted when applied as v1alpha1", + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Subnet: "1.1.1.1/24", + }, + &InClusterIPPoolSpec{ + Gateway: "1.1.1.1", + Subnet: "1.1.1.1/24", + First: "1.1.1.1", + Last: "1.1.1.254", + Prefix: 24, + }, + ), + ) +}) + +var _ = Describe("fuzzy conversion", func() { + Describe("InClusterIPPool", func() { + It("passes capi fuzzer tests", func() { + scheme := runtime.NewScheme() + Expect(AddToScheme(scheme)).To(Succeed()) + Expect(v1alpha2.AddToScheme(scheme)).To(Succeed()) + + utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ + Scheme: scheme, + Hub: &v1alpha2.InClusterIPPool{}, + Spoke: &InClusterIPPool{}, + }) + }) + }) + + Describe("GlobalInClusterIPPool", func() { + It("passes capi fuzzer tests", func() { + scheme := runtime.NewScheme() + Expect(AddToScheme(scheme)).To(Succeed()) + Expect(v1alpha2.AddToScheme(scheme)).To(Succeed()) + + utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ + Scheme: scheme, + Hub: &v1alpha2.GlobalInClusterIPPool{}, + Spoke: &GlobalInClusterIPPool{}, + }) + }) + }) +}) diff --git a/api/v1alpha1/doc.go b/api/v1alpha1/doc.go new file mode 100644 index 0000000..c1d889a --- /dev/null +++ b/api/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains API Schema definitions for the infrastructure v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=ipam.cluster.x-k8s.io +// +k8s:conversion-gen=sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2 +package v1alpha1 diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index b5c73a0..d8f897b 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -33,4 +33,7 @@ var ( // AddToScheme adds the types in this group-version to the given scheme. AddToScheme = SchemeBuilder.AddToScheme + + // localSchemeBuilder is used by the generated conversion code to manage the scheme. + localSchemeBuilder = &SchemeBuilder.SchemeBuilder ) diff --git a/api/v1alpha1/inclusterippool_conversion.go b/api/v1alpha1/inclusterippool_conversion.go new file mode 100644 index 0000000..b220577 --- /dev/null +++ b/api/v1alpha1/inclusterippool_conversion.go @@ -0,0 +1,131 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//nolint:forcetypeassert,golint,revive,stylecheck +package v1alpha1 + +import ( + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + v1alpha2 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" +) + +// ConvertTo converts v1alpha1.InClusterIPPool to v1alpha2.InClusterIPPool. +func (src *InClusterIPPool) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha2.InClusterIPPool) + if err := Convert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(src, dst, nil); err != nil { + return err + } + + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} + +// ConvertTo converts v1alpha2.InClusterIPPool to v1alpha1.InClusterIPPool. +func (dst *InClusterIPPool) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha2.InClusterIPPool) + if err := Convert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool(src, dst, nil); err != nil { + return err + } + + restored := &InClusterIPPool{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + restoredV2 := &v1alpha2.InClusterIPPool{} + if err := Convert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(restored, restoredV2, nil); err != nil { + return err + } + + addressesAreEqual := true + if len(restoredV2.Spec.Addresses) == len(src.Spec.Addresses) { + for i := range restoredV2.Spec.Addresses { + if restoredV2.Spec.Addresses[i] != src.Spec.Addresses[i] { + addressesAreEqual = false + break + } + } + } else { + addressesAreEqual = false + } + + if addressesAreEqual && + restoredV2.Spec.Prefix == src.Spec.Prefix && + restoredV2.Spec.Gateway == src.Spec.Gateway { + dst.Spec = restored.Spec + } + + return nil +} + +// ConvertTo converts v1alpha1.GlobalInClusterIPPool to v1alpha2.GlobalInClusterIPPool. +func (src *GlobalInClusterIPPool) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha2.GlobalInClusterIPPool) + if err := Convert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool(src, dst, nil); err != nil { + return err + } + + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} + +// ConvertTo converts v1alpha2.GlobalInClusterIPPool to v1alpha1.GlobalInClusterIPPool. +func (dst *GlobalInClusterIPPool) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha2.GlobalInClusterIPPool) + if err := Convert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool(src, dst, nil); err != nil { + return err + } + + restored := &GlobalInClusterIPPool{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + dst.Spec = restored.Spec + return nil +} + +// ConvertTo converts v1alpha1.InClusterIPPoolList to v1alpha2.InClusterIPPoolList. +func (src *InClusterIPPoolList) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha2.InClusterIPPoolList) + return Convert_v1alpha1_InClusterIPPoolList_To_v1alpha2_InClusterIPPoolList(src, dst, nil) +} + +// ConvertTo converts v1alpha2.InClusterIPPoolList to v1alpha1.InClusterIPPoolList. +func (dst *InClusterIPPoolList) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha2.InClusterIPPoolList) + return Convert_v1alpha2_InClusterIPPoolList_To_v1alpha1_InClusterIPPoolList(src, dst, nil) +} + +// ConvertTo converts v1alpha1.GlobalInClusterIPPoolList to v1alpha2.GlobalInClusterIPPoolList. +func (src *GlobalInClusterIPPoolList) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha2.GlobalInClusterIPPoolList) + return Convert_v1alpha1_GlobalInClusterIPPoolList_To_v1alpha2_GlobalInClusterIPPoolList(src, dst, nil) +} + +// ConvertTo converts v1alpha2.GlobalInClusterIPPoolList to v1alpha1.GlobalInClusterIPPoolList. +func (dst *GlobalInClusterIPPoolList) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha2.GlobalInClusterIPPoolList) + return Convert_v1alpha2_GlobalInClusterIPPoolList_To_v1alpha1_GlobalInClusterIPPoolList(src, dst, nil) +} diff --git a/api/v1alpha1/inclusterippool_types.go b/api/v1alpha1/inclusterippool_types.go index 15999f3..058eb56 100644 --- a/api/v1alpha1/inclusterippool_types.go +++ b/api/v1alpha1/inclusterippool_types.go @@ -84,6 +84,7 @@ type InClusterIPPoolStatusIPAddresses struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:deprecatedversion // +kubebuilder:printcolumn:name="Subnet",type="string",JSONPath=".spec.subnet",description="Subnet to allocate IPs from" // +kubebuilder:printcolumn:name="First",type="string",JSONPath=".spec.first",description="First address of the range to allocate from" // +kubebuilder:printcolumn:name="Last",type="string",JSONPath=".spec.last",description="Last address of the range to allocate from" @@ -112,6 +113,7 @@ type InClusterIPPoolList struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:deprecatedversion // +kubebuilder:resource:scope=Cluster // +kubebuilder:printcolumn:name="Subnet",type="string",JSONPath=".spec.subnet",description="Subnet to allocate IPs from" // +kubebuilder:printcolumn:name="First",type="string",JSONPath=".spec.first",description="First address of the range to allocate from" diff --git a/api/v1alpha1/v1alpha1_suite_test.go b/api/v1alpha1/v1alpha1_suite_test.go new file mode 100644 index 0000000..6746667 --- /dev/null +++ b/api/v1alpha1/v1alpha1_suite_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestV1alpha1(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "V1alpha1 Suite") +} diff --git a/api/v1alpha1/zz_generated.conversion.go b/api/v1alpha1/zz_generated.conversion.go new file mode 100644 index 0000000..fbca7fb --- /dev/null +++ b/api/v1alpha1/zz_generated.conversion.go @@ -0,0 +1,325 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + unsafe "unsafe" + + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + v1alpha2 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*GlobalInClusterIPPool)(nil), (*v1alpha2.GlobalInClusterIPPool)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool(a.(*GlobalInClusterIPPool), b.(*v1alpha2.GlobalInClusterIPPool), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.GlobalInClusterIPPool)(nil), (*GlobalInClusterIPPool)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool(a.(*v1alpha2.GlobalInClusterIPPool), b.(*GlobalInClusterIPPool), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*GlobalInClusterIPPoolList)(nil), (*v1alpha2.GlobalInClusterIPPoolList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_GlobalInClusterIPPoolList_To_v1alpha2_GlobalInClusterIPPoolList(a.(*GlobalInClusterIPPoolList), b.(*v1alpha2.GlobalInClusterIPPoolList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.GlobalInClusterIPPoolList)(nil), (*GlobalInClusterIPPoolList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_GlobalInClusterIPPoolList_To_v1alpha1_GlobalInClusterIPPoolList(a.(*v1alpha2.GlobalInClusterIPPoolList), b.(*GlobalInClusterIPPoolList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*InClusterIPPool)(nil), (*v1alpha2.InClusterIPPool)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(a.(*InClusterIPPool), b.(*v1alpha2.InClusterIPPool), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.InClusterIPPool)(nil), (*InClusterIPPool)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool(a.(*v1alpha2.InClusterIPPool), b.(*InClusterIPPool), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*InClusterIPPoolList)(nil), (*v1alpha2.InClusterIPPoolList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_InClusterIPPoolList_To_v1alpha2_InClusterIPPoolList(a.(*InClusterIPPoolList), b.(*v1alpha2.InClusterIPPoolList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.InClusterIPPoolList)(nil), (*InClusterIPPoolList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_InClusterIPPoolList_To_v1alpha1_InClusterIPPoolList(a.(*v1alpha2.InClusterIPPoolList), b.(*InClusterIPPoolList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.InClusterIPPoolSpec)(nil), (*InClusterIPPoolSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec(a.(*v1alpha2.InClusterIPPoolSpec), b.(*InClusterIPPoolSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*InClusterIPPoolStatus)(nil), (*v1alpha2.InClusterIPPoolStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus(a.(*InClusterIPPoolStatus), b.(*v1alpha2.InClusterIPPoolStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.InClusterIPPoolStatus)(nil), (*InClusterIPPoolStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus(a.(*v1alpha2.InClusterIPPoolStatus), b.(*InClusterIPPoolStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*InClusterIPPoolStatusIPAddresses)(nil), (*v1alpha2.InClusterIPPoolStatusIPAddresses)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_InClusterIPPoolStatusIPAddresses_To_v1alpha2_InClusterIPPoolStatusIPAddresses(a.(*InClusterIPPoolStatusIPAddresses), b.(*v1alpha2.InClusterIPPoolStatusIPAddresses), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.InClusterIPPoolStatusIPAddresses)(nil), (*InClusterIPPoolStatusIPAddresses)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_InClusterIPPoolStatusIPAddresses_To_v1alpha1_InClusterIPPoolStatusIPAddresses(a.(*v1alpha2.InClusterIPPoolStatusIPAddresses), b.(*InClusterIPPoolStatusIPAddresses), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*InClusterIPPoolSpec)(nil), (*v1alpha2.InClusterIPPoolSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_InClusterIPPoolSpec_To_v1alpha2_InClusterIPPoolSpec(a.(*InClusterIPPoolSpec), b.(*v1alpha2.InClusterIPPoolSpec), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool(in *GlobalInClusterIPPool, out *v1alpha2.GlobalInClusterIPPool, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_InClusterIPPoolSpec_To_v1alpha2_InClusterIPPoolSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool is an autogenerated conversion function. +func Convert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool(in *GlobalInClusterIPPool, out *v1alpha2.GlobalInClusterIPPool, s conversion.Scope) error { + return autoConvert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool(in, out, s) +} + +func autoConvert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool(in *v1alpha2.GlobalInClusterIPPool, out *GlobalInClusterIPPool, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool is an autogenerated conversion function. +func Convert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool(in *v1alpha2.GlobalInClusterIPPool, out *GlobalInClusterIPPool, s conversion.Scope) error { + return autoConvert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool(in, out, s) +} + +func autoConvert_v1alpha1_GlobalInClusterIPPoolList_To_v1alpha2_GlobalInClusterIPPoolList(in *GlobalInClusterIPPoolList, out *v1alpha2.GlobalInClusterIPPoolList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.GlobalInClusterIPPool, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_GlobalInClusterIPPool_To_v1alpha2_GlobalInClusterIPPool(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_GlobalInClusterIPPoolList_To_v1alpha2_GlobalInClusterIPPoolList is an autogenerated conversion function. +func Convert_v1alpha1_GlobalInClusterIPPoolList_To_v1alpha2_GlobalInClusterIPPoolList(in *GlobalInClusterIPPoolList, out *v1alpha2.GlobalInClusterIPPoolList, s conversion.Scope) error { + return autoConvert_v1alpha1_GlobalInClusterIPPoolList_To_v1alpha2_GlobalInClusterIPPoolList(in, out, s) +} + +func autoConvert_v1alpha2_GlobalInClusterIPPoolList_To_v1alpha1_GlobalInClusterIPPoolList(in *v1alpha2.GlobalInClusterIPPoolList, out *GlobalInClusterIPPoolList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GlobalInClusterIPPool, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_GlobalInClusterIPPool_To_v1alpha1_GlobalInClusterIPPool(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_GlobalInClusterIPPoolList_To_v1alpha1_GlobalInClusterIPPoolList is an autogenerated conversion function. +func Convert_v1alpha2_GlobalInClusterIPPoolList_To_v1alpha1_GlobalInClusterIPPoolList(in *v1alpha2.GlobalInClusterIPPoolList, out *GlobalInClusterIPPoolList, s conversion.Scope) error { + return autoConvert_v1alpha2_GlobalInClusterIPPoolList_To_v1alpha1_GlobalInClusterIPPoolList(in, out, s) +} + +func autoConvert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(in *InClusterIPPool, out *v1alpha2.InClusterIPPool, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_InClusterIPPoolSpec_To_v1alpha2_InClusterIPPoolSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool is an autogenerated conversion function. +func Convert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(in *InClusterIPPool, out *v1alpha2.InClusterIPPool, s conversion.Scope) error { + return autoConvert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(in, out, s) +} + +func autoConvert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool(in *v1alpha2.InClusterIPPool, out *InClusterIPPool, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool is an autogenerated conversion function. +func Convert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool(in *v1alpha2.InClusterIPPool, out *InClusterIPPool, s conversion.Scope) error { + return autoConvert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool(in, out, s) +} + +func autoConvert_v1alpha1_InClusterIPPoolList_To_v1alpha2_InClusterIPPoolList(in *InClusterIPPoolList, out *v1alpha2.InClusterIPPoolList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.InClusterIPPool, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_InClusterIPPool_To_v1alpha2_InClusterIPPool(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_InClusterIPPoolList_To_v1alpha2_InClusterIPPoolList is an autogenerated conversion function. +func Convert_v1alpha1_InClusterIPPoolList_To_v1alpha2_InClusterIPPoolList(in *InClusterIPPoolList, out *v1alpha2.InClusterIPPoolList, s conversion.Scope) error { + return autoConvert_v1alpha1_InClusterIPPoolList_To_v1alpha2_InClusterIPPoolList(in, out, s) +} + +func autoConvert_v1alpha2_InClusterIPPoolList_To_v1alpha1_InClusterIPPoolList(in *v1alpha2.InClusterIPPoolList, out *InClusterIPPoolList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InClusterIPPool, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_InClusterIPPool_To_v1alpha1_InClusterIPPool(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_InClusterIPPoolList_To_v1alpha1_InClusterIPPoolList is an autogenerated conversion function. +func Convert_v1alpha2_InClusterIPPoolList_To_v1alpha1_InClusterIPPoolList(in *v1alpha2.InClusterIPPoolList, out *InClusterIPPoolList, s conversion.Scope) error { + return autoConvert_v1alpha2_InClusterIPPoolList_To_v1alpha1_InClusterIPPoolList(in, out, s) +} + +func autoConvert_v1alpha1_InClusterIPPoolSpec_To_v1alpha2_InClusterIPPoolSpec(in *InClusterIPPoolSpec, out *v1alpha2.InClusterIPPoolSpec, s conversion.Scope) error { + out.Addresses = *(*[]string)(unsafe.Pointer(&in.Addresses)) + // WARNING: in.Subnet requires manual conversion: does not exist in peer-type + // WARNING: in.First requires manual conversion: does not exist in peer-type + // WARNING: in.Last requires manual conversion: does not exist in peer-type + out.Prefix = in.Prefix + out.Gateway = in.Gateway + return nil +} + +func autoConvert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec(in *v1alpha2.InClusterIPPoolSpec, out *InClusterIPPoolSpec, s conversion.Scope) error { + out.Addresses = *(*[]string)(unsafe.Pointer(&in.Addresses)) + out.Prefix = in.Prefix + out.Gateway = in.Gateway + return nil +} + +// Convert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec is an autogenerated conversion function. +func Convert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec(in *v1alpha2.InClusterIPPoolSpec, out *InClusterIPPoolSpec, s conversion.Scope) error { + return autoConvert_v1alpha2_InClusterIPPoolSpec_To_v1alpha1_InClusterIPPoolSpec(in, out, s) +} + +func autoConvert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus(in *InClusterIPPoolStatus, out *v1alpha2.InClusterIPPoolStatus, s conversion.Scope) error { + out.Addresses = (*v1alpha2.InClusterIPPoolStatusIPAddresses)(unsafe.Pointer(in.Addresses)) + return nil +} + +// Convert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus is an autogenerated conversion function. +func Convert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus(in *InClusterIPPoolStatus, out *v1alpha2.InClusterIPPoolStatus, s conversion.Scope) error { + return autoConvert_v1alpha1_InClusterIPPoolStatus_To_v1alpha2_InClusterIPPoolStatus(in, out, s) +} + +func autoConvert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus(in *v1alpha2.InClusterIPPoolStatus, out *InClusterIPPoolStatus, s conversion.Scope) error { + out.Addresses = (*InClusterIPPoolStatusIPAddresses)(unsafe.Pointer(in.Addresses)) + return nil +} + +// Convert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus is an autogenerated conversion function. +func Convert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus(in *v1alpha2.InClusterIPPoolStatus, out *InClusterIPPoolStatus, s conversion.Scope) error { + return autoConvert_v1alpha2_InClusterIPPoolStatus_To_v1alpha1_InClusterIPPoolStatus(in, out, s) +} + +func autoConvert_v1alpha1_InClusterIPPoolStatusIPAddresses_To_v1alpha2_InClusterIPPoolStatusIPAddresses(in *InClusterIPPoolStatusIPAddresses, out *v1alpha2.InClusterIPPoolStatusIPAddresses, s conversion.Scope) error { + out.Total = in.Total + out.Free = in.Free + out.Used = in.Used + out.OutOfRange = in.OutOfRange + return nil +} + +// Convert_v1alpha1_InClusterIPPoolStatusIPAddresses_To_v1alpha2_InClusterIPPoolStatusIPAddresses is an autogenerated conversion function. +func Convert_v1alpha1_InClusterIPPoolStatusIPAddresses_To_v1alpha2_InClusterIPPoolStatusIPAddresses(in *InClusterIPPoolStatusIPAddresses, out *v1alpha2.InClusterIPPoolStatusIPAddresses, s conversion.Scope) error { + return autoConvert_v1alpha1_InClusterIPPoolStatusIPAddresses_To_v1alpha2_InClusterIPPoolStatusIPAddresses(in, out, s) +} + +func autoConvert_v1alpha2_InClusterIPPoolStatusIPAddresses_To_v1alpha1_InClusterIPPoolStatusIPAddresses(in *v1alpha2.InClusterIPPoolStatusIPAddresses, out *InClusterIPPoolStatusIPAddresses, s conversion.Scope) error { + out.Total = in.Total + out.Free = in.Free + out.Used = in.Used + out.OutOfRange = in.OutOfRange + return nil +} + +// Convert_v1alpha2_InClusterIPPoolStatusIPAddresses_To_v1alpha1_InClusterIPPoolStatusIPAddresses is an autogenerated conversion function. +func Convert_v1alpha2_InClusterIPPoolStatusIPAddresses_To_v1alpha1_InClusterIPPoolStatusIPAddresses(in *v1alpha2.InClusterIPPoolStatusIPAddresses, out *InClusterIPPoolStatusIPAddresses, s conversion.Scope) error { + return autoConvert_v1alpha2_InClusterIPPoolStatusIPAddresses_To_v1alpha1_InClusterIPPoolStatusIPAddresses(in, out, s) +} diff --git a/api/v1alpha2/doc.go b/api/v1alpha2/doc.go new file mode 100644 index 0000000..a3e39c7 --- /dev/null +++ b/api/v1alpha2/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha2 contains API Schema definitions for the infrastructure v1alpha2 API group +// +kubebuilder:object:generate=true +// +groupName=ipam.cluster.x-k8s.io +package v1alpha2 diff --git a/api/v1alpha2/groupversion_info.go b/api/v1alpha2/groupversion_info.go new file mode 100644 index 0000000..1ec51b4 --- /dev/null +++ b/api/v1alpha2/groupversion_info.go @@ -0,0 +1,33 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "ipam.cluster.x-k8s.io", Version: "v1alpha2"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1alpha2/inclusterippool_conversion.go b/api/v1alpha2/inclusterippool_conversion.go new file mode 100644 index 0000000..ab82af4 --- /dev/null +++ b/api/v1alpha2/inclusterippool_conversion.go @@ -0,0 +1,29 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +// Hub marks InClusterIPPool as a conversion hub. +func (*InClusterIPPool) Hub() {} + +// Hub marks GlobalInClusterIPPool as a conversion hub. +func (*GlobalInClusterIPPool) Hub() {} + +// Hub marks InClusterIPPoolList as a conversion hub. +func (*InClusterIPPoolList) Hub() {} + +// Hub marks GlobalInClusterIPPoolList as a conversion hub. +func (*GlobalInClusterIPPoolList) Hub() {} diff --git a/api/v1alpha2/inclusterippool_types.go b/api/v1alpha2/inclusterippool_types.go new file mode 100644 index 0000000..09853ca --- /dev/null +++ b/api/v1alpha2/inclusterippool_types.go @@ -0,0 +1,147 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// InClusterIPPoolSpec defines the desired state of InClusterIPPool. +type InClusterIPPoolSpec struct { + // Addresses is a list of IP addresses that can be assigned. This set of + // addresses can be non-contiguous. + Addresses []string `json:"addresses"` + + // Prefix is the network prefix to use. + // +kubebuilder:validation:Maximum=128 + Prefix int `json:"prefix"` + + // Gateway + // +optional + Gateway string `json:"gateway,omitempty"` +} + +// InClusterIPPoolStatus defines the observed state of InClusterIPPool. +type InClusterIPPoolStatus struct { + // Addresses reports the count of total, free, and used IPs in the pool. + // +optional + Addresses *InClusterIPPoolStatusIPAddresses `json:"ipAddresses,omitempty"` +} + +// InClusterIPPoolStatusIPAddresses contains the count of total, free, and used IPs in a pool. +type InClusterIPPoolStatusIPAddresses struct { + // Total is the total number of IPs configured for the pool. + // Counts greater than int can contain will report as math.MaxInt. + Total int `json:"total"` + + // Free is the count of unallocated IPs in the pool. + // Counts greater than int can contain will report as math.MaxInt. + Free int `json:"free"` + + // Used is the count of allocated IPs in the pool. + // Counts greater than int can contain will report as math.MaxInt. + Used int `json:"used"` + + // Out of Range is the count of allocated IPs in the pool that is not + // contained within spec.Addresses. + // Counts greater than int can contain will report as math.MaxInt. + OutOfRange int `json:"outOfRange"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Addresses",type="string",JSONPath=".spec.addresses",description="List of addresses, to allocate from" +// +kubebuilder:printcolumn:name="Total",type="integer",JSONPath=".status.ipAddresses.total",description="Count of IPs configured for the pool" +// +kubebuilder:printcolumn:name="Free",type="integer",JSONPath=".status.ipAddresses.free",description="Count of unallocated IPs in the pool" +// +kubebuilder:printcolumn:name="Used",type="integer",JSONPath=".status.ipAddresses.used",description="Count of allocated IPs in the pool" + +// InClusterIPPool is the Schema for the inclusterippools API. +type InClusterIPPool struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InClusterIPPoolSpec `json:"spec,omitempty"` + Status InClusterIPPoolStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// InClusterIPPoolList contains a list of InClusterIPPool. +type InClusterIPPoolList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []InClusterIPPool `json:"items"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="Addresses",type="string",JSONPath=".spec.addresses",description="List of addresses, to allocate from" +// +kubebuilder:printcolumn:name="Total",type="integer",JSONPath=".status.ipAddresses.total",description="Count of IPs configured for the pool" +// +kubebuilder:printcolumn:name="Free",type="integer",JSONPath=".status.ipAddresses.free",description="Count of unallocated IPs in the pool" +// +kubebuilder:printcolumn:name="Used",type="integer",JSONPath=".status.ipAddresses.used",description="Count of allocated IPs in the pool" + +// GlobalInClusterIPPool is the Schema for the global inclusterippools API. +// This pool type is cluster scoped. IPAddressClaims can reference +// pools of this type from any any namespace. +type GlobalInClusterIPPool struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InClusterIPPoolSpec `json:"spec,omitempty"` + Status InClusterIPPoolStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// GlobalInClusterIPPoolList contains a list of GlobalInClusterIPPool. +type GlobalInClusterIPPoolList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []GlobalInClusterIPPool `json:"items"` +} + +func init() { + SchemeBuilder.Register( + &InClusterIPPool{}, + &InClusterIPPoolList{}, + &GlobalInClusterIPPool{}, + &GlobalInClusterIPPoolList{}, + ) +} + +// PoolSpec implements the genericInClusterPool interface. +func (p *InClusterIPPool) PoolSpec() *InClusterIPPoolSpec { + return &p.Spec +} + +// PoolStatus implements the genericInClusterPool interface. +func (p *InClusterIPPool) PoolStatus() *InClusterIPPoolStatus { + return &p.Status +} + +// PoolSpec implements the genericInClusterPool interface. +func (p *GlobalInClusterIPPool) PoolSpec() *InClusterIPPoolSpec { + return &p.Spec +} + +// PoolStatus implements the genericInClusterPool interface. +func (p *GlobalInClusterIPPool) PoolStatus() *InClusterIPPoolStatus { + return &p.Status +} diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 0000000..dc60336 --- /dev/null +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,199 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalInClusterIPPool) DeepCopyInto(out *GlobalInClusterIPPool) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalInClusterIPPool. +func (in *GlobalInClusterIPPool) DeepCopy() *GlobalInClusterIPPool { + if in == nil { + return nil + } + out := new(GlobalInClusterIPPool) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalInClusterIPPool) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalInClusterIPPoolList) DeepCopyInto(out *GlobalInClusterIPPoolList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GlobalInClusterIPPool, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalInClusterIPPoolList. +func (in *GlobalInClusterIPPoolList) DeepCopy() *GlobalInClusterIPPoolList { + if in == nil { + return nil + } + out := new(GlobalInClusterIPPoolList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalInClusterIPPoolList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InClusterIPPool) DeepCopyInto(out *InClusterIPPool) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InClusterIPPool. +func (in *InClusterIPPool) DeepCopy() *InClusterIPPool { + if in == nil { + return nil + } + out := new(InClusterIPPool) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InClusterIPPool) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InClusterIPPoolList) DeepCopyInto(out *InClusterIPPoolList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InClusterIPPool, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InClusterIPPoolList. +func (in *InClusterIPPoolList) DeepCopy() *InClusterIPPoolList { + if in == nil { + return nil + } + out := new(InClusterIPPoolList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InClusterIPPoolList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InClusterIPPoolSpec) DeepCopyInto(out *InClusterIPPoolSpec) { + *out = *in + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InClusterIPPoolSpec. +func (in *InClusterIPPoolSpec) DeepCopy() *InClusterIPPoolSpec { + if in == nil { + return nil + } + out := new(InClusterIPPoolSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InClusterIPPoolStatus) DeepCopyInto(out *InClusterIPPoolStatus) { + *out = *in + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = new(InClusterIPPoolStatusIPAddresses) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InClusterIPPoolStatus. +func (in *InClusterIPPoolStatus) DeepCopy() *InClusterIPPoolStatus { + if in == nil { + return nil + } + out := new(InClusterIPPoolStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InClusterIPPoolStatusIPAddresses) DeepCopyInto(out *InClusterIPPoolStatusIPAddresses) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InClusterIPPoolStatusIPAddresses. +func (in *InClusterIPPoolStatusIPAddresses) DeepCopy() *InClusterIPPoolStatusIPAddresses { + if in == nil { + return nil + } + out := new(InClusterIPPoolStatusIPAddresses) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/ipam.cluster.x-k8s.io_globalinclusterippools.yaml b/config/crd/bases/ipam.cluster.x-k8s.io_globalinclusterippools.yaml index 51170a6..6ca15ee 100644 --- a/config/crd/bases/ipam.cluster.x-k8s.io_globalinclusterippools.yaml +++ b/config/crd/bases/ipam.cluster.x-k8s.io_globalinclusterippools.yaml @@ -41,6 +41,7 @@ spec: jsonPath: .status.ipAddresses.used name: Used type: integer + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -125,6 +126,98 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: List of addresses, to allocate from + jsonPath: .spec.addresses + name: Addresses + type: string + - description: Count of IPs configured for the pool + jsonPath: .status.ipAddresses.total + name: Total + type: integer + - description: Count of unallocated IPs in the pool + jsonPath: .status.ipAddresses.free + name: Free + type: integer + - description: Count of allocated IPs in the pool + jsonPath: .status.ipAddresses.used + name: Used + type: integer + name: v1alpha2 + schema: + openAPIV3Schema: + description: GlobalInClusterIPPool is the Schema for the global inclusterippools + API. This pool type is cluster scoped. IPAddressClaims can reference pools + of this type from any any namespace. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: InClusterIPPoolSpec defines the desired state of InClusterIPPool. + properties: + addresses: + description: Addresses is a list of IP addresses that can be assigned. + This set of addresses can be non-contiguous. + items: + type: string + type: array + gateway: + description: Gateway + type: string + prefix: + description: Prefix is the network prefix to use. + maximum: 128 + type: integer + required: + - addresses + - prefix + type: object + status: + description: InClusterIPPoolStatus defines the observed state of InClusterIPPool. + properties: + ipAddresses: + description: Addresses reports the count of total, free, and used + IPs in the pool. + properties: + free: + description: Free is the count of unallocated IPs in the pool. + Counts greater than int can contain will report as math.MaxInt. + type: integer + outOfRange: + description: Out of Range is the count of allocated IPs in the + pool that is not contained within spec.Addresses. Counts greater + than int can contain will report as math.MaxInt. + type: integer + total: + description: Total is the total number of IPs configured for the + pool. Counts greater than int can contain will report as math.MaxInt. + type: integer + used: + description: Used is the count of allocated IPs in the pool. Counts + greater than int can contain will report as math.MaxInt. + type: integer + required: + - free + - outOfRange + - total + - used + type: object + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/ipam.cluster.x-k8s.io_inclusterippools.yaml b/config/crd/bases/ipam.cluster.x-k8s.io_inclusterippools.yaml index 0ac8963..82bdb5f 100644 --- a/config/crd/bases/ipam.cluster.x-k8s.io_inclusterippools.yaml +++ b/config/crd/bases/ipam.cluster.x-k8s.io_inclusterippools.yaml @@ -45,6 +45,7 @@ spec: jsonPath: .status.ipAddresses.used name: Used type: integer + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -127,6 +128,96 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: List of addresses, to allocate from + jsonPath: .spec.addresses + name: Addresses + type: string + - description: Count of IPs configured for the pool + jsonPath: .status.ipAddresses.total + name: Total + type: integer + - description: Count of unallocated IPs in the pool + jsonPath: .status.ipAddresses.free + name: Free + type: integer + - description: Count of allocated IPs in the pool + jsonPath: .status.ipAddresses.used + name: Used + type: integer + name: v1alpha2 + schema: + openAPIV3Schema: + description: InClusterIPPool is the Schema for the inclusterippools API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: InClusterIPPoolSpec defines the desired state of InClusterIPPool. + properties: + addresses: + description: Addresses is a list of IP addresses that can be assigned. + This set of addresses can be non-contiguous. + items: + type: string + type: array + gateway: + description: Gateway + type: string + prefix: + description: Prefix is the network prefix to use. + maximum: 128 + type: integer + required: + - addresses + - prefix + type: object + status: + description: InClusterIPPoolStatus defines the observed state of InClusterIPPool. + properties: + ipAddresses: + description: Addresses reports the count of total, free, and used + IPs in the pool. + properties: + free: + description: Free is the count of unallocated IPs in the pool. + Counts greater than int can contain will report as math.MaxInt. + type: integer + outOfRange: + description: Out of Range is the count of allocated IPs in the + pool that is not contained within spec.Addresses. Counts greater + than int can contain will report as math.MaxInt. + type: integer + total: + description: Total is the total number of IPs configured for the + pool. Counts greater than int can contain will report as math.MaxInt. + type: integer + used: + description: Used is the count of allocated IPs in the pool. Counts + greater than int can contain will report as math.MaxInt. + type: integer + required: + - free + - outOfRange + - total + - used + type: object + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index c0aee64..dec6ec5 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -1,20 +1,25 @@ # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - bases/ipam.cluster.x-k8s.io_inclusterippools.yaml - bases/ipam.cluster.x-k8s.io_globalinclusterippools.yaml #+kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: +patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_inclusterippools.yaml +- path: patches/webhook_in_inclusterippools.yaml +- path: patches/webhook_in_globalinclusterippools.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_inclusterippools.yaml +#- patches/cainjection_in_globalinclusterippools.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_globalinclusterippools.yaml b/config/crd/patches/cainjection_in_globalinclusterippools.yaml new file mode 100644 index 0000000..7bd2091 --- /dev/null +++ b/config/crd/patches/cainjection_in_globalinclusterippools.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: globalinclusterippools.ipam.cluster.x-k8s.io diff --git a/config/crd/patches/cainjection_in_inclusterippools.yaml b/config/crd/patches/cainjection_in_inclusterippools.yaml index c12833c..1436d89 100644 --- a/config/crd/patches/cainjection_in_inclusterippools.yaml +++ b/config/crd/patches/cainjection_in_inclusterippools.yaml @@ -4,4 +4,4 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: inclusterippools.ipam.cluster.x-k8s.io.ipam.cluster.x-k8s.io + name: inclusterippools.ipam.cluster.x-k8s.io diff --git a/config/crd/patches/webhook_in_globalinclusterippools.yaml b/config/crd/patches/webhook_in_globalinclusterippools.yaml new file mode 100644 index 0000000..92e9a86 --- /dev/null +++ b/config/crd/patches/webhook_in_globalinclusterippools.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: globalinclusterippools.ipam.cluster.x-k8s.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/crd/patches/webhook_in_inclusterippools.yaml b/config/crd/patches/webhook_in_inclusterippools.yaml index 860746b..ada0a0b 100644 --- a/config/crd/patches/webhook_in_inclusterippools.yaml +++ b/config/crd/patches/webhook_in_inclusterippools.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: inclusterippools.ipam.cluster.x-k8s.io.ipam.cluster.x-k8s.io + name: inclusterippools.ipam.cluster.x-k8s.io spec: conversion: strategy: Webhook diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index aa9a5cb..55a97c7 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -12,4 +12,18 @@ kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) \ No newline at end of file + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: inclusterippools.ipam.cluster.x-k8s.io +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: globalinclusterippools.ipam.cluster.x-k8s.io diff --git a/config/samples/inclusterippool.yaml b/config/samples/inclusterippool.yaml deleted file mode 100644 index 32c2c6c..0000000 --- a/config/samples/inclusterippool.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: ipam.cluster.x-k8s.io/v1alpha1 -kind: InClusterIPPool -metadata: - name: inclusterippool-sample -spec: - subnet: 10.0.0.0/24 - gateway: 10.0.0.1 diff --git a/config/samples/ipam_v1alpha2_globalinclusterippool.yaml b/config/samples/ipam_v1alpha2_globalinclusterippool.yaml new file mode 100644 index 0000000..1d2efec --- /dev/null +++ b/config/samples/ipam_v1alpha2_globalinclusterippool.yaml @@ -0,0 +1,15 @@ +apiVersion: ipam.cluster.x-k8s.io/v1alpha2 +kind: GlobalInClusterIPPool +metadata: + labels: + app.kubernetes.io/name: globalinclusterippool + app.kubernetes.io/instance: globalinclusterippool-sample + app.kubernetes.io/part-of: cluster-api-ipam-provider-in-cluster + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: cluster-api-ipam-provider-in-cluster + name: globalinclusterippool-sample +spec: + addresses: + - 10.0.0.2-10.0.0.254 + prefix: 24 + gateway: 10.0.0.1 diff --git a/config/samples/ipam_v1alpha2_inclusterippool.yaml b/config/samples/ipam_v1alpha2_inclusterippool.yaml new file mode 100644 index 0000000..f44ea8a --- /dev/null +++ b/config/samples/ipam_v1alpha2_inclusterippool.yaml @@ -0,0 +1,15 @@ +apiVersion: ipam.cluster.x-k8s.io/v1alpha2 +kind: InClusterIPPool +metadata: + labels: + app.kubernetes.io/name: inclusterippool + app.kubernetes.io/instance: inclusterippool-sample + app.kubernetes.io/part-of: cluster-api-ipam-provider-in-cluster + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: cluster-api-ipam-provider-in-cluster + name: inclusterippool-sample +spec: + addresses: + - 10.0.0.2-10.0.0.254 + prefix: 24 + gateway: 10.0.0.1 diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index efa10b4..bc7274b 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -13,7 +13,7 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-ipam-cluster-x-k8s-io-v1alpha1-inclusterippool + path: /mutate-ipam-cluster-x-k8s-io-v1alpha2-inclusterippool failurePolicy: Fail matchPolicy: Equivalent name: default.inclusterippool.ipam.cluster.x-k8s.io @@ -21,7 +21,7 @@ webhooks: - apiGroups: - ipam.cluster.x-k8s.io apiVersions: - - v1alpha1 + - v1alpha2 operations: - CREATE - UPDATE @@ -35,7 +35,7 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-ipam-cluster-x-k8s-io-v1alpha1-globalinclusterippool + path: /mutate-ipam-cluster-x-k8s-io-v1alpha2-globalinclusterippool failurePolicy: Fail matchPolicy: Equivalent name: default.globalinclusterippool.ipam.cluster.x-k8s.io @@ -43,7 +43,7 @@ webhooks: - apiGroups: - ipam.cluster.x-k8s.io apiVersions: - - v1alpha1 + - v1alpha2 operations: - CREATE - UPDATE @@ -65,7 +65,7 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-ipam-cluster-x-k8s-io-v1alpha1-inclusterippool + path: /validate-ipam-cluster-x-k8s-io-v1alpha2-inclusterippool failurePolicy: Fail matchPolicy: Equivalent name: validation.inclusterippool.ipam.cluster.x-k8s.io @@ -73,7 +73,7 @@ webhooks: - apiGroups: - ipam.cluster.x-k8s.io apiVersions: - - v1alpha1 + - v1alpha2 operations: - CREATE - UPDATE @@ -88,7 +88,7 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-ipam-cluster-x-k8s-io-v1alpha1-globalinclusterippool + path: /validate-ipam-cluster-x-k8s-io-v1alpha2-globalinclusterippool failurePolicy: Fail matchPolicy: Equivalent name: validation.globalinclusterippool.ipam.cluster.x-k8s.io @@ -96,7 +96,7 @@ webhooks: - apiGroups: - ipam.cluster.x-k8s.io apiVersions: - - v1alpha1 + - v1alpha2 operations: - CREATE - UPDATE diff --git a/internal/controllers/inclusterippool.go b/internal/controllers/inclusterippool.go index 5de7417..8f891e2 100644 --- a/internal/controllers/inclusterippool.go +++ b/internal/controllers/inclusterippool.go @@ -36,7 +36,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/poolutil" pooltypes "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/pkg/types" ) @@ -58,7 +58,7 @@ type InClusterIPPoolReconciler struct { // SetupWithManager sets up the controller with the Manager. func (r *InClusterIPPoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1.InClusterIPPool{}). + For(&v1alpha2.InClusterIPPool{}). Watches(&source.Kind{Type: &ipamv1.IPAddress{}}, handler.EnqueueRequestsFromMapFunc(r.ipAddressToInClusterIPPool)). Complete(r) @@ -71,7 +71,7 @@ func (r *InClusterIPPoolReconciler) ipAddressToInClusterIPPool(clientObj client. } if ipAddress.Spec.PoolRef.APIGroup != nil && - *ipAddress.Spec.PoolRef.APIGroup == v1alpha1.GroupVersion.Group && + *ipAddress.Spec.PoolRef.APIGroup == v1alpha2.GroupVersion.Group && ipAddress.Spec.PoolRef.Kind == inClusterIPPoolKind { return []reconcile.Request{{ NamespacedName: types.NamespacedName{ @@ -93,7 +93,7 @@ type GlobalInClusterIPPoolReconciler struct { // SetupWithManager sets up the controller with the Manager. func (r *GlobalInClusterIPPoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1.GlobalInClusterIPPool{}). + For(&v1alpha2.GlobalInClusterIPPool{}). Watches(&source.Kind{Type: &ipamv1.IPAddress{}}, handler.EnqueueRequestsFromMapFunc(r.ipAddressToGlobalInClusterIPPool)). Complete(r) @@ -106,7 +106,7 @@ func (r *GlobalInClusterIPPoolReconciler) ipAddressToGlobalInClusterIPPool(clien } if ipAddress.Spec.PoolRef.APIGroup != nil && - *ipAddress.Spec.PoolRef.APIGroup == v1alpha1.GroupVersion.Group && + *ipAddress.Spec.PoolRef.APIGroup == v1alpha2.GroupVersion.Group && ipAddress.Spec.PoolRef.Kind == globalInClusterIPPoolKind { return []reconcile.Request{{ NamespacedName: types.NamespacedName{ @@ -129,7 +129,7 @@ func (r *InClusterIPPoolReconciler) Reconcile(ctx context.Context, req ctrl.Requ log := ctrl.LoggerFrom(ctx) log.Info("Reconciling pool") - pool := &v1alpha1.InClusterIPPool{} + pool := &v1alpha2.InClusterIPPool{} if err := r.Client.Get(ctx, req.NamespacedName, pool); err != nil { if !apierrors.IsNotFound(err) { return ctrl.Result{}, errors.Wrap(err, "failed to fetch InClusterIPPool") @@ -149,7 +149,7 @@ func (r *GlobalInClusterIPPoolReconciler) Reconcile(ctx context.Context, req ctr log := ctrl.LoggerFrom(ctx) log.Info("Reconciling pool") - pool := &v1alpha1.GlobalInClusterIPPool{} + pool := &v1alpha2.GlobalInClusterIPPool{} if err := r.Client.Get(ctx, req.NamespacedName, pool); err != nil { if !apierrors.IsNotFound(err) { return ctrl.Result{}, errors.Wrap(err, "failed to fetch GlobalInClusterIPPool") @@ -174,7 +174,7 @@ func genericReconcile(ctx context.Context, c client.Client, pool pooltypes.Gener }() poolTypeRef := corev1.TypedLocalObjectReference{ - APIGroup: pointer.String(v1alpha1.GroupVersion.Group), + APIGroup: pointer.String(v1alpha2.GroupVersion.Group), Kind: pool.GetObjectKind().GroupVersionKind().Kind, Name: pool.GetName(), } @@ -197,7 +197,7 @@ func genericReconcile(ctx context.Context, c client.Client, pool pooltypes.Gener return ctrl.Result{}, nil } - poolIPSet, err := poolutil.IPPoolSpecToIPSet(pool.PoolSpec()) + poolIPSet, err := poolutil.AddressesToIPSet(pool.PoolSpec().Addresses) if err != nil { return ctrl.Result{}, errors.Wrap(err, "failed to build ip set from pool spec") } @@ -220,7 +220,7 @@ func genericReconcile(ctx context.Context, c client.Client, pool pooltypes.Gener return ctrl.Result{}, errors.Wrap(err, "failed to build out of range ip set") } - pool.PoolStatus().Addresses = &v1alpha1.InClusterIPPoolStatusIPAddresses{ + pool.PoolStatus().Addresses = &v1alpha2.InClusterIPPoolStatusIPAddresses{ Total: poolCount, Used: inUseCount, Free: free, diff --git a/internal/controllers/inclusterippool_test.go b/internal/controllers/inclusterippool_test.go index 776c205..bcdb817 100644 --- a/internal/controllers/inclusterippool_test.go +++ b/internal/controllers/inclusterippool_test.go @@ -29,7 +29,7 @@ import ( ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1alpha1" . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" pooltypes "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/pkg/types" ) @@ -118,7 +118,7 @@ var _ = Describe("IP Pool Reconciler", func() { DescribeTable("it shows the out of range ips if any", func(poolType string, addresses []string, gateway string, updatedAddresses []string, numClaims, expectedOutOfRange int) { - poolSpec := v1alpha1.InClusterIPPoolSpec{ + poolSpec := v1alpha2.InClusterIPPoolSpec{ Prefix: 24, Gateway: gateway, Addresses: addresses, @@ -126,12 +126,12 @@ var _ = Describe("IP Pool Reconciler", func() { switch poolType { case "InClusterIPPool": - genericPool = &v1alpha1.InClusterIPPool{ + genericPool = &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{GenerateName: testPool, Namespace: namespace}, Spec: poolSpec, } case "GlobalInClusterIPPool": - genericPool = &v1alpha1.GlobalInClusterIPPool{ + genericPool = &v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{GenerateName: testPool, Namespace: namespace}, Spec: poolSpec, } @@ -228,7 +228,7 @@ var _ = Describe("IP Pool Reconciler", func() { }) func newPool(poolType, generateName, namespace, gateway string, addresses []string, prefix int) pooltypes.GenericInClusterPool { - poolSpec := v1alpha1.InClusterIPPoolSpec{ + poolSpec := v1alpha2.InClusterIPPoolSpec{ Prefix: prefix, Gateway: gateway, Addresses: addresses, @@ -236,12 +236,12 @@ func newPool(poolType, generateName, namespace, gateway string, addresses []stri switch poolType { case "InClusterIPPool": - return &v1alpha1.InClusterIPPool{ + return &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{GenerateName: generateName, Namespace: namespace}, Spec: poolSpec, } case "GlobalInClusterIPPool": - return &v1alpha1.GlobalInClusterIPPool{ + return &v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{GenerateName: generateName}, Spec: poolSpec, } diff --git a/internal/controllers/ipaddressclaim.go b/internal/controllers/ipaddressclaim.go index 455f150..4d4f9df 100644 --- a/internal/controllers/ipaddressclaim.go +++ b/internal/controllers/ipaddressclaim.go @@ -44,7 +44,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/index" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/poolutil" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/pkg/ipamutil" @@ -74,11 +74,11 @@ func (r *IPAddressClaimReconciler) SetupWithManager(ctx context.Context, mgr ctr For(&ipamv1.IPAddressClaim{}, builder.WithPredicates( predicate.Or( ipampredicates.ClaimReferencesPoolKind(metav1.GroupKind{ - Group: v1alpha1.GroupVersion.Group, + Group: v1alpha2.GroupVersion.Group, Kind: inClusterIPPoolKind, }), ipampredicates.ClaimReferencesPoolKind(metav1.GroupKind{ - Group: v1alpha1.GroupVersion.Group, + Group: v1alpha2.GroupVersion.Group, Kind: globalInClusterIPPoolKind, }), ), @@ -105,18 +105,18 @@ func (r *IPAddressClaimReconciler) SetupWithManager(ctx context.Context, mgr ctr MaxConcurrentReconciles: 1, }). Watches( - &source.Kind{Type: &v1alpha1.InClusterIPPool{}}, + &source.Kind{Type: &v1alpha2.InClusterIPPool{}}, handler.EnqueueRequestsFromMapFunc(r.inClusterIPPoolToIPClaims("InClusterIPPool")), builder.WithPredicates(resourceTransitionedToUnpaused()), ). Watches( - &source.Kind{Type: &v1alpha1.GlobalInClusterIPPool{}}, + &source.Kind{Type: &v1alpha2.GlobalInClusterIPPool{}}, handler.EnqueueRequestsFromMapFunc(r.inClusterIPPoolToIPClaims("GlobalInClusterIPPool")), builder.WithPredicates(resourceTransitionedToUnpaused()), ). Owns(&ipamv1.IPAddress{}, builder.WithPredicates( ipampredicates.AddressReferencesPoolKind(metav1.GroupKind{ - Group: v1alpha1.GroupVersion.Group, + Group: v1alpha2.GroupVersion.Group, Kind: inClusterIPPoolKind, }), )). @@ -134,7 +134,7 @@ func (r *IPAddressClaimReconciler) inClusterIPPoolToIPClaims(kind string) func(c "index.poolRef": index.IPPoolRefValue(corev1.TypedLocalObjectReference{ Name: pool.GetName(), Kind: kind, - APIGroup: &v1alpha1.GroupVersion.Group, + APIGroup: &v1alpha2.GroupVersion.Group, }), }, client.InNamespace(pool.GetNamespace()), @@ -213,13 +213,13 @@ func (r *IPAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque var pool pooltypes.GenericInClusterPool if claim.Spec.PoolRef.Kind == inClusterIPPoolKind { - icippool := &v1alpha1.InClusterIPPool{} + icippool := &v1alpha2.InClusterIPPool{} if err := r.Client.Get(ctx, types.NamespacedName{Namespace: claim.Namespace, Name: claim.Spec.PoolRef.Name}, icippool); err != nil && !apierrors.IsNotFound(err) { return ctrl.Result{}, errors.Wrap(err, "failed to fetch pool") } pool = icippool } else if claim.Spec.PoolRef.Kind == globalInClusterIPPoolKind { - gicippool := &v1alpha1.GlobalInClusterIPPool{} + gicippool := &v1alpha2.GlobalInClusterIPPool{} if err := r.Client.Get(ctx, types.NamespacedName{Name: claim.Spec.PoolRef.Name}, gicippool); err != nil && !apierrors.IsNotFound(err) { return ctrl.Result{}, errors.Wrap(err, "failed to fetch pool") } @@ -325,7 +325,7 @@ func (r *IPAddressClaimReconciler) allocateAddress(claim *ipamv1.IPAddressClaim, return nil, fmt.Errorf("failed to convert IPAddressList to IPSet: %w", err) } - poolIPSet, err := poolutil.IPPoolSpecToIPSet(pool.PoolSpec()) + poolIPSet, err := poolutil.AddressesToIPSet(pool.PoolSpec().Addresses) if err != nil { return nil, fmt.Errorf("failed to convert pool to range: %w", err) } diff --git a/internal/controllers/ipaddressclaim_test.go b/internal/controllers/ipaddressclaim_test.go index c442258..7279552 100644 --- a/internal/controllers/ipaddressclaim_test.go +++ b/internal/controllers/ipaddressclaim_test.go @@ -30,7 +30,7 @@ import ( "sigs.k8s.io/cluster-api/util/patch" . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" ) var IgnoreUIDsOnIPAddress = IgnorePaths{ @@ -57,16 +57,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "unknown-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.1.1", - Last: "10.0.1.254", - Prefix: 24, - Gateway: "10.0.1.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.1.1-10.0.1.254"}, + Prefix: 24, + Gateway: "10.0.1.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -106,16 +105,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -163,7 +161,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -200,16 +198,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -250,12 +247,12 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Prefix: 24, Gateway: "10.0.1.1", Addresses: []string{ @@ -302,7 +299,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -346,15 +343,14 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -402,7 +398,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -437,15 +433,14 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "global-pool" var secondNamespace string BeforeEach(func() { - pool := v1alpha1.GlobalInClusterIPPool{ + pool := v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ // global pool, no namespace Name: poolName, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.2", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.1", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.2-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.1", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -502,7 +497,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "GlobalInClusterIPPool", @@ -539,7 +534,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-second-namespace", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "GlobalInClusterIPPool", @@ -595,15 +590,14 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.GlobalInClusterIPPool{ + pool := v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.2", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.1", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.2-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.1", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -645,12 +639,12 @@ var _ = Describe("IPAddressClaimReconciler", func() { var claim1, claim2 ipamv1.IPAddressClaim BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.50", "10.0.0.128", @@ -712,7 +706,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-1", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -749,7 +743,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-2", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -801,12 +795,12 @@ var _ = Describe("IPAddressClaimReconciler", func() { var claim1, claim2 ipamv1.IPAddressClaim BeforeEach(func() { - poolA := v1alpha1.InClusterIPPool{ + poolA := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: commonPoolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.50"}, Prefix: 24, Gateway: "10.0.0.1", @@ -817,12 +811,12 @@ var _ = Describe("IPAddressClaimReconciler", func() { secondNamespace = createNamespace() - poolB := v1alpha1.InClusterIPPool{ + poolB := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: commonPoolName, Namespace: secondNamespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.50"}, Prefix: 24, Gateway: "10.0.0.1", @@ -882,7 +876,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-1", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -919,7 +913,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-2", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -970,12 +964,12 @@ var _ = Describe("IPAddressClaimReconciler", func() { var claimFromNamespacedPool, claimFromGlobalPool ipamv1.IPAddressClaim BeforeEach(func() { - namespacedPool := v1alpha1.InClusterIPPool{ + namespacedPool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: commonPoolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.50"}, Prefix: 24, Gateway: "10.0.0.1", @@ -984,11 +978,11 @@ var _ = Describe("IPAddressClaimReconciler", func() { Expect(k8sClient.Create(context.Background(), &namespacedPool)).To(Succeed()) Eventually(Get(&namespacedPool)).Should(Succeed()) - globalPool := v1alpha1.GlobalInClusterIPPool{ + globalPool := v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ // global pool, no namespace Name: commonPoolName, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.50"}, Prefix: 24, Gateway: "10.0.0.1", @@ -1049,7 +1043,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-1", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -1086,7 +1080,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test-2", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "GlobalInClusterIPPool", @@ -1135,10 +1129,10 @@ var _ = Describe("IPAddressClaimReconciler", func() { When("the pool is paused", func() { When("a claim is created", func() { const poolName = "paused-pool" - var pool v1alpha1.InClusterIPPool + var pool v1alpha2.InClusterIPPool BeforeEach(func() { - pool = v1alpha1.InClusterIPPool{ + pool = v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, @@ -1146,7 +1140,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { clusterv1.PausedAnnotation: "", }, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.50"}, Prefix: 24, Gateway: "10.0.1.1", @@ -1197,15 +1191,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { When("a claim is deleted", func() { const poolName = "paused-delete-claim-pool" - var pool v1alpha1.InClusterIPPool + var pool v1alpha2.InClusterIPPool BeforeEach(func() { - pool = v1alpha1.InClusterIPPool{ + pool = v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.20.51"}, Prefix: 24, Gateway: "10.0.20.1", @@ -1274,16 +1268,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -1349,7 +1342,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -1378,16 +1371,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -1467,7 +1459,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -1496,11 +1488,11 @@ var _ = Describe("IPAddressClaimReconciler", func() { const poolName = "test-pool" BeforeEach(func() { - pool := v1alpha1.GlobalInClusterIPPool{ + pool := v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.2-10.0.0.254", }, @@ -1561,7 +1553,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "GlobalInClusterIPPool", @@ -1598,7 +1590,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "GlobalInClusterIPPool", @@ -1659,16 +1651,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { ) BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -1739,7 +1730,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -1816,16 +1807,15 @@ var _ = Describe("IPAddressClaimReconciler", func() { ) BeforeEach(func() { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: poolName, Namespace: namespace, }, - Spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.1", - Last: "10.0.0.254", - Prefix: 24, - Gateway: "10.0.0.2", + Spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{"10.0.0.1-10.0.0.254"}, + Prefix: 24, + Gateway: "10.0.0.2", }, } Expect(k8sClient.Create(context.Background(), &pool)).To(Succeed()) @@ -1882,7 +1872,7 @@ var _ = Describe("IPAddressClaimReconciler", func() { Name: "test", }, { - APIVersion: "ipam.cluster.x-k8s.io/v1alpha1", + APIVersion: "ipam.cluster.x-k8s.io/v1alpha2", BlockOwnerDeletion: pointer.Bool(true), Controller: pointer.Bool(false), Kind: "InClusterIPPool", @@ -1943,7 +1933,7 @@ func deleteCluster(name, namespace string) { } func deleteClusterScopedPool(name string) { - pool := v1alpha1.GlobalInClusterIPPool{ + pool := v1alpha2.GlobalInClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, @@ -1953,7 +1943,7 @@ func deleteClusterScopedPool(name string) { } func deleteNamespacedPool(name, namespace string) { - pool := v1alpha1.InClusterIPPool{ + pool := v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, diff --git a/internal/controllers/suite_test.go b/internal/controllers/suite_test.go index a050d67..69f8d5e 100644 --- a/internal/controllers/suite_test.go +++ b/internal/controllers/suite_test.go @@ -35,7 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest/komega" //+kubebuilder:scaffold:imports - v1alpha1 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + v1alpha2 "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/index" ) @@ -77,7 +77,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - Expect(v1alpha1.AddToScheme(scheme.Scheme)).To(Succeed()) + Expect(v1alpha2.AddToScheme(scheme.Scheme)).To(Succeed()) Expect(clusterv1.AddToScheme(scheme.Scheme)).To(Succeed()) Expect(ipamv1.AddToScheme(scheme.Scheme)).To(Succeed()) diff --git a/internal/poolutil/pool.go b/internal/poolutil/pool.go index 9235dd6..6eb8ade 100644 --- a/internal/poolutil/pool.go +++ b/internal/poolutil/pool.go @@ -31,7 +31,6 @@ import ( ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/index" ) @@ -173,29 +172,6 @@ func IPSetCount(ipSet *netipx.IPSet) int { return math.MaxInt } -// IPPoolSpecToIPSet converts poolSpec to a set of IP. -func IPPoolSpecToIPSet(poolSpec *v1alpha1.InClusterIPPoolSpec) (*netipx.IPSet, error) { - if len(poolSpec.Addresses) > 0 { - return AddressesToIPSet(poolSpec.Addresses) - } - - builder := &netipx.IPSetBuilder{} - - start, err := netip.ParseAddr(poolSpec.First) - if err != nil { - return nil, err - } - - end, err := netip.ParseAddr(poolSpec.Last) - if err != nil { - return nil, err - } - - builder.AddRange(netipx.IPRangeFrom(start, end)) - - return builder.IPSet() -} - // AddressStrParses checks to see that the addresss string is one of // a valid single IP address, a hyphonated IP range, or a Prefix. func AddressStrParses(addressStr string) bool { diff --git a/internal/poolutil/pool_test.go b/internal/poolutil/pool_test.go index 690ba0d..5cc9c88 100644 --- a/internal/poolutil/pool_test.go +++ b/internal/poolutil/pool_test.go @@ -23,11 +23,9 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go4.org/netipx" - - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" ) -var _ = Describe("AddressListToSet", func() { +var _ = Describe("AddressesToIPSet", func() { It("converts the slice to an IPSet", func() { addressSlice := []string{ "192.168.1.25", @@ -82,13 +80,50 @@ var _ = Describe("AddressListToSet", func() { Expect(ipSet.Contains(ip)).To(BeFalse()) }) - It("errors on invalid addresses", func() { - addressSlice := []string{ - "192.168.1.25", - "192.168.1.2612", + It("handles combinations of IPv6 ranges, cidrs, and individual IPs", func() { + addresses := []string{ + "fe80::10/127", + "fe80::13-fe80::14", + "fe80::16", } + + ipSet, err := AddressesToIPSet(addresses) + Expect(err).NotTo(HaveOccurred()) + Expect(ipSet.Contains(mustParse("fe80::9"))).To(BeFalse()) + Expect(ipSet.Contains(mustParse("fe80::10"))).To(BeTrue()) + Expect(ipSet.Contains(mustParse("fe80::11"))).To(BeTrue()) + Expect(ipSet.Contains(mustParse("fe80::12"))).To(BeFalse()) + + Expect(ipSet.Contains(mustParse("fe80::12"))).To(BeFalse()) + Expect(ipSet.Contains(mustParse("fe80::13"))).To(BeTrue()) + Expect(ipSet.Contains(mustParse("fe80::14"))).To(BeTrue()) + Expect(ipSet.Contains(mustParse("fe80::15"))).To(BeFalse()) + + Expect(ipSet.Contains(mustParse("fe80::16"))).To(BeTrue()) + }) + + It("returns and error when addresses is invalid", func() { + addressSlice := []string{"192.168.1.25", "192.168.1.2612"} _, err := AddressesToIPSet(addressSlice) Expect(err).To(HaveOccurred()) + addressSlice = []string{"192.168.1.2-192.168.1.4-"} + _, err = AddressesToIPSet(addressSlice) + Expect(err).To(HaveOccurred()) + addressSlice = []string{"192.168.1.2-192.168.1.4-192.168.1.9"} + _, err = AddressesToIPSet(addressSlice) + Expect(err).To(HaveOccurred()) + addressSlice = []string{"192.168.1.4-192.168.1.2"} + _, err = AddressesToIPSet(addressSlice) + Expect(err).To(HaveOccurred()) + addressSlice = []string{"192.168.1.4/21-192.168.1.2"} + _, err = AddressesToIPSet(addressSlice) + Expect(err).To(HaveOccurred()) + addressSlice = []string{"192.168.1.4/-192.168.1.2"} + _, err = AddressesToIPSet(addressSlice) + Expect(err).To(HaveOccurred()) + addressSlice = []string{"192.168.1.4-192.168.1.2"} + _, err = AddressesToIPSet(addressSlice) + Expect(err).To(HaveOccurred()) }) }) @@ -272,159 +307,6 @@ var _ = Describe("AddressStrParses", func() { }) }) -var _ = Describe("IPPoolSpecToIPSet", func() { - Context("when the poolSpec has a list of addresses", func() { - It("parses pools with start/end", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{ - First: "192.168.1.2", - Last: "192.168.1.4", - } - - ipSet, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).NotTo(HaveOccurred()) - Expect(ipSet.Contains(mustParse("192.168.1.2"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.3"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.4"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.5"))).To(BeFalse()) - }) - - It("parses single IPs", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "192.168.1.2", - "192.168.1.3", - }, - } - - ipSet, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).NotTo(HaveOccurred()) - Expect(ipSet.Contains(mustParse("192.168.1.2"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.3"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.4"))).To(BeFalse()) - }) - - It("parses IP ranges with a dash", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "192.168.1.2-192.168.1.4", - "192.168.1.7-192.168.1.9", - }, - } - - ipSet, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).NotTo(HaveOccurred()) - Expect(ipSet.Contains(mustParse("192.168.1.2"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.3"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.4"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.5"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("192.168.1.6"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("192.168.1.7"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.8"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.9"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.10"))).To(BeFalse()) - }) - - It("parses IP CIDRs", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "192.168.1.8/29", - "192.168.1.100/30", - }, - } - - ipSet, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).NotTo(HaveOccurred()) - Expect(ipSet.Contains(mustParse("192.168.1.7"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("192.168.1.8"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.9"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.10"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.11"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.12"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.13"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.14"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.15"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.16"))).To(BeFalse()) - - Expect(ipSet.Contains(mustParse("192.168.1.99"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("192.168.1.100"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.101"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.102"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.103"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.104"))).To(BeFalse()) - }) - - It("handles combinations of ranges, cidrs, and individual IPs", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "192.168.1.100/31", - "192.168.1.2-192.168.1.4", - "192.168.2.1", - }, - } - - ipSet, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).NotTo(HaveOccurred()) - Expect(ipSet.Contains(mustParse("192.168.1.99"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("192.168.1.100"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.101"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.102"))).To(BeFalse()) - - Expect(ipSet.Contains(mustParse("192.168.1.1"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("192.168.1.2"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.3"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.4"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("192.168.1.5"))).To(BeFalse()) - - Expect(ipSet.Contains(mustParse("192.168.2.1"))).To(BeTrue()) - }) - - It("handles combinations of IPv6 ranges, cidrs, and individual IPs", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "fe80::10/127", - "fe80::13-fe80::14", - "fe80::16", - }, - } - - ipSet, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).NotTo(HaveOccurred()) - Expect(ipSet.Contains(mustParse("fe80::9"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("fe80::10"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("fe80::11"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("fe80::12"))).To(BeFalse()) - - Expect(ipSet.Contains(mustParse("fe80::12"))).To(BeFalse()) - Expect(ipSet.Contains(mustParse("fe80::13"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("fe80::14"))).To(BeTrue()) - Expect(ipSet.Contains(mustParse("fe80::15"))).To(BeFalse()) - - Expect(ipSet.Contains(mustParse("fe80::16"))).To(BeTrue()) - }) - - It("returns and error when spec is invalid", func() { - poolSpec := &v1alpha1.InClusterIPPoolSpec{Addresses: []string{"192.168.1.2-192.168.1.4-"}} - _, err := IPPoolSpecToIPSet(poolSpec) - Expect(err).To(HaveOccurred()) - poolSpec = &v1alpha1.InClusterIPPoolSpec{Addresses: []string{"192.168.1.2-192.168.1.4-192.168.1.9"}} - _, err = IPPoolSpecToIPSet(poolSpec) - Expect(err).To(HaveOccurred()) - poolSpec = &v1alpha1.InClusterIPPoolSpec{Addresses: []string{"192.168.1.4-192.168.1.2"}} - _, err = IPPoolSpecToIPSet(poolSpec) - Expect(err).To(HaveOccurred()) - poolSpec = &v1alpha1.InClusterIPPoolSpec{Addresses: []string{"192.168.1.4/21-192.168.1.2"}} - _, err = IPPoolSpecToIPSet(poolSpec) - Expect(err).To(HaveOccurred()) - poolSpec = &v1alpha1.InClusterIPPoolSpec{Addresses: []string{"192.168.1.4/-192.168.1.2"}} - _, err = IPPoolSpecToIPSet(poolSpec) - Expect(err).To(HaveOccurred()) - poolSpec = &v1alpha1.InClusterIPPoolSpec{Addresses: []string{"192.168.1.4-192.168.1.2"}} - _, err = IPPoolSpecToIPSet(poolSpec) - Expect(err).To(HaveOccurred()) - }) - }) -}) - var _ = DescribeTable("IPSetCount", func(expectedCount int, addresses ...string) { ipSet, err := AddressesToIPSet(addresses) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/webhooks/inclusterippool.go b/internal/webhooks/inclusterippool.go index 6f2be06..efe47af 100644 --- a/internal/webhooks/inclusterippool.go +++ b/internal/webhooks/inclusterippool.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/poolutil" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/pkg/types" ) @@ -45,7 +45,7 @@ const ( func (webhook *InClusterIPPool) SetupWebhookWithManager(mgr ctrl.Manager) error { err := ctrl.NewWebhookManagedBy(mgr). - For(&v1alpha1.InClusterIPPool{}). + For(&v1alpha2.InClusterIPPool{}). WithDefaulter(webhook). WithValidator(webhook). Complete() @@ -53,17 +53,17 @@ func (webhook *InClusterIPPool) SetupWebhookWithManager(mgr ctrl.Manager) error return err } return ctrl.NewWebhookManagedBy(mgr). - For(&v1alpha1.GlobalInClusterIPPool{}). + For(&v1alpha2.GlobalInClusterIPPool{}). WithDefaulter(webhook). WithValidator(webhook). Complete() } -// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-ipam-cluster-x-k8s-io-v1alpha1-inclusterippool,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=inclusterippools,versions=v1alpha1,name=validation.inclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 -// +kubebuilder:webhook:verbs=create;update,path=/mutate-ipam-cluster-x-k8s-io-v1alpha1-inclusterippool,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=inclusterippools,versions=v1alpha1,name=default.inclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-ipam-cluster-x-k8s-io-v1alpha2-inclusterippool,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=inclusterippools,versions=v1alpha2,name=validation.inclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:verbs=create;update,path=/mutate-ipam-cluster-x-k8s-io-v1alpha2-inclusterippool,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=inclusterippools,versions=v1alpha2,name=default.inclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 -// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-ipam-cluster-x-k8s-io-v1alpha1-globalinclusterippool,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=globalinclusterippools,versions=v1alpha1,name=validation.globalinclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 -// +kubebuilder:webhook:verbs=create;update,path=/mutate-ipam-cluster-x-k8s-io-v1alpha1-globalinclusterippool,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=globalinclusterippools,versions=v1alpha1,name=default.globalinclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-ipam-cluster-x-k8s-io-v1alpha2-globalinclusterippool,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=globalinclusterippools,versions=v1alpha2,name=validation.globalinclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 +// +kubebuilder:webhook:verbs=create;update,path=/mutate-ipam-cluster-x-k8s-io-v1alpha2-globalinclusterippool,mutating=true,failurePolicy=fail,matchPolicy=Equivalent,groups=ipam.cluster.x-k8s.io,resources=globalinclusterippools,versions=v1alpha2,name=default.globalinclusterippool.ipam.cluster.x-k8s.io,sideEffects=None,admissionReviewVersions=v1;v1beta1 // InClusterIPPool implements a validating and defaulting webhook for InClusterIPPool and GlobalInClusterIPPool. type InClusterIPPool struct { @@ -75,51 +75,6 @@ var _ webhook.CustomValidator = &InClusterIPPool{} // Default satisfies the defaulting webhook interface. func (webhook *InClusterIPPool) Default(_ context.Context, obj runtime.Object) error { - pool, ok := obj.(types.GenericInClusterPool) - if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected a InClusterIPPool or an GlobalInClusterIPPool but got a %T", obj)) - } - poolSpec := pool.PoolSpec() - if len(poolSpec.Addresses) > 0 { - return nil - } - - if poolSpec.Subnet == "" { - first, err := netip.ParseAddr(poolSpec.First) - if err != nil { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind(pool.GetObjectKind().GroupVersionKind().Kind).GroupKind(), pool.GetName(), field.ErrorList{ - field.Invalid(field.NewPath("spec", "subnet"), poolSpec.Subnet, err.Error()), - }) - } - - prefix, err := first.Prefix(poolSpec.Prefix) - if err != nil { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind(pool.GetObjectKind().GroupVersionKind().Kind).GroupKind(), pool.GetName(), field.ErrorList{ - field.Invalid(field.NewPath("spec", "prefix"), poolSpec.Prefix, err.Error()), - }) - } - - poolSpec.Subnet = prefix.String() - } else { - prefix, err := netip.ParsePrefix(poolSpec.Subnet) - if err != nil { - return apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind(pool.GetObjectKind().GroupVersionKind().Kind).GroupKind(), pool.GetName(), field.ErrorList{ - field.Invalid(field.NewPath("spec", "subnet"), poolSpec.Subnet, err.Error()), - }) - } - - prefixRange := netipx.RangeOfPrefix(prefix) - if poolSpec.First == "" { - poolSpec.First = prefixRange.From().Next().String() // omits the first address, the assumed gateway - } - if poolSpec.Last == "" { - poolSpec.Last = prefixRange.To().Prev().String() // omits the last address, the assumed broadcast - } - if poolSpec.Prefix == 0 { - poolSpec.Prefix = prefix.Bits() - } - } - return nil } @@ -149,7 +104,7 @@ func (webhook *InClusterIPPool) ValidateUpdate(ctx context.Context, oldObj, newO } oldPoolRef := corev1.TypedLocalObjectReference{ - APIGroup: pointer.String(v1alpha1.GroupVersion.Group), + APIGroup: pointer.String(v1alpha2.GroupVersion.Group), Kind: oldPool.GetObjectKind().GroupVersionKind().Kind, Name: oldPool.GetName(), } @@ -220,66 +175,16 @@ func (webhook *InClusterIPPool) validate(_, newPool types.GenericInClusterPool) var allErrs field.ErrorList defer func() { if len(allErrs) > 0 { - reterr = apierrors.NewInvalid(v1alpha1.GroupVersion.WithKind(newPool.GetObjectKind().GroupVersionKind().Kind).GroupKind(), newPool.GetName(), allErrs) + reterr = apierrors.NewInvalid(v1alpha2.GroupVersion.WithKind(newPool.GetObjectKind().GroupVersionKind().Kind).GroupKind(), newPool.GetName(), allErrs) } }() - if len(newPool.PoolSpec().Addresses) > 0 { - allErrs = append(allErrs, validateAddresses(newPool)...) - return - } - - prefix, err := netip.ParsePrefix(newPool.PoolSpec().Subnet) - if err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "subnet"), newPool.PoolSpec().Subnet, err.Error())) - return - } - - first, err := netip.ParseAddr(newPool.PoolSpec().First) - if err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "first"), newPool.PoolSpec().First, err.Error())) - } else if !prefix.Contains(first) { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "first"), newPool.PoolSpec().First, "address is not part of spec.subnet")) - } - - last, err := netip.ParseAddr(newPool.PoolSpec().Last) - if err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "last"), newPool.PoolSpec().Last, err.Error())) - } else if !prefix.Contains(last) { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "last"), newPool.PoolSpec().Last, "address is not part of spec.subnet")) - } - - if prefix.Bits() != newPool.PoolSpec().Prefix { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "prefix"), newPool.PoolSpec().Prefix, "does not match prefix of spec.subnet")) - } - - if newPool.PoolSpec().Gateway != "" { - _, err := netip.ParseAddr(newPool.PoolSpec().Gateway) - if err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "gateway"), newPool.PoolSpec().Gateway, err.Error())) - } - } - - return //nolint:nakedret -} - -func validateAddresses(newPool types.GenericInClusterPool) field.ErrorList { - var allErrs field.ErrorList - - if newPool.PoolSpec().Subnet != "" { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "subnet"), newPool.PoolSpec().Subnet, "subnet may not be used with addresses")) - } - - if newPool.PoolSpec().First != "" { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "start"), newPool.PoolSpec().First, "start may not be used with addresses")) - } - - if newPool.PoolSpec().Last != "" { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "end"), newPool.PoolSpec().Last, "end may not be used with addresses")) + if len(newPool.PoolSpec().Addresses) == 0 { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "addresses"), newPool.PoolSpec().Addresses, "addresses is required")) } if newPool.PoolSpec().Prefix == 0 { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "prefix"), newPool.PoolSpec().Prefix, "a valid prefix is required when using addresses")) + allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "prefix"), newPool.PoolSpec().Prefix, "a valid prefix is required")) } var hasIPv4Addr, hasIPv6Addr bool @@ -315,10 +220,10 @@ func validateAddresses(newPool types.GenericInClusterPool) field.ErrorList { } } - return allErrs + return //nolint:nakedret } -func validatePrefix(spec *v1alpha1.InClusterIPPoolSpec) (*netipx.IPSet, field.ErrorList) { +func validatePrefix(spec *v1alpha2.InClusterIPPoolSpec) (*netipx.IPSet, field.ErrorList) { var errors field.ErrorList addressesIPSet, err := poolutil.AddressesToIPSet(spec.Addresses) @@ -348,13 +253,9 @@ func validatePrefix(spec *v1alpha1.InClusterIPPoolSpec) (*netipx.IPSet, field.Er return prefixIPSet, errors } -func validateAddressesAreWithinPrefix(spec *v1alpha1.InClusterIPPoolSpec) field.ErrorList { +func validateAddressesAreWithinPrefix(spec *v1alpha2.InClusterIPPoolSpec) field.ErrorList { var errors field.ErrorList - if len(spec.Addresses) == 0 { - return errors - } - prefixIPSet, prefixErrs := validatePrefix(spec) if len(prefixErrs) > 0 { return prefixErrs diff --git a/internal/webhooks/inclusterippool_test.go b/internal/webhooks/inclusterippool_test.go index 7336871..6187b5b 100644 --- a/internal/webhooks/inclusterippool_test.go +++ b/internal/webhooks/inclusterippool_test.go @@ -29,7 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/index" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/pkg/types" ) @@ -40,22 +40,22 @@ func TestPoolDeletionWithExistingIPAddresses(t *testing.T) { scheme := runtime.NewScheme() g.Expect(ipamv1.AddToScheme(scheme)).To(Succeed()) - namespacedPool := &v1alpha1.InClusterIPPool{ + namespacedPool := &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: "my-pool", }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.10-10.0.0.20"}, Prefix: 24, Gateway: "10.0.0.1", }, } - globalPool := &v1alpha1.InClusterIPPool{ + globalPool := &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: "my-pool", }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.10-10.0.0.20"}, Prefix: 24, Gateway: "10.0.0.1", @@ -124,22 +124,22 @@ func TestUpdatingPoolInUseAddresses(t *testing.T) { scheme := runtime.NewScheme() g.Expect(ipamv1.AddToScheme(scheme)).To(Succeed()) - namespacedPool := &v1alpha1.InClusterIPPool{ + namespacedPool := &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: "my-pool", }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.10-10.0.0.20"}, Prefix: 24, Gateway: "10.0.0.1", }, } - globalPool := &v1alpha1.InClusterIPPool{ + globalPool := &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: "my-pool", }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.10-10.0.0.20"}, Prefix: 24, Gateway: "10.0.0.1", @@ -208,28 +208,28 @@ func TestDeleteSkip(t *testing.T) { scheme := runtime.NewScheme() g.Expect(ipamv1.AddToScheme(scheme)).To(Succeed()) - namespacedPool := &v1alpha1.InClusterIPPool{ + namespacedPool := &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: "my-pool", Annotations: map[string]string{ SkipValidateDeleteWebhookAnnotation: "", }, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.10-10.0.0.20"}, Prefix: 24, Gateway: "10.0.0.1", }, } - globalPool := &v1alpha1.InClusterIPPool{ + globalPool := &v1alpha2.InClusterIPPool{ ObjectMeta: metav1.ObjectMeta{ Name: "my-pool", Annotations: map[string]string{ SkipValidateDeleteWebhookAnnotation: "", }, }, - Spec: v1alpha1.InClusterIPPoolSpec{ + Spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{"10.0.0.10-10.0.0.20"}, Prefix: 24, Gateway: "10.0.0.1", @@ -290,39 +290,13 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { tests := []struct { name string - spec v1alpha1.InClusterIPPoolSpec - expect v1alpha1.InClusterIPPoolSpec + spec v1alpha2.InClusterIPPoolSpec + expect v1alpha2.InClusterIPPoolSpec errors bool }{ - { - name: "infer prefix, first and last from subnet", - spec: v1alpha1.InClusterIPPoolSpec{ - Subnet: "10.0.0.0/24", - }, - expect: v1alpha1.InClusterIPPoolSpec{ - Subnet: "10.0.0.0/24", - Prefix: 24, - First: "10.0.0.1", - Last: "10.0.0.254", - }, - }, - { - name: "derive subnet from prefix and first", - spec: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.25", - Last: "10.0.0.30", - Prefix: 28, - }, - expect: v1alpha1.InClusterIPPoolSpec{ - First: "10.0.0.25", - Last: "10.0.0.30", - Prefix: 28, - Subnet: "10.0.0.16/28", - }, - }, { name: "addresses with gateway and prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -331,7 +305,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { Gateway: "10.0.0.24", Prefix: 28, }, - expect: v1alpha1.InClusterIPPoolSpec{ + expect: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -343,7 +317,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { }, { name: "addresses with prefix and no gateway", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -351,7 +325,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { }, Prefix: 28, }, - expect: v1alpha1.InClusterIPPoolSpec{ + expect: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -362,7 +336,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { }, { name: "IPv6 addresses with gateway and prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "fe01::2", "fe01::3-fe01::5", @@ -371,7 +345,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { Gateway: "fe01::1", Prefix: 28, }, - expect: v1alpha1.InClusterIPPoolSpec{ + expect: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "fe01::2", "fe01::3-fe01::5", @@ -383,7 +357,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { }, { name: "IPv6 addresses and prefix with no gateway", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "fe01::2", "fe01::3-fe01::5", @@ -391,7 +365,7 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { }, Prefix: 28, }, - expect: v1alpha1.InClusterIPPoolSpec{ + expect: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "fe01::2", "fe01::3-fe01::5", @@ -403,8 +377,8 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { } for _, tt := range tests { - namespacedPool := &v1alpha1.InClusterIPPool{Spec: tt.spec} - globalPool := &v1alpha1.GlobalInClusterIPPool{Spec: tt.spec} + namespacedPool := &v1alpha2.InClusterIPPool{Spec: tt.spec} + globalPool := &v1alpha2.GlobalInClusterIPPool{Spec: tt.spec} scheme := runtime.NewScheme() g.Expect(ipamv1.AddToScheme(scheme)).To(Succeed()) @@ -427,51 +401,24 @@ func TestInClusterIPPoolDefaulting(t *testing.T) { type invalidScenarioTest struct { testcase string - spec v1alpha1.InClusterIPPoolSpec + spec v1alpha2.InClusterIPPoolSpec expectedError string } func TestInvalidScenarios(t *testing.T) { tests := []invalidScenarioTest{ { - testcase: "specifying addresses and subnet should not allow subnet", - spec: v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "10.0.0.25", - "10.0.0.26", - "10.0.0.27", - }, - Subnet: "10.0.0.0/24", - }, - expectedError: "subnet may not be used with addresses", - }, - { - testcase: "specifying addresses should not allow first", - spec: v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "10.0.0.25", - "10.0.0.26", - "10.0.0.27", - }, - First: "10.0.0.2", - }, - expectedError: "start may not be used with addresses", - }, - { - testcase: "specifying addresses should not allow last", - spec: v1alpha1.InClusterIPPoolSpec{ - Addresses: []string{ - "10.0.0.25", - "10.0.0.26", - "10.0.0.27", - }, - Last: "10.0.0.2", + testcase: "addresses must be set", + spec: v1alpha2.InClusterIPPoolSpec{ + Addresses: []string{}, + Prefix: 24, + Gateway: "10.0.0.1", }, - expectedError: "end may not be used with addresses", + expectedError: "addresses is required", }, { testcase: "invalid gateway should not be allowed", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -484,7 +431,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "specifying an address that belongs to separate subnets should not be allowed", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -497,7 +444,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "specifying an address that is invalid", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -510,18 +457,18 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "omitting a prefix should not be allowed", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", }, Gateway: "10.0.0.1", }, - expectedError: "a valid prefix is required when using addresses", + expectedError: "a valid prefix is required", }, { testcase: "specifying an invalid prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.25", "10.0.0.26", @@ -533,7 +480,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "address range is out of order", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.10-10.0.0.5", }, @@ -544,7 +491,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "CIDR address has invalid prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.0.10/33", }, @@ -555,7 +502,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "address range is below Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.0.10-10.0.0.20", @@ -567,7 +514,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "address range is above Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.2.10-10.0.2.20", @@ -579,7 +526,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "address range start is below Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.0.20-10.0.1.20", @@ -591,7 +538,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "address range end is above Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.1.20-10.0.2.20", @@ -603,7 +550,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "CIDR is below Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.0.1/24", @@ -615,7 +562,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "CIDR is above Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.2.1/24", @@ -627,7 +574,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "CIDR start is below Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.0.0/23", @@ -639,7 +586,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "CIDR end is above Prefix", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.1.0/23", @@ -651,7 +598,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "Addresses are IPv4 and Gateway is IPv6", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "10.0.1.0", "10.0.0.2-10.0.0.250", @@ -663,7 +610,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "Addresses are IPv6 and Gateway is IPv4", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "fd00::1", "fd00::100-fd00::200", @@ -675,7 +622,7 @@ func TestInvalidScenarios(t *testing.T) { }, { testcase: "Addresses is using mismatched IP families", - spec: v1alpha1.InClusterIPPoolSpec{ + spec: v1alpha2.InClusterIPPoolSpec{ Addresses: []string{ "fd00::1", "fd00::100-fd00::200", @@ -689,8 +636,8 @@ func TestInvalidScenarios(t *testing.T) { }, } for _, tt := range tests { - namespacedPool := &v1alpha1.InClusterIPPool{Spec: tt.spec} - globalPool := &v1alpha1.GlobalInClusterIPPool{Spec: tt.spec} + namespacedPool := &v1alpha2.InClusterIPPool{Spec: tt.spec} + globalPool := &v1alpha2.GlobalInClusterIPPool{Spec: tt.spec} g := NewWithT(t) scheme := runtime.NewScheme() diff --git a/main.go b/main.go index 87320a5..0b0f615 100644 --- a/main.go +++ b/main.go @@ -34,6 +34,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/controllers" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/index" "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/internal/webhooks" @@ -50,6 +51,7 @@ func init() { utilruntime.Must(clusterv1.AddToScheme(scheme)) utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(v1alpha2.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/pkg/types/types.go b/pkg/types/types.go index 4967064..31e7c92 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -20,12 +20,12 @@ package types import ( "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha1" + "sigs.k8s.io/cluster-api-ipam-provider-in-cluster/api/v1alpha2" ) // GenericInClusterPool is a common interface for InClusterIPPool and GlobalInClusterIPPool. type GenericInClusterPool interface { client.Object - PoolSpec() *v1alpha1.InClusterIPPoolSpec - PoolStatus() *v1alpha1.InClusterIPPoolStatus + PoolSpec() *v1alpha2.InClusterIPPoolSpec + PoolStatus() *v1alpha2.InClusterIPPoolStatus }