diff --git a/.custom-gcl.yml b/.custom-gcl.yml new file mode 100644 index 0000000..6899a06 --- /dev/null +++ b/.custom-gcl.yml @@ -0,0 +1,6 @@ +version: v2.6.2 +name: golangci-lint-kube-api-linter +destination: ./bin +plugins: +- module: 'sigs.k8s.io/kube-api-linter' + version: v0.0.0-20251112164541-d94382a24f06 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fca8287..290a33b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: uses: golangci/golangci-lint-action@v7 with: install-mode: goinstall - version: v2.0.2 + version: v2.6.2 - name: Run ShellCheck uses: ludeeus/action-shellcheck@master @@ -29,3 +29,7 @@ jobs: - name: Running nilaway run: | make lint-nilaway + + - name: Running golang-ci-lint-kube-api-linter + run: | + make lint-api diff --git a/.gitignore b/.gitignore index 1143e9d..5c44468 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,4 @@ dist/* # e2e files _artifacts *-envsubst.yaml -test/e2e/data/infrastructure-scaleway/v1beta1/*.yaml +test/e2e/data/infrastructure-scaleway/v1beta2/*.yaml diff --git a/.golangci-kal.yml b/.golangci-kal.yml new file mode 100644 index 0000000..c959d36 --- /dev/null +++ b/.golangci-kal.yml @@ -0,0 +1,80 @@ +version: "2" +run: + go: "1.24" + allow-parallel-runners: true +linters: + default: none + enable: + - kubeapilinter # linter for Kube API conventions + settings: + custom: + kubeapilinter: + type: module + description: KAL is the Kube-API-Linter and lints Kube like APIs based on API conventions and best practices. + settings: + linters: + enable: + - "commentstart" # Ensure comments start with the serialized version of the field name. + - "conditions" # Ensure conditions have the correct json tags and markers. + - "conflictingmarkers" + - "duplicatemarkers" # Ensure there are no exact duplicate markers. for types and fields. + #- "forbiddenmarkers" # Ensure that types and fields do not contain any markers that are forbidden. + - "integers" # Ensure only int32 and int64 are used for integers. + - "jsontags" # Ensure every field has a json tag. + - "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items. + #- "nobools" # Bools do not evolve over time, should use enums instead. + - "nodurations" # Prevents usage of `Duration` types. + - "nofloats" # Ensure floats are not used. + - "nomaps" # Ensure maps are not used. + - "nonullable" # Ensure that types and fields do not have the nullable marker. + - "nophase" # Phase fields are discouraged by the Kube API conventions, use conditions instead. + - "notimestamp" # Prevents usage of 'Timestamp' fields + - "optionalfields" # Ensure that all fields marked as optional adhere to being pointers and + # having the `omitempty` value in their `json` tag where appropriate. + - "optionalorrequired" # Every field should be marked as `+optional` or `+required`. + - "requiredfields" # Required fields should not be pointers, and should not have `omitempty`. + - "ssatags" # Ensure array fields have the appropriate listType markers + - "statusoptional" # Ensure all first children within status should be optional. + - "statussubresource" # All root objects that have a `status` field should have a status subresource. + - "uniquemarkers" # Ensure that types and fields do not contain more than a single definition of a marker that should only be present once. + + # Linters below this line are disabled, pending conversation on how and when to enable them. + disable: + - "*" # We will manually enable new linters after understanding the impact. Disable all by default. + lintersConfig: + conflictingmarkers: + conflicts: + - name: "default_vs_required" + sets: + - ["default", "kubebuilder:default"] + - ["required", "kubebuilder:validation:Required", "k8s:required"] + description: "A field with a default value cannot be required" + conditions: + isFirstField: Warn # Require conditions to be the first field in the status struct. + usePatchStrategy: Forbid # Forbid patchStrategy markers on the Conditions field. + useProtobuf: Forbid # We don't use protobuf, so protobuf tags are not required. + optionalfields: + pointers: + preference: WhenRequired # Always | WhenRequired # Whether to always require pointers, or only when required. Defaults to `Always`. + policy: SuggestFix # SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`. + omitempty: + policy: SuggestFix # SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`. + + exclusions: + generated: strict + paths: + - zz_generated.*\.go$ + - ".*_test.go" # Exclude test files. + rules: + ## KAL should only run on API folders. + - path-except: "api//*" + linters: + - kubeapilinter + + ## Excludes for old apiVersions that can be removed once the apiVersions are dropped (we don't want to make any changes to these APIs). + - path: "api/v1alpha1" + linters: + - kubeapilinter +issues: + max-same-issues: 0 + max-issues-per-linter: 0 diff --git a/.golangci.yml b/.golangci.yml index d913623..9763337 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,9 +31,9 @@ linters: alias: - pkg: sigs.k8s.io/controller-runtime/pkg/log alias: logf - - pkg: sigs.k8s.io/cluster-api/api/v1beta1 + - pkg: sigs.k8s.io/cluster-api/api/core/v1beta2 alias: clusterv1 - - pkg: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1 + - pkg: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 alias: infrav1 - pkg: k8s.io/apimachinery/pkg/api/errors alias: apierrors @@ -41,12 +41,17 @@ linters: alias: utilerrors - pkg: github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb/util alias: lbutil + - pkg: k8s.io/apimachinery/pkg/apis/meta/v1 + alias: metav1 exclusions: generated: lax rules: - linters: - lll path: api/* + - linters: + - lll + path: cmd/main.go - linters: - lll path: test/e2e/* @@ -54,9 +59,6 @@ linters: - dupl - lll path: internal/* - - linters: - - staticcheck - text: "ST1019" - linters: - staticcheck text: "ST1005" @@ -73,11 +75,22 @@ linters: - examples$ formatters: enable: + - gci - gofmt - goimports + settings: + gci: + sections: + - standard + - blank + - dot + - default + - localmodule + custom-order: true exclusions: - generated: lax + generated: strict paths: + - zz_generated.*\.go$ - third_party$ - builtin$ - examples$ diff --git a/Makefile b/Makefile index 98fca88..38ffc96 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ else GOBIN=$(shell go env GOBIN) endif +TOOLS_DIR := hack/tools ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) # CONTAINER_TOOL defines the container tool to be used for building images. @@ -48,10 +49,14 @@ 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 mockgen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +generate: controller-gen generate-go-conversions mockgen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object paths="./..." go generate ./... +.PHONY: generate-go-conversions +generate-go-conversions: conversion-gen ## Generate conversions go code + $(CONVERSION_GEN) --output-file=zz_generated.conversion.go github.com/scaleway/cluster-api-provider-scaleway/api/... + .PHONY: fmt fmt: ## Run go fmt against code. go fmt ./... @@ -70,7 +75,7 @@ KIND_KUBECONFIG ?= /tmp/caps-e2e-kubeconfig E2E_ARTIFACTS ?= $(ROOT_DIR)/_artifacts E2E_CONF_FILE ?= $(ROOT_DIR)/test/e2e/config/scaleway.yaml E2E_CONF_FILE_ENVSUBST := $(ROOT_DIR)/test/e2e/config/scaleway-envsubst.yaml -E2E_V1BETA1_TEMPLATES := $(ROOT_DIR)/test/e2e/data/infrastructure-scaleway/v1beta1 +E2E_V1BETA2_TEMPLATES := $(ROOT_DIR)/test/e2e/data/infrastructure-scaleway/v1beta2 E2E_FOCUS ?= "" .PHONY: setup-test-e2e @@ -83,9 +88,9 @@ setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist .PHONY: generate-e2e generate-e2e: kustomize ## Generate templates for e2e - $(KUSTOMIZE) build $(E2E_V1BETA1_TEMPLATES)/cluster-template --load-restrictor LoadRestrictionsNone > $(E2E_V1BETA1_TEMPLATES)/cluster-template.yaml - $(KUSTOMIZE) build $(E2E_V1BETA1_TEMPLATES)/cluster-template-private-network --load-restrictor LoadRestrictionsNone > $(E2E_V1BETA1_TEMPLATES)/cluster-template-private-network.yaml - $(KUSTOMIZE) build $(E2E_V1BETA1_TEMPLATES)/cluster-template-managed --load-restrictor LoadRestrictionsNone > $(E2E_V1BETA1_TEMPLATES)/cluster-template-managed.yaml + $(KUSTOMIZE) build $(E2E_V1BETA2_TEMPLATES)/cluster-template --load-restrictor LoadRestrictionsNone > $(E2E_V1BETA2_TEMPLATES)/cluster-template.yaml + $(KUSTOMIZE) build $(E2E_V1BETA2_TEMPLATES)/cluster-template-private-network --load-restrictor LoadRestrictionsNone > $(E2E_V1BETA2_TEMPLATES)/cluster-template-private-network.yaml + $(KUSTOMIZE) build $(E2E_V1BETA2_TEMPLATES)/cluster-template-managed --load-restrictor LoadRestrictionsNone > $(E2E_V1BETA2_TEMPLATES)/cluster-template-managed.yaml .PHONY: test-e2e test-e2e: setup-test-e2e generate-e2e docker-build envsubst ginkgo build-installer fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. @@ -101,13 +106,17 @@ cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests @$(KIND) delete cluster --name $(KIND_CLUSTER) --kubeconfig $(KIND_KUBECONFIG) .PHONY: lint -lint: lint-golangci-lint lint-nilaway ## Run linters +lint: lint-api lint-golangci-lint lint-nilaway ## Run linters @echo "done" .PHONY: lint-golangci-lint lint-golangci-lint: golangci-lint ## Run golangci-lint linter $(GOLANGCI_LINT) run +.PHONY: lint-api +lint-api: golangci-lint-kube-api-linter ## Run golangci-lint-kube-api-linter linter + $(GOLANGCI_LINT_KAL) run --config .golangci-kal.yml + .PHONY: lint-nilaway lint-nilaway: nilaway ## Run nilaway linter $(NILAWAY) -include-pkgs=github.com/scaleway/cluster-api-provider-scaleway ./... @@ -217,19 +226,22 @@ NILAWAY = $(LOCALBIN)/nilaway MOCKGEN = $(LOCALBIN)/mockgen ENVSUBST = $(LOCALBIN)/envsubst GINKGO = $(LOCALBIN)/ginkgo +GOLANGCI_LINT_KAL := $(LOCALBIN)/golangci-lint-kube-api-linter +CONVERSION_GEN ?= $(LOCALBIN)/conversion-gen ## Tool Versions -KUSTOMIZE_VERSION ?= v5.6.0 -CONTROLLER_TOOLS_VERSION ?= v0.18.0 +KUSTOMIZE_VERSION ?= v5.7.1 +CONTROLLER_TOOLS_VERSION ?= v0.19.0 #ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') #ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') -GOLANGCI_LINT_VERSION ?= v2.1.0 +GOLANGCI_LINT_VERSION ?= v2.6.2 NILAWAY_VERSION ?= latest -MOCKGEN_VERSION ?= v0.5.2 +MOCKGEN_VERSION ?= v0.6.0 ENVSUBST_VERSION ?=latest -GINKGO_VERSION ?= v2.23.4 +GINKGO_VERSION ?= v2.25.3 +CONVERSION_GEN_VERSION ?= v0.34.0 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. @@ -241,6 +253,11 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(CONTROLLER_GEN): $(LOCALBIN) $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) +.PHONY: conversion-gen +conversion-gen: $(CONVERSION_GEN) ## Download conversion-gen locally if necessary. +$(CONVERSION_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONVERSION_GEN),k8s.io/code-generator/cmd/conversion-gen,$(CONVERSION_GEN_VERSION)) + .PHONY: setup-envtest setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." @@ -279,6 +296,11 @@ ginkgo: $(GINKGO) ## Download ginkgo locally if necessary. $(GINKGO): $(LOCALBIN) $(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo,$(GINKGO_VERSION)) +.PHONY: golangci-lint-kube-api-linter +golangci-lint-kube-api-linter: $(GOLANGCI_LINT_KAL) ## Build golangci-lint-kal from custom configuration. +$(GOLANGCI_LINT_KAL): $(GOLANGCI_LINT) + $(GOLANGCI_LINT) custom + # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary # $2 - package url which can be installed diff --git a/PROJECT b/PROJECT index 5a32448..c62c745 100644 --- a/PROJECT +++ b/PROJECT @@ -69,4 +69,96 @@ resources: kind: ScalewayManagedMachinePool path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayCluster + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + spoke: + - v1alpha1 + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayMachine + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + spoke: + - v1alpha1 + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayClusterTemplate + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + spoke: + - v1alpha1 + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayMachineTemplate + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + spoke: + - v1alpha1 + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayManagedCluster + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + spoke: + - v1alpha1 + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayManagedControlPlane + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + defaulting: true + spoke: + - v1alpha1 + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: ScalewayManagedMachinePool + path: github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + spoke: + - v1alpha1 + webhookVersion: v1 version: "3" diff --git a/README.md b/README.md index d28a64e..a774989 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,12 @@ If you need help with CAPS, please visit the #cluster-api channel on ### Cluster API Versions -Currently, CAPS is compatible only with the `v1beta1` version of CAPI (v1.0.x). +This provider's versions are compatible with the following versions of Cluster API: + +| Scaleway Provider CRD version | Cluster API `v1beta1` (v1.0-v1.10) | Cluster API `v1beta2` (v1.11+) | +| ----------------------------- | ---------------------------------- | ------------------------------ | +| `v1alpha1` (v0.1.x) | ✓ | ☓ | +| `v1alpha2` (v0.2.x, main) | ☓ | ✓ | ### Kubernetes Versions diff --git a/api/v1alpha1/conversion.go b/api/v1alpha1/conversion.go new file mode 100644 index 0000000..335bf25 --- /dev/null +++ b/api/v1alpha1/conversion.go @@ -0,0 +1,745 @@ +package v1alpha1 + +import ( + "errors" + "reflect" + unsafe "unsafe" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apimachineryconversion "k8s.io/apimachinery/pkg/conversion" + "k8s.io/utils/ptr" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" //nolint:staticcheck + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// ConvertTo converts this ScalewayCluster (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayCluster) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayCluster) + + return Convert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayCluster (v1alpha1). +func (dst *ScalewayCluster) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayCluster) + + return Convert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster(src, dst, nil) +} + +// ConvertTo converts this ScalewayClusterTemplate (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayClusterTemplate) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayClusterTemplate) + + return Convert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayClusterTemplate (v1alpha1). +func (dst *ScalewayClusterTemplate) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayClusterTemplate) + + return Convert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate(src, dst, nil) +} + +// ConvertTo converts this ScalewayMachine (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayMachine) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayMachine) + + return Convert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayMachine (v1alpha1). +func (dst *ScalewayMachine) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayMachine) + + return Convert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine(src, dst, nil) +} + +// ConvertTo converts this ScalewayMachineTemplate (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayMachineTemplate) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayMachineTemplate) + + return Convert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayMachineTemplate (v1alpha1). +func (dst *ScalewayMachineTemplate) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayMachineTemplate) + + return Convert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate(src, dst, nil) +} + +// ConvertTo converts this ScalewayManagedCluster (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayManagedCluster) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayManagedCluster) + + return Convert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayManagedCluster (v1alpha1). +func (dst *ScalewayManagedCluster) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayManagedCluster) + + return Convert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster(src, dst, nil) +} + +// ConvertTo converts this ScalewayManagedControlPlane (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayManagedControlPlane) + + return Convert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayManagedControlPlane (v1alpha1). +func (dst *ScalewayManagedControlPlane) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayManagedControlPlane) + + return Convert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane(src, dst, nil) +} + +// ConvertTo converts this ScalewayManagedMachinePool (v1alpha1) to the Hub version (v1alpha2). +func (src *ScalewayManagedMachinePool) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*infrav1.ScalewayManagedMachinePool) + + return Convert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool(src, dst, nil) +} + +// ConvertFrom converts the Hub version (v1alpha2) to this ScalewayManagedMachinePool (v1alpha1). +func (dst *ScalewayManagedMachinePool) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*infrav1.ScalewayManagedMachinePool) + + return Convert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool(src, dst, nil) +} + +func Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(in *clusterv1beta1.APIEndpoint, out *clusterv1.APIEndpoint, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(in, out, s) +} + +func Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(in *clusterv1.APIEndpoint, out *clusterv1beta1.APIEndpoint, s apimachineryconversion.Scope) error { + return clusterv1beta1.Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(in, out, s) +} + +func Convert_v1_ObjectMeta_To_v1beta2_ObjectMeta(in *metav1.ObjectMeta, out *clusterv1.ObjectMeta, s apimachineryconversion.Scope) error { + out.Annotations = in.Annotations + out.Labels = in.Labels + + return nil +} + +func Convert_v1beta2_ObjectMeta_To_v1_ObjectMeta(in *clusterv1.ObjectMeta, out *metav1.ObjectMeta, s apimachineryconversion.Scope) error { + out.Annotations = in.Annotations + out.Labels = in.Labels + + return nil +} + +func Convert_v1alpha1_PrivateNetworkSpec_To_v1alpha2_PrivateNetworkSpec(in *PrivateNetworkSpec, out *infrav1.PrivateNetworkSpec, s apimachineryconversion.Scope) error { + if in == nil { + return nil + } + + if err := autoConvert_v1alpha1_PrivateNetworkSpec_To_v1alpha2_PrivateNetworkSpec(in, out, s); err != nil { + return err + } + + out.ID = infrav1.UUID(ptr.Deref(in.ID, "")) + out.VPCID = infrav1.UUID(ptr.Deref(in.VPCID, "")) + out.Subnet = infrav1.CIDR(ptr.Deref(in.Subnet, "")) + + return nil +} + +func Convert_v1alpha2_PrivateNetworkSpec_To_v1alpha1_PrivateNetworkSpec(in *infrav1.PrivateNetworkSpec, out *PrivateNetworkSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_PrivateNetworkSpec_To_v1alpha1_PrivateNetworkSpec(in, out, s); err != nil { + return err + } + + out.ID = ptrIfNotZero(string(in.ID)) + out.VPCID = ptrIfNotZero(string(in.VPCID)) + out.Subnet = ptrIfNotZero(string(in.Subnet)) + + return nil +} + +func Convert_v1alpha1_ScalewayClusterSpec_To_v1alpha2_ScalewayClusterSpec(in *ScalewayClusterSpec, out *infrav1.ScalewayClusterSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayClusterSpec_To_v1alpha2_ScalewayClusterSpec(in, out, nil); err != nil { + return err + } + + if in.Network == nil { + return nil + } + + var privateLB bool + + if in.Network.ControlPlaneLoadBalancer != nil { + out.Network.ControlPlaneLoadBalancer.Zone = infrav1.ScalewayZone(ptr.Deref(in.Network.ControlPlaneLoadBalancer.Zone, "")) + out.Network.ControlPlaneLoadBalancer.Type = ptr.Deref(in.Network.ControlPlaneLoadBalancer.Type, "") + out.Network.ControlPlaneLoadBalancer.IP = infrav1.IPv4(ptr.Deref(in.Network.ControlPlaneLoadBalancer.IP, "")) + out.Network.ControlPlaneLoadBalancer.PrivateIP = infrav1.IPv4(ptr.Deref(in.Network.ControlPlaneLoadBalancer.PrivateIP, "")) + out.Network.ControlPlaneLoadBalancer.AllowedRanges = *(*[]infrav1.CIDR)(unsafe.Pointer(&in.Network.ControlPlaneLoadBalancer.AllowedRanges)) + out.Network.ControlPlaneLoadBalancer.Private = in.Network.ControlPlaneLoadBalancer.Private + privateLB = ptr.Deref(in.Network.ControlPlaneLoadBalancer.Private, false) + } + + for _, lb := range in.Network.ControlPlaneExtraLoadBalancers { + out.Network.ControlPlaneExtraLoadBalancers = append( + out.Network.ControlPlaneExtraLoadBalancers, + infrav1.LoadBalancer{ + Zone: infrav1.ScalewayZone(ptr.Deref(lb.Zone, "")), + Type: ptr.Deref(lb.Type, ""), + IP: infrav1.IPv4(ptr.Deref(lb.IP, "")), + PrivateIP: infrav1.IPv4(ptr.Deref(lb.PrivateIP, "")), + }, + ) + } + + if in.Network.ControlPlaneDNS != nil { + if privateLB { + return errors.New("cluster with controlPlaneDNS and a private LB cannot be converted") + } + + out.Network.ControlPlaneDNS.Domain = in.Network.ControlPlaneDNS.Domain + out.Network.ControlPlaneDNS.Name = in.Network.ControlPlaneDNS.Name + } + + // NOTE: ControlPlanePrivateDNS is merged into ControlPlaneDNS during conversion. + if in.Network.ControlPlanePrivateDNS != nil { + out.Network.ControlPlaneDNS.Name = in.Network.ControlPlanePrivateDNS.Name + } + + if err := Convert_v1alpha1_PrivateNetworkSpec_To_v1alpha2_PrivateNetworkSpec(in.Network.PrivateNetwork, &out.Network.PrivateNetwork, s); err != nil { + return err + } + + for _, pgw := range in.Network.PublicGateways { + out.Network.PublicGateways = append(out.Network.PublicGateways, infrav1.PublicGateway{ + Type: ptr.Deref(pgw.Type, ""), + IP: infrav1.IPv4(ptr.Deref(pgw.IP, "")), + Zone: infrav1.ScalewayZone(ptr.Deref(pgw.Zone, "")), + }) + } + + return nil +} + +func Convert_v1alpha2_ScalewayClusterSpec_To_v1alpha1_ScalewayClusterSpec(in *infrav1.ScalewayClusterSpec, out *ScalewayClusterSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayClusterSpec_To_v1alpha1_ScalewayClusterSpec(in, out, s); err != nil { + return err + } + + // Exit early if in.Network is not defined. + if reflect.DeepEqual(in.Network, infrav1.ScalewayClusterNetwork{}) { + return nil + } + + out.Network = &NetworkSpec{} + + // Used to determine if we should put v1alpha2 ControlPlaneDNS into v1alpha1 ControlPlaneDNS or ControlPlanePrivateDNS. + var privateLB bool + + if !reflect.DeepEqual(in.Network.ControlPlaneLoadBalancer, infrav1.ControlPlaneLoadBalancer{}) { + out.Network.ControlPlaneLoadBalancer = &ControlPlaneLoadBalancerSpec{ + Private: in.Network.ControlPlaneLoadBalancer.Private, + LoadBalancerSpec: LoadBalancerSpec{ + Zone: ptrIfNotZero(string(in.Network.ControlPlaneLoadBalancer.Zone)), + Type: ptrIfNotZero(in.Network.ControlPlaneLoadBalancer.Type), + IP: ptrIfNotZero(string(in.Network.ControlPlaneLoadBalancer.IP)), + PrivateIP: ptrIfNotZero(string(in.Network.ControlPlaneLoadBalancer.PrivateIP)), + }, + AllowedRanges: *(*[]CIDR)(unsafe.Pointer(&in.Network.ControlPlaneLoadBalancer.AllowedRanges)), + } + + privateLB = ptr.Deref(in.Network.ControlPlaneLoadBalancer.Private, false) + } + + // Extra LBs. + for _, lb := range in.Network.ControlPlaneExtraLoadBalancers { + out.Network.ControlPlaneExtraLoadBalancers = append( + out.Network.ControlPlaneExtraLoadBalancers, + LoadBalancerSpec{ + IP: ptrIfNotZero(string(lb.IP)), + Type: ptrIfNotZero(lb.Type), + PrivateIP: ptrIfNotZero(string(lb.PrivateIP)), + Zone: ptrIfNotZero(string(lb.Zone)), + }, + ) + } + + if !reflect.DeepEqual(in.Network.ControlPlaneDNS, infrav1.ControlPlaneDNS{}) { + if privateLB { + out.Network.ControlPlanePrivateDNS = &ControlPlanePrivateDNSSpec{ + Name: in.Network.ControlPlaneDNS.Name, + } + } else { + out.Network.ControlPlaneDNS = &ControlPlaneDNSSpec{ + Domain: in.Network.ControlPlaneDNS.Domain, + Name: in.Network.ControlPlaneDNS.Name, + } + } + } + + if !reflect.DeepEqual(in.Network.PrivateNetwork, infrav1.PrivateNetworkSpec{}) { + out.Network.PrivateNetwork = &PrivateNetworkSpec{} + if err := Convert_v1alpha2_PrivateNetworkSpec_To_v1alpha1_PrivateNetworkSpec(&in.Network.PrivateNetwork, out.Network.PrivateNetwork, s); err != nil { + return err + } + } + + for _, pgw := range in.Network.PublicGateways { + out.Network.PublicGateways = append(out.Network.PublicGateways, PublicGatewaySpec{ + IP: ptrIfNotZero(string(pgw.IP)), + Type: ptrIfNotZero(pgw.Type), + Zone: ptrIfNotZero(string(pgw.Zone)), + }) + } + + return nil +} + +func Convert_v1alpha1_ScalewayClusterStatus_To_v1alpha2_ScalewayClusterStatus(in *ScalewayClusterStatus, out *infrav1.ScalewayClusterStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayClusterStatus_To_v1alpha2_ScalewayClusterStatus(in, out, s); err != nil { + return err + } + + for name, fd := range in.FailureDomains { + out.FailureDomains = append(out.FailureDomains, clusterv1.FailureDomain{ + Name: name, + ControlPlane: &fd.ControlPlane, + Attributes: fd.Attributes, + }) + } + + out.Initialization.Provisioned = &in.Ready + + if in.Network != nil { + out.Network.VPCID = infrav1.UUID(ptr.Deref(in.Network.VPCID, "")) + out.Network.PrivateNetworkID = infrav1.UUID(ptr.Deref(in.Network.PrivateNetworkID, "")) + out.Network.PublicGatewayIDs = *(*[]infrav1.UUID)(unsafe.Pointer(&in.Network.PublicGatewayIDs)) + out.Network.LoadBalancerIP = infrav1.IPv4(ptr.Deref(in.Network.LoadBalancerIP, "")) + out.Network.ExtraLoadBalancerIPs = *(*[]infrav1.IPv4)(unsafe.Pointer(&in.Network.ExtraLoadBalancerIPs)) + } + + return nil +} + +func Convert_v1alpha2_ScalewayClusterStatus_To_v1alpha1_ScalewayClusterStatus(in *infrav1.ScalewayClusterStatus, out *ScalewayClusterStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayClusterStatus_To_v1alpha1_ScalewayClusterStatus(in, out, s); err != nil { + return err + } + + for _, fd := range in.FailureDomains { + if out.FailureDomains == nil { + out.FailureDomains = make(clusterv1beta1.FailureDomains) + } + + out.FailureDomains[fd.Name] = clusterv1beta1.FailureDomainSpec{ + ControlPlane: ptr.Deref(fd.ControlPlane, false), + Attributes: fd.Attributes, + } + } + + out.Ready = ptr.Deref(in.Initialization.Provisioned, false) + + if !reflect.DeepEqual(in.Network, infrav1.ScalewayClusterNetworkStatus{}) { + out.Network = &NetworkStatus{ + VPCID: ptrIfNotZero(string(in.Network.VPCID)), + PrivateNetworkID: ptrIfNotZero(string(in.Network.PrivateNetworkID)), + LoadBalancerIP: ptrIfNotZero(string(in.Network.LoadBalancerIP)), + PublicGatewayIDs: *(*[]string)(unsafe.Pointer(&in.Network.PublicGatewayIDs)), + ExtraLoadBalancerIPs: *(*[]string)(unsafe.Pointer(&in.Network.ExtraLoadBalancerIPs)), + } + } + + return nil +} + +func Convert_v1alpha1_ScalewayMachineSpec_To_v1alpha2_ScalewayMachineSpec(in *ScalewayMachineSpec, out *infrav1.ScalewayMachineSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayMachineSpec_To_v1alpha2_ScalewayMachineSpec(in, out, s); err != nil { + return err + } + + if in.RootVolume != nil { + out.RootVolume.IOPS = ptr.Deref(in.RootVolume.IOPS, 0) + out.RootVolume.Size = ptr.Deref(in.RootVolume.Size, 0) + out.RootVolume.Type = ptr.Deref(in.RootVolume.Type, "") + } + + if in.PublicNetwork != nil { + out.PublicNetwork.EnableIPv4 = in.PublicNetwork.EnableIPv4 + out.PublicNetwork.EnableIPv6 = in.PublicNetwork.EnableIPv6 + } + + if in.PlacementGroup != nil { + out.PlacementGroup = infrav1.IDOrName{ + ID: infrav1.UUID(ptr.Deref(in.PlacementGroup.ID, "")), + Name: ptr.Deref(in.PlacementGroup.Name, ""), + } + } + + if in.SecurityGroup != nil { + out.SecurityGroup = infrav1.IDOrName{ + ID: infrav1.UUID(ptr.Deref(in.SecurityGroup.ID, "")), + Name: ptr.Deref(in.SecurityGroup.Name, ""), + } + } + + return nil +} + +func Convert_v1alpha2_ScalewayMachineSpec_To_v1alpha1_ScalewayMachineSpec(in *infrav1.ScalewayMachineSpec, out *ScalewayMachineSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayMachineSpec_To_v1alpha1_ScalewayMachineSpec(in, out, s); err != nil { + return err + } + + if !reflect.DeepEqual(in.RootVolume, infrav1.RootVolume{}) { + out.RootVolume = &RootVolumeSpec{ + Size: ptrIfNotZero(in.RootVolume.Size), + Type: ptrIfNotZero(in.RootVolume.Type), + IOPS: ptrIfNotZero(in.RootVolume.IOPS), + } + } + + if !reflect.DeepEqual(in.PublicNetwork, infrav1.PublicNetwork{}) { + out.PublicNetwork = &PublicNetworkSpec{ + EnableIPv4: in.PublicNetwork.EnableIPv4, + EnableIPv6: in.PublicNetwork.EnableIPv6, + } + } + + if !reflect.DeepEqual(in.PlacementGroup, infrav1.IDOrName{}) { + out.PlacementGroup = &PlacementGroupSpec{ + ID: ptrIfNotZero(string(in.PlacementGroup.ID)), + Name: ptrIfNotZero(in.PlacementGroup.Name), + } + } + + if !reflect.DeepEqual(in.SecurityGroup, infrav1.IDOrName{}) { + out.SecurityGroup = &SecurityGroupSpec{ + ID: ptrIfNotZero(string(in.SecurityGroup.ID)), + Name: ptrIfNotZero(in.SecurityGroup.Name), + } + } + + return nil +} +func Convert_v1alpha1_ScalewayMachineStatus_To_v1alpha2_ScalewayMachineStatus(in *ScalewayMachineStatus, out *infrav1.ScalewayMachineStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayMachineStatus_To_v1alpha2_ScalewayMachineStatus(in, out, s); err != nil { + return err + } + + out.Initialization.Provisioned = &in.Ready + + return nil +} + +func Convert_v1alpha2_ScalewayMachineStatus_To_v1alpha1_ScalewayMachineStatus(in *infrav1.ScalewayMachineStatus, out *ScalewayMachineStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayMachineStatus_To_v1alpha1_ScalewayMachineStatus(in, out, s); err != nil { + return err + } + + out.Ready = ptr.Deref(in.Initialization.Provisioned, false) + + return nil +} +func Convert_v1alpha1_ScalewayManagedClusterSpec_To_v1alpha2_ScalewayManagedClusterSpec(in *ScalewayManagedClusterSpec, out *infrav1.ScalewayManagedClusterSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayManagedClusterSpec_To_v1alpha2_ScalewayManagedClusterSpec(in, out, s); err != nil { + return err + } + + if in.Network == nil { + return nil + } + + if in.Network.PrivateNetwork != nil { + out.Network.PrivateNetwork.ID = infrav1.UUID(ptr.Deref(in.Network.PrivateNetwork.ID, "")) + out.Network.PrivateNetwork.Subnet = infrav1.CIDR(ptr.Deref(in.Network.PrivateNetwork.Subnet, "")) + out.Network.PrivateNetwork.VPCID = infrav1.UUID(ptr.Deref(in.Network.PrivateNetwork.VPCID, "")) + } + + for _, pgw := range in.Network.PublicGateways { + out.Network.PublicGateways = append(out.Network.PublicGateways, infrav1.PublicGateway{ + Type: ptr.Deref(pgw.Type, ""), + IP: infrav1.IPv4(ptr.Deref(pgw.IP, "")), + Zone: infrav1.ScalewayZone(ptr.Deref(pgw.Zone, "")), + }) + } + + return nil +} + +func Convert_v1alpha2_ScalewayManagedClusterSpec_To_v1alpha1_ScalewayManagedClusterSpec(in *infrav1.ScalewayManagedClusterSpec, out *ScalewayManagedClusterSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayManagedClusterSpec_To_v1alpha1_ScalewayManagedClusterSpec(in, out, s); err != nil { + return err + } + + if reflect.DeepEqual(in.Network, infrav1.ScalewayManagedClusterNetwork{}) { + return nil + } + + out.Network = &ManagedNetworkSpec{} + + if !reflect.DeepEqual(in.Network.PrivateNetwork, infrav1.PrivateNetwork{}) { + out.Network.PrivateNetwork = &PrivateNetworkParams{ + ID: ptrIfNotZero(string(in.Network.PrivateNetwork.ID)), + VPCID: ptrIfNotZero(string(in.Network.PrivateNetwork.VPCID)), + Subnet: ptrIfNotZero(string(in.Network.PrivateNetwork.Subnet)), + } + } + + for _, pgw := range in.Network.PublicGateways { + out.Network.PublicGateways = append(out.Network.PublicGateways, PublicGatewaySpec{ + Type: ptrIfNotZero(pgw.Type), + IP: ptrIfNotZero(string(pgw.IP)), + Zone: ptrIfNotZero(string(pgw.Zone)), + }) + } + + return nil +} + +func Convert_v1alpha1_ScalewayManagedClusterStatus_To_v1alpha2_ScalewayManagedClusterStatus(in *ScalewayManagedClusterStatus, out *infrav1.ScalewayManagedClusterStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayManagedClusterStatus_To_v1alpha2_ScalewayManagedClusterStatus(in, out, s); err != nil { + return err + } + + out.Initialization.Provisioned = &in.Ready + + if in.Network != nil { + out.Network.PrivateNetworkID = infrav1.UUID(ptr.Deref(in.Network.PrivateNetworkID, "")) + } + + return nil +} + +func Convert_v1alpha2_ScalewayManagedClusterStatus_To_v1alpha1_ScalewayManagedClusterStatus(in *infrav1.ScalewayManagedClusterStatus, out *ScalewayManagedClusterStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayManagedClusterStatus_To_v1alpha1_ScalewayManagedClusterStatus(in, out, s); err != nil { + return err + } + + out.Ready = ptr.Deref(in.Initialization.Provisioned, false) + + if in.Network.PrivateNetworkID != "" { + out.Network = &ManagedNetworkStatus{ + PrivateNetworkID: ptrIfNotZero(string(in.Network.PrivateNetworkID)), + } + } + + return nil +} + +func Convert_v1alpha1_ScalewayManagedControlPlaneSpec_To_v1alpha2_ScalewayManagedControlPlaneSpec(in *ScalewayManagedControlPlaneSpec, out *infrav1.ScalewayManagedControlPlaneSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayManagedControlPlaneSpec_To_v1alpha2_ScalewayManagedControlPlaneSpec(in, out, s); err != nil { + return err + } + + if in.Autoscaler != nil { + out.Autoscaler.ScaleDownDisabled = in.Autoscaler.ScaleDownDisabled + out.Autoscaler.ScaleDownDelayAfterAdd = ptr.Deref(in.Autoscaler.ScaleDownDelayAfterAdd, "") + out.Autoscaler.Estimator = ptr.Deref(in.Autoscaler.Estimator, "") + out.Autoscaler.Expander = ptr.Deref(in.Autoscaler.Expander, "") + out.Autoscaler.IgnoreDaemonsetsUtilization = in.Autoscaler.IgnoreDaemonsetsUtilization + out.Autoscaler.BalanceSimilarNodeGroups = in.Autoscaler.BalanceSimilarNodeGroups + out.Autoscaler.ExpendablePodsPriorityCutoff = in.Autoscaler.ExpendablePodsPriorityCutoff + out.Autoscaler.ScaleDownUnneededTime = ptr.Deref(in.Autoscaler.ScaleDownUnneededTime, "") + out.Autoscaler.ScaleDownUtilizationThreshold = ptr.Deref(in.Autoscaler.ScaleDownUtilizationThreshold, "") + out.Autoscaler.MaxGracefulTerminationSec = ptr.Deref(in.Autoscaler.MaxGracefulTerminationSec, 0) + } + + if in.AutoUpgrade != nil { + out.AutoUpgrade.Enabled = &in.AutoUpgrade.Enabled + + if in.AutoUpgrade.MaintenanceWindow != nil { + out.AutoUpgrade.MaintenanceWindow.Day = ptr.Deref(in.AutoUpgrade.MaintenanceWindow.Day, "") + out.AutoUpgrade.MaintenanceWindow.StartHour = in.AutoUpgrade.MaintenanceWindow.StartHour + } + } + + if in.OpenIDConnect != nil { + out.OpenIDConnect.IssuerURL = in.OpenIDConnect.IssuerURL + out.OpenIDConnect.ClientID = in.OpenIDConnect.ClientID + out.OpenIDConnect.UsernameClaim = ptr.Deref(in.OpenIDConnect.UsernameClaim, "") + out.OpenIDConnect.UsernamePrefix = ptr.Deref(in.OpenIDConnect.UsernamePrefix, "") + out.OpenIDConnect.GroupsClaim = in.OpenIDConnect.GroupsClaim + out.OpenIDConnect.GroupsPrefix = ptr.Deref(in.OpenIDConnect.GroupsPrefix, "") + out.OpenIDConnect.RequiredClaim = in.OpenIDConnect.RequiredClaim + } + + if in.OnDelete != nil { + out.OnDelete.WithAdditionalResources = in.OnDelete.WithAdditionalResources + } + + return nil +} + +func Convert_v1alpha2_ScalewayManagedControlPlaneSpec_To_v1alpha1_ScalewayManagedControlPlaneSpec(in *infrav1.ScalewayManagedControlPlaneSpec, out *ScalewayManagedControlPlaneSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayManagedControlPlaneSpec_To_v1alpha1_ScalewayManagedControlPlaneSpec(in, out, s); err != nil { + return err + } + + if !reflect.DeepEqual(in.Autoscaler, infrav1.Autoscaler{}) { + out.Autoscaler = &AutoscalerSpec{ + ScaleDownDisabled: in.Autoscaler.ScaleDownDisabled, + ScaleDownDelayAfterAdd: ptrIfNotZero(in.Autoscaler.ScaleDownDelayAfterAdd), + Estimator: ptrIfNotZero(in.Autoscaler.Estimator), + Expander: ptrIfNotZero(in.Autoscaler.Expander), + IgnoreDaemonsetsUtilization: in.Autoscaler.IgnoreDaemonsetsUtilization, + BalanceSimilarNodeGroups: in.Autoscaler.BalanceSimilarNodeGroups, + ExpendablePodsPriorityCutoff: in.Autoscaler.ExpendablePodsPriorityCutoff, + ScaleDownUnneededTime: ptrIfNotZero(in.Autoscaler.ScaleDownUnneededTime), + ScaleDownUtilizationThreshold: ptrIfNotZero(in.Autoscaler.ScaleDownUtilizationThreshold), + MaxGracefulTerminationSec: ptrIfNotZero(in.Autoscaler.MaxGracefulTerminationSec), + } + } + + if !reflect.DeepEqual(in.AutoUpgrade, infrav1.AutoUpgrade{}) { + out.AutoUpgrade = &AutoUpgradeSpec{ + Enabled: ptr.Deref(in.AutoUpgrade.Enabled, false), + } + + if !reflect.DeepEqual(in.AutoUpgrade.MaintenanceWindow, infrav1.MaintenanceWindow{}) { + out.AutoUpgrade.MaintenanceWindow = &MaintenanceWindowSpec{ + StartHour: in.AutoUpgrade.MaintenanceWindow.StartHour, + Day: ptrIfNotZero(in.AutoUpgrade.MaintenanceWindow.Day), + } + } + } + + if !reflect.DeepEqual(in.OpenIDConnect, infrav1.OpenIDConnect{}) { + out.OpenIDConnect = &OpenIDConnectSpec{ + IssuerURL: in.OpenIDConnect.IssuerURL, + ClientID: in.OpenIDConnect.ClientID, + UsernameClaim: ptrIfNotZero(in.OpenIDConnect.UsernameClaim), + UsernamePrefix: ptrIfNotZero(in.OpenIDConnect.UsernamePrefix), + GroupsClaim: in.OpenIDConnect.GroupsClaim, + GroupsPrefix: ptrIfNotZero(in.OpenIDConnect.GroupsPrefix), + RequiredClaim: in.OpenIDConnect.RequiredClaim, + } + } + + if !reflect.DeepEqual(in.OnDelete, infrav1.OnDelete{}) { + out.OnDelete = &OnDeleteSpec{ + WithAdditionalResources: in.OnDelete.WithAdditionalResources, + } + } + + return nil +} + +func Convert_v1alpha1_ScalewayManagedControlPlaneStatus_To_v1alpha2_ScalewayManagedControlPlaneStatus(in *ScalewayManagedControlPlaneStatus, out *infrav1.ScalewayManagedControlPlaneStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayManagedControlPlaneStatus_To_v1alpha2_ScalewayManagedControlPlaneStatus(in, out, s); err != nil { + return err + } + + // Drop in.Ready + + out.Initialization.ControlPlaneInitialized = &in.Initialized + + return nil +} + +func Convert_v1alpha2_ScalewayManagedControlPlaneStatus_To_v1alpha1_ScalewayManagedControlPlaneStatus(in *infrav1.ScalewayManagedControlPlaneStatus, out *ScalewayManagedControlPlaneStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayManagedControlPlaneStatus_To_v1alpha1_ScalewayManagedControlPlaneStatus(in, out, s); err != nil { + return err + } + + out.Ready = ptr.Deref(in.Initialization.ControlPlaneInitialized, false) + out.Initialized = ptr.Deref(in.Initialization.ControlPlaneInitialized, false) + + return nil +} + +func Convert_v1alpha1_ScalewayManagedMachinePoolSpec_To_v1alpha2_ScalewayManagedMachinePoolSpec(in *ScalewayManagedMachinePoolSpec, out *infrav1.ScalewayManagedMachinePoolSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayManagedMachinePoolSpec_To_v1alpha2_ScalewayManagedMachinePoolSpec(in, out, s); err != nil { + return err + } + + out.PlacementGroupID = infrav1.UUID(ptr.Deref(in.PlacementGroupID, "")) + out.SecurityGroupID = infrav1.UUID(ptr.Deref(in.SecurityGroupID, "")) + + if in.Scaling != nil { + out.Scaling.Autoscaling = in.Scaling.Autoscaling + out.Scaling.MinSize = in.Scaling.MinSize + out.Scaling.MaxSize = in.Scaling.MaxSize + } + + if in.UpgradePolicy != nil { + out.UpgradePolicy.MaxSurge = in.UpgradePolicy.MaxSurge + out.UpgradePolicy.MaxUnavailable = in.UpgradePolicy.MaxUnavailable + } + + return nil +} + +func Convert_v1alpha2_ScalewayManagedMachinePoolSpec_To_v1alpha1_ScalewayManagedMachinePoolSpec(in *infrav1.ScalewayManagedMachinePoolSpec, out *ScalewayManagedMachinePoolSpec, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayManagedMachinePoolSpec_To_v1alpha1_ScalewayManagedMachinePoolSpec(in, out, s); err != nil { + return err + } + + out.PlacementGroupID = ptrIfNotZero(string(in.PlacementGroupID)) + out.SecurityGroupID = ptrIfNotZero(string(in.SecurityGroupID)) + + if !reflect.DeepEqual(in.Scaling, infrav1.Scaling{}) { + out.Scaling = &ScalingSpec{ + Autoscaling: in.Scaling.Autoscaling, + MinSize: in.Scaling.MinSize, + MaxSize: in.Scaling.MaxSize, + } + } + + if !reflect.DeepEqual(in.UpgradePolicy, infrav1.UpgradePolicy{}) { + out.UpgradePolicy = &UpgradePolicySpec{ + MaxSurge: in.UpgradePolicy.MaxSurge, + MaxUnavailable: in.UpgradePolicy.MaxUnavailable, + } + } + + return nil +} + +func Convert_v1alpha1_ScalewayManagedMachinePoolStatus_To_v1alpha2_ScalewayManagedMachinePoolStatus(in *ScalewayManagedMachinePoolStatus, out *infrav1.ScalewayManagedMachinePoolStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha1_ScalewayManagedMachinePoolStatus_To_v1alpha2_ScalewayManagedMachinePoolStatus(in, out, s); err != nil { + return err + } + + out.Initialization.Provisioned = &in.Ready + + return nil +} +func Convert_v1alpha2_ScalewayManagedMachinePoolStatus_To_v1alpha1_ScalewayManagedMachinePoolStatus(in *infrav1.ScalewayManagedMachinePoolStatus, out *ScalewayManagedMachinePoolStatus, s apimachineryconversion.Scope) error { + if err := autoConvert_v1alpha2_ScalewayManagedMachinePoolStatus_To_v1alpha1_ScalewayManagedMachinePoolStatus(in, out, s); err != nil { + return err + } + + out.Ready = ptr.Deref(in.Initialization.Provisioned, false) + + return nil +} + +func Convert_v1alpha1_ImageSpec_To_v1alpha2_Image(in *ImageSpec, out *infrav1.Image, s apimachineryconversion.Scope) error { + out.ID = infrav1.UUID(ptr.Deref(in.ID, "")) + out.Name = ptr.Deref(in.Name, "") + out.Label = ptr.Deref(in.Label, "") + + return nil +} +func Convert_v1alpha2_Image_To_v1alpha1_ImageSpec(in *infrav1.Image, out *ImageSpec, s apimachineryconversion.Scope) error { + out.ID = ptrIfNotZero(string(in.ID)) + out.Label = ptrIfNotZero(in.Label) + out.Name = ptrIfNotZero(in.Name) + + return nil +} + +// ptrIfNotZero returns a pointer to v if v is NOT a zero value. Otherwise, it returns nil. +func ptrIfNotZero[T comparable](v T) *T { + var e T + + if v == e { + return nil + } + + return &v +} diff --git a/api/v1alpha1/conversion_test.go b/api/v1alpha1/conversion_test.go new file mode 100644 index 0000000..08c0322 --- /dev/null +++ b/api/v1alpha1/conversion_test.go @@ -0,0 +1,36 @@ +package v1alpha1 + +import ( + "reflect" + "testing" + + "k8s.io/utils/ptr" +) + +func Test_ptrIfNotZero(t *testing.T) { + tests := []struct { + name string // description of this test case + // Named input parameters for target function. + v string + want *string + }{ + { + name: "empty value", + v: "", + want: nil, + }, + { + name: "value not empty", + v: "test", + want: ptr.To("test"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := ptrIfNotZero(tt.v) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ptrIfNotZero() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/v1alpha1/doc.go b/api/v1alpha1/doc.go new file mode 100644 index 0000000..4a27e85 --- /dev/null +++ b/api/v1alpha1/doc.go @@ -0,0 +1,7 @@ +// Package v1alpha1 contains API Schema definitions for the infrastructure v1alpha1 API group. +// +k8s:conversion-gen=github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2 +// +kubebuilder:object:generate=true +// +groupName=infrastructure.cluster.x-k8s.io +// +// Deprecated: This package is deprecated and is going to be removed when support for v1alpha1 will be dropped. +package v1alpha1 diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index cf0b84f..7dde24a 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -1,20 +1,29 @@ -// Package v1alpha1 contains API Schema definitions for the infrastructure v1alpha1 API group. -// +kubebuilder:object:generate=true -// +groupName=infrastructure.cluster.x-k8s.io package v1alpha1 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" "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: "infrastructure.cluster.x-k8s.io", Version: "v1alpha1"} - // SchemeBuilder is used to add go types to the GroupVersionKind scheme. - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + // schemeBuilder is used to add go types to the GroupVersionKind scheme. + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme + AddToScheme = schemeBuilder.AddToScheme + + objectTypes = []runtime.Object{} + + // localSchemeBuilder is used for type conversions. + localSchemeBuilder = &schemeBuilder ) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, objectTypes...) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/api/v1alpha1/scalewaycluster_types.go b/api/v1alpha1/scalewaycluster_types.go index 6a530ad..2d15d0a 100644 --- a/api/v1alpha1/scalewaycluster_types.go +++ b/api/v1alpha1/scalewaycluster_types.go @@ -2,7 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" //nolint:staticcheck ) const ClusterFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/sc-protection" @@ -48,7 +48,7 @@ type ScalewayClusterSpec struct { // ControlPlaneEndpoint represents the endpoint used to communicate with the control plane. // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" // +optional - ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty"` + ControlPlaneEndpoint clusterv1beta1.APIEndpoint `json:"controlPlaneEndpoint,omitempty"` } // NetworkSpec defines network specific settings. @@ -190,7 +190,7 @@ type ScalewayClusterStatus struct { // FailureDomains is a list of failure domain objects synced from the infrastructure provider. // +optional - FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"` + FailureDomains clusterv1beta1.FailureDomains `json:"failureDomains,omitempty"` } // NetworkStatus contains information about network resources of the cluster. @@ -223,7 +223,7 @@ type NetworkStatus struct { // +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.region",description="Region of the cluster" // +kubebuilder:printcolumn:name="Ready",type="boolean",JSONPath=".status.ready",description="Ready is true when the cluster is fully provisioned" // +kubebuilder:resource:path=scalewayclusters,scope=Namespaced,categories=cluster-api,shortName=sc -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // ScalewayCluster is the Schema for the scalewayclusters API. // +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" @@ -246,5 +246,5 @@ type ScalewayClusterList struct { } func init() { - SchemeBuilder.Register(&ScalewayCluster{}, &ScalewayClusterList{}) + objectTypes = append(objectTypes, &ScalewayCluster{}, &ScalewayClusterList{}) } diff --git a/api/v1alpha1/scalewayclustertemplate_types.go b/api/v1alpha1/scalewayclustertemplate_types.go index 571762a..923b111 100644 --- a/api/v1alpha1/scalewayclustertemplate_types.go +++ b/api/v1alpha1/scalewayclustertemplate_types.go @@ -19,7 +19,7 @@ type ScalewayClusterTemplateResource struct { // +kubebuilder:object:root=true // +kubebuilder:resource:path=scalewayclustertemplates,scope=Namespaced,categories=cluster-api,shortName=sct -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // ScalewayClusterTemplate is the Schema for the scalewayclustertemplates API. type ScalewayClusterTemplate struct { @@ -39,5 +39,5 @@ type ScalewayClusterTemplateList struct { } func init() { - SchemeBuilder.Register(&ScalewayClusterTemplate{}, &ScalewayClusterTemplateList{}) + objectTypes = append(objectTypes, &ScalewayClusterTemplate{}, &ScalewayClusterTemplateList{}) } diff --git a/api/v1alpha1/scalewaymachine_types.go b/api/v1alpha1/scalewaymachine_types.go index a91afe4..da749ec 100644 --- a/api/v1alpha1/scalewaymachine_types.go +++ b/api/v1alpha1/scalewaymachine_types.go @@ -2,7 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" //nolint:staticcheck ) const MachineFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/sm-protection" @@ -115,7 +115,7 @@ type ImageSpec struct { type ScalewayMachineStatus struct { // Addresses contains the associated addresses for the machine. // +optional - Addresses []clusterv1.MachineAddress `json:"addresses,omitempty"` + Addresses []clusterv1beta1.MachineAddress `json:"addresses,omitempty"` // Ready denotes that the Scaleway machine infrastructure is fully provisioned. // NOTE: this field is part of the Cluster API contract and it is used to orchestrate provisioning. @@ -131,7 +131,7 @@ type ScalewayMachineStatus struct { // +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="Node provider ID" // +kubebuilder:printcolumn:name="Ready",type="boolean",JSONPath=".status.ready",description="Indicates whether the Scaleway machine is ready" // +kubebuilder:resource:path=scalewaymachines,scope=Namespaced,categories=cluster-api,shortName=sm -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // ScalewayMachine is the Schema for the scalewaymachines API. // +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" @@ -155,5 +155,5 @@ type ScalewayMachineList struct { } func init() { - SchemeBuilder.Register(&ScalewayMachine{}, &ScalewayMachineList{}) + objectTypes = append(objectTypes, &ScalewayMachine{}, &ScalewayMachineList{}) } diff --git a/api/v1alpha1/scalewaymachinetemplate_types.go b/api/v1alpha1/scalewaymachinetemplate_types.go index 59f3396..ae651b6 100644 --- a/api/v1alpha1/scalewaymachinetemplate_types.go +++ b/api/v1alpha1/scalewaymachinetemplate_types.go @@ -19,7 +19,7 @@ type ScalewayMachineTemplateSpec struct { // +kubebuilder:object:root=true // +kubebuilder:resource:path=scalewaymachinetemplates,scope=Namespaced,categories=cluster-api,shortName=smt -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // ScalewayMachineTemplate is the Schema for the scalewaymachinetemplates API. type ScalewayMachineTemplate struct { @@ -39,5 +39,5 @@ type ScalewayMachineTemplateList struct { } func init() { - SchemeBuilder.Register(&ScalewayMachineTemplate{}, &ScalewayMachineTemplateList{}) + objectTypes = append(objectTypes, &ScalewayMachineTemplate{}, &ScalewayMachineTemplateList{}) } diff --git a/api/v1alpha1/scalewaymanagedcluster_types.go b/api/v1alpha1/scalewaymanagedcluster_types.go index c3d0da6..05b6e65 100644 --- a/api/v1alpha1/scalewaymanagedcluster_types.go +++ b/api/v1alpha1/scalewaymanagedcluster_types.go @@ -2,7 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" //nolint:staticcheck ) const ManagedClusterFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/smc-protection" @@ -35,7 +35,7 @@ type ScalewayManagedClusterSpec struct { // ControlPlaneEndpoint represents the endpoint used to communicate with the control plane. // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" // +optional - ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty,omitzero"` + ControlPlaneEndpoint clusterv1beta1.APIEndpoint `json:"controlPlaneEndpoint,omitempty,omitzero"` } // ManagedNetworkSpec defines the network configuration of a managed cluster. @@ -79,7 +79,7 @@ type ManagedNetworkStatus struct { // +kubebuilder:object:root=true // +kubebuilder:resource:path=scalewaymanagedclusters,scope=Namespaced,categories=cluster-api,shortName=smc // +kubebuilder:subresource:status -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this ScalewayManagedCluster belongs" // +kubebuilder:printcolumn:name="Ready",type="boolean",JSONPath=".status.ready",description="Ready is true when the managed cluster is fully provisioned" // +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.region",description="Region of the managed cluster" @@ -115,5 +115,5 @@ type ScalewayManagedClusterList struct { } func init() { - SchemeBuilder.Register(&ScalewayManagedCluster{}, &ScalewayManagedClusterList{}) + objectTypes = append(objectTypes, &ScalewayManagedCluster{}, &ScalewayManagedClusterList{}) } diff --git a/api/v1alpha1/scalewaymanagedcontrolplane_types.go b/api/v1alpha1/scalewaymanagedcontrolplane_types.go index 34ae4d2..ab8e415 100644 --- a/api/v1alpha1/scalewaymanagedcontrolplane_types.go +++ b/api/v1alpha1/scalewaymanagedcontrolplane_types.go @@ -2,7 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" //nolint:staticcheck ) const ManagedControlPlaneFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/smcp-protection" @@ -82,7 +82,7 @@ type ScalewayManagedControlPlaneSpec struct { // ControlPlaneEndpoint represents the endpoint used to communicate with the control plane. // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" // +optional - ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty"` + ControlPlaneEndpoint clusterv1beta1.APIEndpoint `json:"controlPlaneEndpoint,omitempty"` } // OnDeleteSpec configures the settings to apply when deleting the Scaleway managed cluster. @@ -215,7 +215,6 @@ type ScalewayManagedControlPlaneStatus struct { // Initialized is true when the control plane is available for initial contact. // This may occur before the control plane is fully ready. - // In the AzureManagedControlPlane implementation, these are identical. // +optional Initialized bool `json:"initialized,omitempty"` @@ -244,7 +243,7 @@ type ACLSpec struct { // +kubebuilder:object:root=true // +kubebuilder:resource:path=scalewaymanagedcontrolplanes,scope=Namespaced,categories=cluster-api,shortName=smcp // +kubebuilder:subresource:status -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this ScalewayManagedControlPlane belongs" // +kubebuilder:printcolumn:name="Ready",type="boolean",JSONPath=".status.ready",description="Ready is true when the managed cluster is fully provisioned" // +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description="The Kubernetes version of the Scaleway control plane" @@ -280,5 +279,5 @@ type ScalewayManagedControlPlaneList struct { } func init() { - SchemeBuilder.Register(&ScalewayManagedControlPlane{}, &ScalewayManagedControlPlaneList{}) + objectTypes = append(objectTypes, &ScalewayManagedControlPlane{}, &ScalewayManagedControlPlaneList{}) } diff --git a/api/v1alpha1/scalewaymanagedmachinepool_types.go b/api/v1alpha1/scalewaymanagedmachinepool_types.go index 7af8623..9eaa109 100644 --- a/api/v1alpha1/scalewaymanagedmachinepool_types.go +++ b/api/v1alpha1/scalewaymanagedmachinepool_types.go @@ -128,7 +128,7 @@ type ScalewayManagedMachinePoolStatus struct { // +kubebuilder:object:root=true // +kubebuilder:resource:path=scalewaymanagedmachinepools,scope=Namespaced,categories=cluster-api,shortName=smmp // +kubebuilder:subresource:status -// +kubebuilder:storageversion +// +kubebuilder:deprecatedversion // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready" // +kubebuilder:printcolumn:name="Replicas",type="string",JSONPath=".status.replicas" @@ -161,5 +161,5 @@ type ScalewayManagedMachinePoolList struct { } func init() { - SchemeBuilder.Register(&ScalewayManagedMachinePool{}, &ScalewayManagedMachinePoolList{}) + objectTypes = append(objectTypes, &ScalewayManagedMachinePool{}, &ScalewayManagedMachinePoolList{}) } diff --git a/api/v1alpha1/zz_generated.conversion.go b/api/v1alpha1/zz_generated.conversion.go new file mode 100644 index 0000000..368758e --- /dev/null +++ b/api/v1alpha1/zz_generated.conversion.go @@ -0,0 +1,1232 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + unsafe "unsafe" + + v1alpha2 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + v1beta1 "sigs.k8s.io/cluster-api/api/core/v1beta1" + v1beta2 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +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((*ScalewayCluster)(nil), (*v1alpha2.ScalewayCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster(a.(*ScalewayCluster), b.(*v1alpha2.ScalewayCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayCluster)(nil), (*ScalewayCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster(a.(*v1alpha2.ScalewayCluster), b.(*ScalewayCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayClusterList)(nil), (*v1alpha2.ScalewayClusterList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterList_To_v1alpha2_ScalewayClusterList(a.(*ScalewayClusterList), b.(*v1alpha2.ScalewayClusterList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayClusterList)(nil), (*ScalewayClusterList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterList_To_v1alpha1_ScalewayClusterList(a.(*v1alpha2.ScalewayClusterList), b.(*ScalewayClusterList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayClusterTemplate)(nil), (*v1alpha2.ScalewayClusterTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate(a.(*ScalewayClusterTemplate), b.(*v1alpha2.ScalewayClusterTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayClusterTemplate)(nil), (*ScalewayClusterTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate(a.(*v1alpha2.ScalewayClusterTemplate), b.(*ScalewayClusterTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayClusterTemplateList)(nil), (*v1alpha2.ScalewayClusterTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterTemplateList_To_v1alpha2_ScalewayClusterTemplateList(a.(*ScalewayClusterTemplateList), b.(*v1alpha2.ScalewayClusterTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayClusterTemplateList)(nil), (*ScalewayClusterTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterTemplateList_To_v1alpha1_ScalewayClusterTemplateList(a.(*v1alpha2.ScalewayClusterTemplateList), b.(*ScalewayClusterTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayClusterTemplateResource)(nil), (*v1alpha2.ScalewayClusterTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterTemplateResource_To_v1alpha2_ScalewayClusterTemplateResource(a.(*ScalewayClusterTemplateResource), b.(*v1alpha2.ScalewayClusterTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayClusterTemplateResource)(nil), (*ScalewayClusterTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterTemplateResource_To_v1alpha1_ScalewayClusterTemplateResource(a.(*v1alpha2.ScalewayClusterTemplateResource), b.(*ScalewayClusterTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayClusterTemplateSpec)(nil), (*v1alpha2.ScalewayClusterTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterTemplateSpec_To_v1alpha2_ScalewayClusterTemplateSpec(a.(*ScalewayClusterTemplateSpec), b.(*v1alpha2.ScalewayClusterTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayClusterTemplateSpec)(nil), (*ScalewayClusterTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterTemplateSpec_To_v1alpha1_ScalewayClusterTemplateSpec(a.(*v1alpha2.ScalewayClusterTemplateSpec), b.(*ScalewayClusterTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayMachine)(nil), (*v1alpha2.ScalewayMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine(a.(*ScalewayMachine), b.(*v1alpha2.ScalewayMachine), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayMachine)(nil), (*ScalewayMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine(a.(*v1alpha2.ScalewayMachine), b.(*ScalewayMachine), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayMachineList)(nil), (*v1alpha2.ScalewayMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineList_To_v1alpha2_ScalewayMachineList(a.(*ScalewayMachineList), b.(*v1alpha2.ScalewayMachineList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayMachineList)(nil), (*ScalewayMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineList_To_v1alpha1_ScalewayMachineList(a.(*v1alpha2.ScalewayMachineList), b.(*ScalewayMachineList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayMachineTemplate)(nil), (*v1alpha2.ScalewayMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate(a.(*ScalewayMachineTemplate), b.(*v1alpha2.ScalewayMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayMachineTemplate)(nil), (*ScalewayMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate(a.(*v1alpha2.ScalewayMachineTemplate), b.(*ScalewayMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayMachineTemplateList)(nil), (*v1alpha2.ScalewayMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineTemplateList_To_v1alpha2_ScalewayMachineTemplateList(a.(*ScalewayMachineTemplateList), b.(*v1alpha2.ScalewayMachineTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayMachineTemplateList)(nil), (*ScalewayMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineTemplateList_To_v1alpha1_ScalewayMachineTemplateList(a.(*v1alpha2.ScalewayMachineTemplateList), b.(*ScalewayMachineTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayMachineTemplateResource)(nil), (*v1alpha2.ScalewayMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineTemplateResource_To_v1alpha2_ScalewayMachineTemplateResource(a.(*ScalewayMachineTemplateResource), b.(*v1alpha2.ScalewayMachineTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayMachineTemplateResource)(nil), (*ScalewayMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineTemplateResource_To_v1alpha1_ScalewayMachineTemplateResource(a.(*v1alpha2.ScalewayMachineTemplateResource), b.(*ScalewayMachineTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayMachineTemplateSpec)(nil), (*v1alpha2.ScalewayMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineTemplateSpec_To_v1alpha2_ScalewayMachineTemplateSpec(a.(*ScalewayMachineTemplateSpec), b.(*v1alpha2.ScalewayMachineTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayMachineTemplateSpec)(nil), (*ScalewayMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineTemplateSpec_To_v1alpha1_ScalewayMachineTemplateSpec(a.(*v1alpha2.ScalewayMachineTemplateSpec), b.(*ScalewayMachineTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayManagedCluster)(nil), (*v1alpha2.ScalewayManagedCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster(a.(*ScalewayManagedCluster), b.(*v1alpha2.ScalewayManagedCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayManagedCluster)(nil), (*ScalewayManagedCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster(a.(*v1alpha2.ScalewayManagedCluster), b.(*ScalewayManagedCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayManagedClusterList)(nil), (*v1alpha2.ScalewayManagedClusterList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedClusterList_To_v1alpha2_ScalewayManagedClusterList(a.(*ScalewayManagedClusterList), b.(*v1alpha2.ScalewayManagedClusterList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayManagedClusterList)(nil), (*ScalewayManagedClusterList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedClusterList_To_v1alpha1_ScalewayManagedClusterList(a.(*v1alpha2.ScalewayManagedClusterList), b.(*ScalewayManagedClusterList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayManagedControlPlane)(nil), (*v1alpha2.ScalewayManagedControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane(a.(*ScalewayManagedControlPlane), b.(*v1alpha2.ScalewayManagedControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayManagedControlPlane)(nil), (*ScalewayManagedControlPlane)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane(a.(*v1alpha2.ScalewayManagedControlPlane), b.(*ScalewayManagedControlPlane), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayManagedControlPlaneList)(nil), (*v1alpha2.ScalewayManagedControlPlaneList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedControlPlaneList_To_v1alpha2_ScalewayManagedControlPlaneList(a.(*ScalewayManagedControlPlaneList), b.(*v1alpha2.ScalewayManagedControlPlaneList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayManagedControlPlaneList)(nil), (*ScalewayManagedControlPlaneList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedControlPlaneList_To_v1alpha1_ScalewayManagedControlPlaneList(a.(*v1alpha2.ScalewayManagedControlPlaneList), b.(*ScalewayManagedControlPlaneList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayManagedMachinePool)(nil), (*v1alpha2.ScalewayManagedMachinePool)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool(a.(*ScalewayManagedMachinePool), b.(*v1alpha2.ScalewayManagedMachinePool), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayManagedMachinePool)(nil), (*ScalewayManagedMachinePool)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool(a.(*v1alpha2.ScalewayManagedMachinePool), b.(*ScalewayManagedMachinePool), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*ScalewayManagedMachinePoolList)(nil), (*v1alpha2.ScalewayManagedMachinePoolList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedMachinePoolList_To_v1alpha2_ScalewayManagedMachinePoolList(a.(*ScalewayManagedMachinePoolList), b.(*v1alpha2.ScalewayManagedMachinePoolList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.ScalewayManagedMachinePoolList)(nil), (*ScalewayManagedMachinePoolList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedMachinePoolList_To_v1alpha1_ScalewayManagedMachinePoolList(a.(*v1alpha2.ScalewayManagedMachinePoolList), b.(*ScalewayManagedMachinePoolList), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1.ObjectMeta)(nil), (*v1beta2.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_ObjectMeta_To_v1beta2_ObjectMeta(a.(*v1.ObjectMeta), b.(*v1beta2.ObjectMeta), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ImageSpec)(nil), (*v1alpha2.Image)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ImageSpec_To_v1alpha2_Image(a.(*ImageSpec), b.(*v1alpha2.Image), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*PrivateNetworkSpec)(nil), (*v1alpha2.PrivateNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_PrivateNetworkSpec_To_v1alpha2_PrivateNetworkSpec(a.(*PrivateNetworkSpec), b.(*v1alpha2.PrivateNetworkSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayClusterSpec)(nil), (*v1alpha2.ScalewayClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterSpec_To_v1alpha2_ScalewayClusterSpec(a.(*ScalewayClusterSpec), b.(*v1alpha2.ScalewayClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayClusterStatus)(nil), (*v1alpha2.ScalewayClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayClusterStatus_To_v1alpha2_ScalewayClusterStatus(a.(*ScalewayClusterStatus), b.(*v1alpha2.ScalewayClusterStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayMachineSpec)(nil), (*v1alpha2.ScalewayMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineSpec_To_v1alpha2_ScalewayMachineSpec(a.(*ScalewayMachineSpec), b.(*v1alpha2.ScalewayMachineSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayMachineStatus)(nil), (*v1alpha2.ScalewayMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayMachineStatus_To_v1alpha2_ScalewayMachineStatus(a.(*ScalewayMachineStatus), b.(*v1alpha2.ScalewayMachineStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayManagedClusterSpec)(nil), (*v1alpha2.ScalewayManagedClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedClusterSpec_To_v1alpha2_ScalewayManagedClusterSpec(a.(*ScalewayManagedClusterSpec), b.(*v1alpha2.ScalewayManagedClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayManagedClusterStatus)(nil), (*v1alpha2.ScalewayManagedClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedClusterStatus_To_v1alpha2_ScalewayManagedClusterStatus(a.(*ScalewayManagedClusterStatus), b.(*v1alpha2.ScalewayManagedClusterStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayManagedControlPlaneSpec)(nil), (*v1alpha2.ScalewayManagedControlPlaneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedControlPlaneSpec_To_v1alpha2_ScalewayManagedControlPlaneSpec(a.(*ScalewayManagedControlPlaneSpec), b.(*v1alpha2.ScalewayManagedControlPlaneSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayManagedControlPlaneStatus)(nil), (*v1alpha2.ScalewayManagedControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedControlPlaneStatus_To_v1alpha2_ScalewayManagedControlPlaneStatus(a.(*ScalewayManagedControlPlaneStatus), b.(*v1alpha2.ScalewayManagedControlPlaneStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayManagedMachinePoolSpec)(nil), (*v1alpha2.ScalewayManagedMachinePoolSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedMachinePoolSpec_To_v1alpha2_ScalewayManagedMachinePoolSpec(a.(*ScalewayManagedMachinePoolSpec), b.(*v1alpha2.ScalewayManagedMachinePoolSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*ScalewayManagedMachinePoolStatus)(nil), (*v1alpha2.ScalewayManagedMachinePoolStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ScalewayManagedMachinePoolStatus_To_v1alpha2_ScalewayManagedMachinePoolStatus(a.(*ScalewayManagedMachinePoolStatus), b.(*v1alpha2.ScalewayManagedMachinePoolStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.Image)(nil), (*ImageSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_Image_To_v1alpha1_ImageSpec(a.(*v1alpha2.Image), b.(*ImageSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.PrivateNetworkSpec)(nil), (*PrivateNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_PrivateNetworkSpec_To_v1alpha1_PrivateNetworkSpec(a.(*v1alpha2.PrivateNetworkSpec), b.(*PrivateNetworkSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayClusterSpec)(nil), (*ScalewayClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterSpec_To_v1alpha1_ScalewayClusterSpec(a.(*v1alpha2.ScalewayClusterSpec), b.(*ScalewayClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayClusterStatus)(nil), (*ScalewayClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayClusterStatus_To_v1alpha1_ScalewayClusterStatus(a.(*v1alpha2.ScalewayClusterStatus), b.(*ScalewayClusterStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayMachineSpec)(nil), (*ScalewayMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineSpec_To_v1alpha1_ScalewayMachineSpec(a.(*v1alpha2.ScalewayMachineSpec), b.(*ScalewayMachineSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayMachineStatus)(nil), (*ScalewayMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayMachineStatus_To_v1alpha1_ScalewayMachineStatus(a.(*v1alpha2.ScalewayMachineStatus), b.(*ScalewayMachineStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayManagedClusterSpec)(nil), (*ScalewayManagedClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedClusterSpec_To_v1alpha1_ScalewayManagedClusterSpec(a.(*v1alpha2.ScalewayManagedClusterSpec), b.(*ScalewayManagedClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayManagedClusterStatus)(nil), (*ScalewayManagedClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedClusterStatus_To_v1alpha1_ScalewayManagedClusterStatus(a.(*v1alpha2.ScalewayManagedClusterStatus), b.(*ScalewayManagedClusterStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayManagedControlPlaneSpec)(nil), (*ScalewayManagedControlPlaneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedControlPlaneSpec_To_v1alpha1_ScalewayManagedControlPlaneSpec(a.(*v1alpha2.ScalewayManagedControlPlaneSpec), b.(*ScalewayManagedControlPlaneSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayManagedControlPlaneStatus)(nil), (*ScalewayManagedControlPlaneStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedControlPlaneStatus_To_v1alpha1_ScalewayManagedControlPlaneStatus(a.(*v1alpha2.ScalewayManagedControlPlaneStatus), b.(*ScalewayManagedControlPlaneStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayManagedMachinePoolSpec)(nil), (*ScalewayManagedMachinePoolSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedMachinePoolSpec_To_v1alpha1_ScalewayManagedMachinePoolSpec(a.(*v1alpha2.ScalewayManagedMachinePoolSpec), b.(*ScalewayManagedMachinePoolSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.ScalewayManagedMachinePoolStatus)(nil), (*ScalewayManagedMachinePoolStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ScalewayManagedMachinePoolStatus_To_v1alpha1_ScalewayManagedMachinePoolStatus(a.(*v1alpha2.ScalewayManagedMachinePoolStatus), b.(*ScalewayManagedMachinePoolStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.APIEndpoint)(nil), (*v1beta2.APIEndpoint)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(a.(*v1beta1.APIEndpoint), b.(*v1beta2.APIEndpoint), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.APIEndpoint)(nil), (*v1beta1.APIEndpoint)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(a.(*v1beta2.APIEndpoint), b.(*v1beta1.APIEndpoint), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta2.ObjectMeta)(nil), (*v1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_ObjectMeta_To_v1_ObjectMeta(a.(*v1beta2.ObjectMeta), b.(*v1.ObjectMeta), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_PrivateNetworkSpec_To_v1alpha2_PrivateNetworkSpec(in *PrivateNetworkSpec, out *v1alpha2.PrivateNetworkSpec, s conversion.Scope) error { + // WARNING: in.PrivateNetworkParams requires manual conversion: does not exist in peer-type + if err := v1.Convert_bool_To_Pointer_bool(&in.Enabled, &out.Enabled, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha2_PrivateNetworkSpec_To_v1alpha1_PrivateNetworkSpec(in *v1alpha2.PrivateNetworkSpec, out *PrivateNetworkSpec, s conversion.Scope) error { + // WARNING: in.PrivateNetwork requires manual conversion: does not exist in peer-type + if err := v1.Convert_Pointer_bool_To_bool(&in.Enabled, &out.Enabled, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster(in *ScalewayCluster, out *v1alpha2.ScalewayCluster, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayClusterSpec_To_v1alpha2_ScalewayClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayClusterStatus_To_v1alpha2_ScalewayClusterStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster(in *ScalewayCluster, out *v1alpha2.ScalewayCluster, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster(in *v1alpha2.ScalewayCluster, out *ScalewayCluster, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayClusterSpec_To_v1alpha1_ScalewayClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayClusterStatus_To_v1alpha1_ScalewayClusterStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster(in *v1alpha2.ScalewayCluster, out *ScalewayCluster, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayClusterList_To_v1alpha2_ScalewayClusterList(in *ScalewayClusterList, out *v1alpha2.ScalewayClusterList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayCluster, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayCluster_To_v1alpha2_ScalewayCluster(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayClusterList_To_v1alpha2_ScalewayClusterList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayClusterList_To_v1alpha2_ScalewayClusterList(in *ScalewayClusterList, out *v1alpha2.ScalewayClusterList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayClusterList_To_v1alpha2_ScalewayClusterList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayClusterList_To_v1alpha1_ScalewayClusterList(in *v1alpha2.ScalewayClusterList, out *ScalewayClusterList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayCluster, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayCluster_To_v1alpha1_ScalewayCluster(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayClusterList_To_v1alpha1_ScalewayClusterList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayClusterList_To_v1alpha1_ScalewayClusterList(in *v1alpha2.ScalewayClusterList, out *ScalewayClusterList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayClusterList_To_v1alpha1_ScalewayClusterList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayClusterSpec_To_v1alpha2_ScalewayClusterSpec(in *ScalewayClusterSpec, out *v1alpha2.ScalewayClusterSpec, s conversion.Scope) error { + out.ProjectID = v1alpha2.UUID(in.ProjectID) + out.Region = v1alpha2.ScalewayRegion(in.Region) + // WARNING: in.Network requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.NetworkSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayClusterNetwork) + out.ScalewaySecretName = in.ScalewaySecretName + out.FailureDomains = *(*[]v1alpha2.ScalewayZone)(unsafe.Pointer(&in.FailureDomains)) + if err := Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(&in.ControlPlaneEndpoint, &out.ControlPlaneEndpoint, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha2_ScalewayClusterSpec_To_v1alpha1_ScalewayClusterSpec(in *v1alpha2.ScalewayClusterSpec, out *ScalewayClusterSpec, s conversion.Scope) error { + out.ProjectID = string(in.ProjectID) + out.Region = string(in.Region) + out.ScalewaySecretName = in.ScalewaySecretName + out.FailureDomains = *(*[]string)(unsafe.Pointer(&in.FailureDomains)) + // WARNING: in.Network requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayClusterNetwork vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.NetworkSpec) + if err := Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(&in.ControlPlaneEndpoint, &out.ControlPlaneEndpoint, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_ScalewayClusterStatus_To_v1alpha2_ScalewayClusterStatus(in *ScalewayClusterStatus, out *v1alpha2.ScalewayClusterStatus, s conversion.Scope) error { + // WARNING: in.Ready requires manual conversion: does not exist in peer-type + // WARNING: in.Network requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.NetworkStatus vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayClusterNetworkStatus) + // WARNING: in.FailureDomains requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api/api/core/v1beta1.FailureDomains vs []sigs.k8s.io/cluster-api/api/core/v1beta2.FailureDomain) + return nil +} + +func autoConvert_v1alpha2_ScalewayClusterStatus_To_v1alpha1_ScalewayClusterStatus(in *v1alpha2.ScalewayClusterStatus, out *ScalewayClusterStatus, s conversion.Scope) error { + // WARNING: in.Conditions requires manual conversion: does not exist in peer-type + // WARNING: in.FailureDomains requires manual conversion: inconvertible types ([]sigs.k8s.io/cluster-api/api/core/v1beta2.FailureDomain vs sigs.k8s.io/cluster-api/api/core/v1beta1.FailureDomains) + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + // WARNING: in.Network requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayClusterNetworkStatus vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.NetworkStatus) + return nil +} + +func autoConvert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate(in *ScalewayClusterTemplate, out *v1alpha2.ScalewayClusterTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayClusterTemplateSpec_To_v1alpha2_ScalewayClusterTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate(in *ScalewayClusterTemplate, out *v1alpha2.ScalewayClusterTemplate, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate(in *v1alpha2.ScalewayClusterTemplate, out *ScalewayClusterTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayClusterTemplateSpec_To_v1alpha1_ScalewayClusterTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate(in *v1alpha2.ScalewayClusterTemplate, out *ScalewayClusterTemplate, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayClusterTemplateList_To_v1alpha2_ScalewayClusterTemplateList(in *ScalewayClusterTemplateList, out *v1alpha2.ScalewayClusterTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayClusterTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayClusterTemplate_To_v1alpha2_ScalewayClusterTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayClusterTemplateList_To_v1alpha2_ScalewayClusterTemplateList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayClusterTemplateList_To_v1alpha2_ScalewayClusterTemplateList(in *ScalewayClusterTemplateList, out *v1alpha2.ScalewayClusterTemplateList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayClusterTemplateList_To_v1alpha2_ScalewayClusterTemplateList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayClusterTemplateList_To_v1alpha1_ScalewayClusterTemplateList(in *v1alpha2.ScalewayClusterTemplateList, out *ScalewayClusterTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayClusterTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayClusterTemplate_To_v1alpha1_ScalewayClusterTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayClusterTemplateList_To_v1alpha1_ScalewayClusterTemplateList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayClusterTemplateList_To_v1alpha1_ScalewayClusterTemplateList(in *v1alpha2.ScalewayClusterTemplateList, out *ScalewayClusterTemplateList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayClusterTemplateList_To_v1alpha1_ScalewayClusterTemplateList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayClusterTemplateResource_To_v1alpha2_ScalewayClusterTemplateResource(in *ScalewayClusterTemplateResource, out *v1alpha2.ScalewayClusterTemplateResource, s conversion.Scope) error { + if err := Convert_v1_ObjectMeta_To_v1beta2_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayClusterSpec_To_v1alpha2_ScalewayClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayClusterTemplateResource_To_v1alpha2_ScalewayClusterTemplateResource is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayClusterTemplateResource_To_v1alpha2_ScalewayClusterTemplateResource(in *ScalewayClusterTemplateResource, out *v1alpha2.ScalewayClusterTemplateResource, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayClusterTemplateResource_To_v1alpha2_ScalewayClusterTemplateResource(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayClusterTemplateResource_To_v1alpha1_ScalewayClusterTemplateResource(in *v1alpha2.ScalewayClusterTemplateResource, out *ScalewayClusterTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta2_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayClusterSpec_To_v1alpha1_ScalewayClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayClusterTemplateResource_To_v1alpha1_ScalewayClusterTemplateResource is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayClusterTemplateResource_To_v1alpha1_ScalewayClusterTemplateResource(in *v1alpha2.ScalewayClusterTemplateResource, out *ScalewayClusterTemplateResource, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayClusterTemplateResource_To_v1alpha1_ScalewayClusterTemplateResource(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayClusterTemplateSpec_To_v1alpha2_ScalewayClusterTemplateSpec(in *ScalewayClusterTemplateSpec, out *v1alpha2.ScalewayClusterTemplateSpec, s conversion.Scope) error { + if err := Convert_v1alpha1_ScalewayClusterTemplateResource_To_v1alpha2_ScalewayClusterTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayClusterTemplateSpec_To_v1alpha2_ScalewayClusterTemplateSpec is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayClusterTemplateSpec_To_v1alpha2_ScalewayClusterTemplateSpec(in *ScalewayClusterTemplateSpec, out *v1alpha2.ScalewayClusterTemplateSpec, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayClusterTemplateSpec_To_v1alpha2_ScalewayClusterTemplateSpec(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayClusterTemplateSpec_To_v1alpha1_ScalewayClusterTemplateSpec(in *v1alpha2.ScalewayClusterTemplateSpec, out *ScalewayClusterTemplateSpec, s conversion.Scope) error { + if err := Convert_v1alpha2_ScalewayClusterTemplateResource_To_v1alpha1_ScalewayClusterTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayClusterTemplateSpec_To_v1alpha1_ScalewayClusterTemplateSpec is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayClusterTemplateSpec_To_v1alpha1_ScalewayClusterTemplateSpec(in *v1alpha2.ScalewayClusterTemplateSpec, out *ScalewayClusterTemplateSpec, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayClusterTemplateSpec_To_v1alpha1_ScalewayClusterTemplateSpec(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine(in *ScalewayMachine, out *v1alpha2.ScalewayMachine, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayMachineSpec_To_v1alpha2_ScalewayMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayMachineStatus_To_v1alpha2_ScalewayMachineStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine(in *ScalewayMachine, out *v1alpha2.ScalewayMachine, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine(in *v1alpha2.ScalewayMachine, out *ScalewayMachine, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayMachineSpec_To_v1alpha1_ScalewayMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayMachineStatus_To_v1alpha1_ScalewayMachineStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine(in *v1alpha2.ScalewayMachine, out *ScalewayMachine, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayMachineList_To_v1alpha2_ScalewayMachineList(in *ScalewayMachineList, out *v1alpha2.ScalewayMachineList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayMachine, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayMachine_To_v1alpha2_ScalewayMachine(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayMachineList_To_v1alpha2_ScalewayMachineList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayMachineList_To_v1alpha2_ScalewayMachineList(in *ScalewayMachineList, out *v1alpha2.ScalewayMachineList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayMachineList_To_v1alpha2_ScalewayMachineList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayMachineList_To_v1alpha1_ScalewayMachineList(in *v1alpha2.ScalewayMachineList, out *ScalewayMachineList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayMachine, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayMachine_To_v1alpha1_ScalewayMachine(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayMachineList_To_v1alpha1_ScalewayMachineList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayMachineList_To_v1alpha1_ScalewayMachineList(in *v1alpha2.ScalewayMachineList, out *ScalewayMachineList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayMachineList_To_v1alpha1_ScalewayMachineList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayMachineSpec_To_v1alpha2_ScalewayMachineSpec(in *ScalewayMachineSpec, out *v1alpha2.ScalewayMachineSpec, s conversion.Scope) error { + if err := v1.Convert_Pointer_string_To_string(&in.ProviderID, &out.ProviderID, s); err != nil { + return err + } + out.CommercialType = in.CommercialType + if err := Convert_v1alpha1_ImageSpec_To_v1alpha2_Image(&in.Image, &out.Image, s); err != nil { + return err + } + // WARNING: in.RootVolume requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.RootVolumeSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.RootVolume) + // WARNING: in.PublicNetwork requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.PublicNetworkSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.PublicNetwork) + // WARNING: in.PlacementGroup requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.PlacementGroupSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.IDOrName) + // WARNING: in.SecurityGroup requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.SecurityGroupSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.IDOrName) + return nil +} + +func autoConvert_v1alpha2_ScalewayMachineSpec_To_v1alpha1_ScalewayMachineSpec(in *v1alpha2.ScalewayMachineSpec, out *ScalewayMachineSpec, s conversion.Scope) error { + if err := v1.Convert_string_To_Pointer_string(&in.ProviderID, &out.ProviderID, s); err != nil { + return err + } + out.CommercialType = in.CommercialType + if err := Convert_v1alpha2_Image_To_v1alpha1_ImageSpec(&in.Image, &out.Image, s); err != nil { + return err + } + // WARNING: in.RootVolume requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.RootVolume vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.RootVolumeSpec) + // WARNING: in.PublicNetwork requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.PublicNetwork vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.PublicNetworkSpec) + // WARNING: in.PlacementGroup requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.IDOrName vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.PlacementGroupSpec) + // WARNING: in.SecurityGroup requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.IDOrName vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.SecurityGroupSpec) + return nil +} + +func autoConvert_v1alpha1_ScalewayMachineStatus_To_v1alpha2_ScalewayMachineStatus(in *ScalewayMachineStatus, out *v1alpha2.ScalewayMachineStatus, s conversion.Scope) error { + out.Addresses = *(*[]v1beta2.MachineAddress)(unsafe.Pointer(&in.Addresses)) + // WARNING: in.Ready requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1alpha2_ScalewayMachineStatus_To_v1alpha1_ScalewayMachineStatus(in *v1alpha2.ScalewayMachineStatus, out *ScalewayMachineStatus, s conversion.Scope) error { + // WARNING: in.Conditions requires manual conversion: does not exist in peer-type + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + out.Addresses = *(*[]v1beta1.MachineAddress)(unsafe.Pointer(&in.Addresses)) + return nil +} + +func autoConvert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate(in *ScalewayMachineTemplate, out *v1alpha2.ScalewayMachineTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayMachineTemplateSpec_To_v1alpha2_ScalewayMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate(in *ScalewayMachineTemplate, out *v1alpha2.ScalewayMachineTemplate, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate(in *v1alpha2.ScalewayMachineTemplate, out *ScalewayMachineTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayMachineTemplateSpec_To_v1alpha1_ScalewayMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate(in *v1alpha2.ScalewayMachineTemplate, out *ScalewayMachineTemplate, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayMachineTemplateList_To_v1alpha2_ScalewayMachineTemplateList(in *ScalewayMachineTemplateList, out *v1alpha2.ScalewayMachineTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayMachineTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayMachineTemplate_To_v1alpha2_ScalewayMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayMachineTemplateList_To_v1alpha2_ScalewayMachineTemplateList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayMachineTemplateList_To_v1alpha2_ScalewayMachineTemplateList(in *ScalewayMachineTemplateList, out *v1alpha2.ScalewayMachineTemplateList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayMachineTemplateList_To_v1alpha2_ScalewayMachineTemplateList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayMachineTemplateList_To_v1alpha1_ScalewayMachineTemplateList(in *v1alpha2.ScalewayMachineTemplateList, out *ScalewayMachineTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayMachineTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayMachineTemplate_To_v1alpha1_ScalewayMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayMachineTemplateList_To_v1alpha1_ScalewayMachineTemplateList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayMachineTemplateList_To_v1alpha1_ScalewayMachineTemplateList(in *v1alpha2.ScalewayMachineTemplateList, out *ScalewayMachineTemplateList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayMachineTemplateList_To_v1alpha1_ScalewayMachineTemplateList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayMachineTemplateResource_To_v1alpha2_ScalewayMachineTemplateResource(in *ScalewayMachineTemplateResource, out *v1alpha2.ScalewayMachineTemplateResource, s conversion.Scope) error { + if err := Convert_v1_ObjectMeta_To_v1beta2_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayMachineSpec_To_v1alpha2_ScalewayMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayMachineTemplateResource_To_v1alpha2_ScalewayMachineTemplateResource is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayMachineTemplateResource_To_v1alpha2_ScalewayMachineTemplateResource(in *ScalewayMachineTemplateResource, out *v1alpha2.ScalewayMachineTemplateResource, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayMachineTemplateResource_To_v1alpha2_ScalewayMachineTemplateResource(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayMachineTemplateResource_To_v1alpha1_ScalewayMachineTemplateResource(in *v1alpha2.ScalewayMachineTemplateResource, out *ScalewayMachineTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta2_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayMachineSpec_To_v1alpha1_ScalewayMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayMachineTemplateResource_To_v1alpha1_ScalewayMachineTemplateResource is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayMachineTemplateResource_To_v1alpha1_ScalewayMachineTemplateResource(in *v1alpha2.ScalewayMachineTemplateResource, out *ScalewayMachineTemplateResource, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayMachineTemplateResource_To_v1alpha1_ScalewayMachineTemplateResource(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayMachineTemplateSpec_To_v1alpha2_ScalewayMachineTemplateSpec(in *ScalewayMachineTemplateSpec, out *v1alpha2.ScalewayMachineTemplateSpec, s conversion.Scope) error { + if err := Convert_v1alpha1_ScalewayMachineTemplateResource_To_v1alpha2_ScalewayMachineTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayMachineTemplateSpec_To_v1alpha2_ScalewayMachineTemplateSpec is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayMachineTemplateSpec_To_v1alpha2_ScalewayMachineTemplateSpec(in *ScalewayMachineTemplateSpec, out *v1alpha2.ScalewayMachineTemplateSpec, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayMachineTemplateSpec_To_v1alpha2_ScalewayMachineTemplateSpec(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayMachineTemplateSpec_To_v1alpha1_ScalewayMachineTemplateSpec(in *v1alpha2.ScalewayMachineTemplateSpec, out *ScalewayMachineTemplateSpec, s conversion.Scope) error { + if err := Convert_v1alpha2_ScalewayMachineTemplateResource_To_v1alpha1_ScalewayMachineTemplateResource(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayMachineTemplateSpec_To_v1alpha1_ScalewayMachineTemplateSpec is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayMachineTemplateSpec_To_v1alpha1_ScalewayMachineTemplateSpec(in *v1alpha2.ScalewayMachineTemplateSpec, out *ScalewayMachineTemplateSpec, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayMachineTemplateSpec_To_v1alpha1_ScalewayMachineTemplateSpec(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster(in *ScalewayManagedCluster, out *v1alpha2.ScalewayManagedCluster, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayManagedClusterSpec_To_v1alpha2_ScalewayManagedClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayManagedClusterStatus_To_v1alpha2_ScalewayManagedClusterStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster(in *ScalewayManagedCluster, out *v1alpha2.ScalewayManagedCluster, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster(in *v1alpha2.ScalewayManagedCluster, out *ScalewayManagedCluster, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayManagedClusterSpec_To_v1alpha1_ScalewayManagedClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayManagedClusterStatus_To_v1alpha1_ScalewayManagedClusterStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster(in *v1alpha2.ScalewayManagedCluster, out *ScalewayManagedCluster, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedClusterList_To_v1alpha2_ScalewayManagedClusterList(in *ScalewayManagedClusterList, out *v1alpha2.ScalewayManagedClusterList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayManagedCluster, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayManagedCluster_To_v1alpha2_ScalewayManagedCluster(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayManagedClusterList_To_v1alpha2_ScalewayManagedClusterList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayManagedClusterList_To_v1alpha2_ScalewayManagedClusterList(in *ScalewayManagedClusterList, out *v1alpha2.ScalewayManagedClusterList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayManagedClusterList_To_v1alpha2_ScalewayManagedClusterList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayManagedClusterList_To_v1alpha1_ScalewayManagedClusterList(in *v1alpha2.ScalewayManagedClusterList, out *ScalewayManagedClusterList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayManagedCluster, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayManagedCluster_To_v1alpha1_ScalewayManagedCluster(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayManagedClusterList_To_v1alpha1_ScalewayManagedClusterList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayManagedClusterList_To_v1alpha1_ScalewayManagedClusterList(in *v1alpha2.ScalewayManagedClusterList, out *ScalewayManagedClusterList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayManagedClusterList_To_v1alpha1_ScalewayManagedClusterList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedClusterSpec_To_v1alpha2_ScalewayManagedClusterSpec(in *ScalewayManagedClusterSpec, out *v1alpha2.ScalewayManagedClusterSpec, s conversion.Scope) error { + out.Region = v1alpha2.ScalewayRegion(in.Region) + out.ProjectID = v1alpha2.UUID(in.ProjectID) + out.ScalewaySecretName = in.ScalewaySecretName + // WARNING: in.Network requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.ManagedNetworkSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayManagedClusterNetwork) + if err := Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(&in.ControlPlaneEndpoint, &out.ControlPlaneEndpoint, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha2_ScalewayManagedClusterSpec_To_v1alpha1_ScalewayManagedClusterSpec(in *v1alpha2.ScalewayManagedClusterSpec, out *ScalewayManagedClusterSpec, s conversion.Scope) error { + out.Region = string(in.Region) + out.ProjectID = string(in.ProjectID) + out.ScalewaySecretName = in.ScalewaySecretName + // WARNING: in.Network requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayManagedClusterNetwork vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.ManagedNetworkSpec) + if err := Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(&in.ControlPlaneEndpoint, &out.ControlPlaneEndpoint, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_ScalewayManagedClusterStatus_To_v1alpha2_ScalewayManagedClusterStatus(in *ScalewayManagedClusterStatus, out *v1alpha2.ScalewayManagedClusterStatus, s conversion.Scope) error { + // WARNING: in.Ready requires manual conversion: does not exist in peer-type + // WARNING: in.Network requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.ManagedNetworkStatus vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayManagedClusterNetworkStatus) + return nil +} + +func autoConvert_v1alpha2_ScalewayManagedClusterStatus_To_v1alpha1_ScalewayManagedClusterStatus(in *v1alpha2.ScalewayManagedClusterStatus, out *ScalewayManagedClusterStatus, s conversion.Scope) error { + // WARNING: in.Conditions requires manual conversion: does not exist in peer-type + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + // WARNING: in.Network requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.ScalewayManagedClusterNetworkStatus vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.ManagedNetworkStatus) + return nil +} + +func autoConvert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane(in *ScalewayManagedControlPlane, out *v1alpha2.ScalewayManagedControlPlane, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayManagedControlPlaneSpec_To_v1alpha2_ScalewayManagedControlPlaneSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayManagedControlPlaneStatus_To_v1alpha2_ScalewayManagedControlPlaneStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane(in *ScalewayManagedControlPlane, out *v1alpha2.ScalewayManagedControlPlane, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane(in *v1alpha2.ScalewayManagedControlPlane, out *ScalewayManagedControlPlane, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayManagedControlPlaneSpec_To_v1alpha1_ScalewayManagedControlPlaneSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayManagedControlPlaneStatus_To_v1alpha1_ScalewayManagedControlPlaneStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane(in *v1alpha2.ScalewayManagedControlPlane, out *ScalewayManagedControlPlane, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedControlPlaneList_To_v1alpha2_ScalewayManagedControlPlaneList(in *ScalewayManagedControlPlaneList, out *v1alpha2.ScalewayManagedControlPlaneList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayManagedControlPlane, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayManagedControlPlane_To_v1alpha2_ScalewayManagedControlPlane(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayManagedControlPlaneList_To_v1alpha2_ScalewayManagedControlPlaneList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayManagedControlPlaneList_To_v1alpha2_ScalewayManagedControlPlaneList(in *ScalewayManagedControlPlaneList, out *v1alpha2.ScalewayManagedControlPlaneList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayManagedControlPlaneList_To_v1alpha2_ScalewayManagedControlPlaneList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayManagedControlPlaneList_To_v1alpha1_ScalewayManagedControlPlaneList(in *v1alpha2.ScalewayManagedControlPlaneList, out *ScalewayManagedControlPlaneList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayManagedControlPlane, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayManagedControlPlane_To_v1alpha1_ScalewayManagedControlPlane(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayManagedControlPlaneList_To_v1alpha1_ScalewayManagedControlPlaneList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayManagedControlPlaneList_To_v1alpha1_ScalewayManagedControlPlaneList(in *v1alpha2.ScalewayManagedControlPlaneList, out *ScalewayManagedControlPlaneList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayManagedControlPlaneList_To_v1alpha1_ScalewayManagedControlPlaneList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedControlPlaneSpec_To_v1alpha2_ScalewayManagedControlPlaneSpec(in *ScalewayManagedControlPlaneSpec, out *v1alpha2.ScalewayManagedControlPlaneSpec, s conversion.Scope) error { + if err := v1.Convert_Pointer_string_To_string(&in.ClusterName, &out.ClusterName, s); err != nil { + return err + } + out.Type = in.Type + out.Version = in.Version + if err := v1.Convert_Pointer_string_To_string(&in.CNI, &out.CNI, s); err != nil { + return err + } + out.AdditionalTags = *(*[]string)(unsafe.Pointer(&in.AdditionalTags)) + // WARNING: in.Autoscaler requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.AutoscalerSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.Autoscaler) + // WARNING: in.AutoUpgrade requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.AutoUpgradeSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.AutoUpgrade) + out.FeatureGates = *(*[]string)(unsafe.Pointer(&in.FeatureGates)) + out.AdmissionPlugins = *(*[]string)(unsafe.Pointer(&in.AdmissionPlugins)) + // WARNING: in.OpenIDConnect requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.OpenIDConnectSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.OpenIDConnect) + out.APIServerCertSANs = *(*[]string)(unsafe.Pointer(&in.APIServerCertSANs)) + // WARNING: in.OnDelete requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.OnDeleteSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.OnDelete) + out.ACL = (*v1alpha2.ACL)(unsafe.Pointer(in.ACL)) + out.EnablePrivateEndpoint = (*bool)(unsafe.Pointer(in.EnablePrivateEndpoint)) + if err := Convert_v1beta1_APIEndpoint_To_v1beta2_APIEndpoint(&in.ControlPlaneEndpoint, &out.ControlPlaneEndpoint, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha2_ScalewayManagedControlPlaneSpec_To_v1alpha1_ScalewayManagedControlPlaneSpec(in *v1alpha2.ScalewayManagedControlPlaneSpec, out *ScalewayManagedControlPlaneSpec, s conversion.Scope) error { + if err := v1.Convert_string_To_Pointer_string(&in.ClusterName, &out.ClusterName, s); err != nil { + return err + } + out.Type = in.Type + out.Version = in.Version + if err := v1.Convert_string_To_Pointer_string(&in.CNI, &out.CNI, s); err != nil { + return err + } + out.AdditionalTags = *(*[]string)(unsafe.Pointer(&in.AdditionalTags)) + // WARNING: in.Autoscaler requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.Autoscaler vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.AutoscalerSpec) + // WARNING: in.AutoUpgrade requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.AutoUpgrade vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.AutoUpgradeSpec) + out.FeatureGates = *(*[]string)(unsafe.Pointer(&in.FeatureGates)) + out.AdmissionPlugins = *(*[]string)(unsafe.Pointer(&in.AdmissionPlugins)) + // WARNING: in.OpenIDConnect requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.OpenIDConnect vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.OpenIDConnectSpec) + out.APIServerCertSANs = *(*[]string)(unsafe.Pointer(&in.APIServerCertSANs)) + // WARNING: in.OnDelete requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.OnDelete vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.OnDeleteSpec) + out.ACL = (*ACLSpec)(unsafe.Pointer(in.ACL)) + out.EnablePrivateEndpoint = (*bool)(unsafe.Pointer(in.EnablePrivateEndpoint)) + if err := Convert_v1beta2_APIEndpoint_To_v1beta1_APIEndpoint(&in.ControlPlaneEndpoint, &out.ControlPlaneEndpoint, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_ScalewayManagedControlPlaneStatus_To_v1alpha2_ScalewayManagedControlPlaneStatus(in *ScalewayManagedControlPlaneStatus, out *v1alpha2.ScalewayManagedControlPlaneStatus, s conversion.Scope) error { + // WARNING: in.Ready requires manual conversion: does not exist in peer-type + // WARNING: in.Initialized requires manual conversion: does not exist in peer-type + if err := v1.Convert_bool_To_Pointer_bool(&in.ExternalManagedControlPlane, &out.ExternalManagedControlPlane, s); err != nil { + return err + } + if err := v1.Convert_Pointer_string_To_string(&in.Version, &out.Version, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha2_ScalewayManagedControlPlaneStatus_To_v1alpha1_ScalewayManagedControlPlaneStatus(in *v1alpha2.ScalewayManagedControlPlaneStatus, out *ScalewayManagedControlPlaneStatus, s conversion.Scope) error { + // WARNING: in.Conditions requires manual conversion: does not exist in peer-type + if err := v1.Convert_string_To_Pointer_string(&in.Version, &out.Version, s); err != nil { + return err + } + if err := v1.Convert_Pointer_bool_To_bool(&in.ExternalManagedControlPlane, &out.ExternalManagedControlPlane, s); err != nil { + return err + } + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool(in *ScalewayManagedMachinePool, out *v1alpha2.ScalewayManagedMachinePool, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_ScalewayManagedMachinePoolSpec_To_v1alpha2_ScalewayManagedMachinePoolSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_ScalewayManagedMachinePoolStatus_To_v1alpha2_ScalewayManagedMachinePoolStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool(in *ScalewayManagedMachinePool, out *v1alpha2.ScalewayManagedMachinePool, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool(in *v1alpha2.ScalewayManagedMachinePool, out *ScalewayManagedMachinePool, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_ScalewayManagedMachinePoolSpec_To_v1alpha1_ScalewayManagedMachinePoolSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_ScalewayManagedMachinePoolStatus_To_v1alpha1_ScalewayManagedMachinePoolStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool(in *v1alpha2.ScalewayManagedMachinePool, out *ScalewayManagedMachinePool, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedMachinePoolList_To_v1alpha2_ScalewayManagedMachinePoolList(in *ScalewayManagedMachinePoolList, out *v1alpha2.ScalewayManagedMachinePoolList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.ScalewayManagedMachinePool, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_ScalewayManagedMachinePool_To_v1alpha2_ScalewayManagedMachinePool(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_ScalewayManagedMachinePoolList_To_v1alpha2_ScalewayManagedMachinePoolList is an autogenerated conversion function. +func Convert_v1alpha1_ScalewayManagedMachinePoolList_To_v1alpha2_ScalewayManagedMachinePoolList(in *ScalewayManagedMachinePoolList, out *v1alpha2.ScalewayManagedMachinePoolList, s conversion.Scope) error { + return autoConvert_v1alpha1_ScalewayManagedMachinePoolList_To_v1alpha2_ScalewayManagedMachinePoolList(in, out, s) +} + +func autoConvert_v1alpha2_ScalewayManagedMachinePoolList_To_v1alpha1_ScalewayManagedMachinePoolList(in *v1alpha2.ScalewayManagedMachinePoolList, out *ScalewayManagedMachinePoolList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayManagedMachinePool, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_ScalewayManagedMachinePool_To_v1alpha1_ScalewayManagedMachinePool(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_ScalewayManagedMachinePoolList_To_v1alpha1_ScalewayManagedMachinePoolList is an autogenerated conversion function. +func Convert_v1alpha2_ScalewayManagedMachinePoolList_To_v1alpha1_ScalewayManagedMachinePoolList(in *v1alpha2.ScalewayManagedMachinePoolList, out *ScalewayManagedMachinePoolList, s conversion.Scope) error { + return autoConvert_v1alpha2_ScalewayManagedMachinePoolList_To_v1alpha1_ScalewayManagedMachinePoolList(in, out, s) +} + +func autoConvert_v1alpha1_ScalewayManagedMachinePoolSpec_To_v1alpha2_ScalewayManagedMachinePoolSpec(in *ScalewayManagedMachinePoolSpec, out *v1alpha2.ScalewayManagedMachinePoolSpec, s conversion.Scope) error { + out.NodeType = in.NodeType + out.Zone = v1alpha2.ScalewayZone(in.Zone) + // WARNING: in.PlacementGroupID requires manual conversion: inconvertible types (*string vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.UUID) + // WARNING: in.Scaling requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.ScalingSpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.Scaling) + out.Autohealing = (*bool)(unsafe.Pointer(in.Autohealing)) + out.AdditionalTags = *(*[]string)(unsafe.Pointer(&in.AdditionalTags)) + out.KubeletArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletArgs)) + // WARNING: in.UpgradePolicy requires manual conversion: inconvertible types (*github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.UpgradePolicySpec vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.UpgradePolicy) + if err := v1.Convert_Pointer_string_To_string(&in.RootVolumeType, &out.RootVolumeType, s); err != nil { + return err + } + if err := v1.Convert_Pointer_int64_To_int64(&in.RootVolumeSizeGB, &out.RootVolumeSizeGB, s); err != nil { + return err + } + out.PublicIPDisabled = (*bool)(unsafe.Pointer(in.PublicIPDisabled)) + // WARNING: in.SecurityGroupID requires manual conversion: inconvertible types (*string vs github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.UUID) + out.ProviderIDList = *(*[]string)(unsafe.Pointer(&in.ProviderIDList)) + return nil +} + +func autoConvert_v1alpha2_ScalewayManagedMachinePoolSpec_To_v1alpha1_ScalewayManagedMachinePoolSpec(in *v1alpha2.ScalewayManagedMachinePoolSpec, out *ScalewayManagedMachinePoolSpec, s conversion.Scope) error { + out.NodeType = in.NodeType + out.Zone = string(in.Zone) + // WARNING: in.PlacementGroupID requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.UUID vs *string) + // WARNING: in.Scaling requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.Scaling vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.ScalingSpec) + out.Autohealing = (*bool)(unsafe.Pointer(in.Autohealing)) + out.AdditionalTags = *(*[]string)(unsafe.Pointer(&in.AdditionalTags)) + out.KubeletArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletArgs)) + // WARNING: in.UpgradePolicy requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.UpgradePolicy vs *github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1.UpgradePolicySpec) + if err := v1.Convert_string_To_Pointer_string(&in.RootVolumeType, &out.RootVolumeType, s); err != nil { + return err + } + if err := v1.Convert_int64_To_Pointer_int64(&in.RootVolumeSizeGB, &out.RootVolumeSizeGB, s); err != nil { + return err + } + out.PublicIPDisabled = (*bool)(unsafe.Pointer(in.PublicIPDisabled)) + // WARNING: in.SecurityGroupID requires manual conversion: inconvertible types (github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2.UUID vs *string) + out.ProviderIDList = *(*[]string)(unsafe.Pointer(&in.ProviderIDList)) + return nil +} + +func autoConvert_v1alpha1_ScalewayManagedMachinePoolStatus_To_v1alpha2_ScalewayManagedMachinePoolStatus(in *ScalewayManagedMachinePoolStatus, out *v1alpha2.ScalewayManagedMachinePoolStatus, s conversion.Scope) error { + if err := v1.Convert_bool_To_Pointer_bool(&in.Ready, &out.Ready, s); err != nil { + return err + } + if err := v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha2_ScalewayManagedMachinePoolStatus_To_v1alpha1_ScalewayManagedMachinePoolStatus(in *v1alpha2.ScalewayManagedMachinePoolStatus, out *ScalewayManagedMachinePoolStatus, s conversion.Scope) error { + // WARNING: in.Conditions requires manual conversion: does not exist in peer-type + if err := v1.Convert_Pointer_bool_To_bool(&in.Ready, &out.Ready, s); err != nil { + return err + } + // WARNING: in.Initialization requires manual conversion: does not exist in peer-type + if err := v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + return nil +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ef96802..98d399f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,8 +5,8 @@ package v1alpha1 import ( - runtime "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/cluster-api/api/core/v1beta1" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/api/v1alpha2/common_types.go b/api/v1alpha2/common_types.go new file mode 100644 index 0000000..f7dc12b --- /dev/null +++ b/api/v1alpha2/common_types.go @@ -0,0 +1,76 @@ +package v1alpha2 + +// UUID is a valid UUID for a Scaleway resource. +// +kubebuilder:validation:Pattern="^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" +// +kubebuilder:validation:MinLength=36 +// +kubebuilder:validation:MaxLength=36 +type UUID string + +// CIDR is an IP address range in CIDR notation (for example, "10.0.0.0/8" or "fd00::/8"). +// +kubebuilder:validation:XValidation:rule="isCIDR(self)",message="value must be a valid CIDR network address" +// +kubebuilder:validation:MinLength=1 +// +kubebuilder:validation:MaxLength=43 +type CIDR string + +// IPv4 is a valid IPv4. +// +kubebuilder:validation:Format=ipv4 +// +kubebuilder:validation:MinLength=1 +// +kubebuilder:validation:MaxLength=15 +type IPv4 string + +// ScalewayRegion is a Scaleway region (e.g. fr-par). +// +kubebuilder:validation:Pattern="^[a-z]{2}-[a-z]{3}$" +// +kubebuilder:validation:MinLength=6 +// +kubebuilder:validation:MaxLength=6 +type ScalewayRegion string + +// ScalewayZone is a Scaleway zone (e.g. fr-par-1). +// +kubebuilder:validation:Pattern="^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$" +// +kubebuilder:validation:MinLength=8 +// +kubebuilder:validation:MaxLength=9 +type ScalewayZone string + +// PrivateNetwork allows to reference an existing Private Network or configure +// the parameters of the auto-created Private Network. +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:XValidation:rule="has(self.vpcID) == has(oldSelf.vpcID)",message="vpcID cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.id) == has(oldSelf.id)",message="id cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.subnet) == has(oldSelf.subnet)",message="subnet cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="!has(self.id) || has(self.id) != has(self.vpcID)",message="id and vpcID cannot be set at the same time" +// +kubebuilder:validation:XValidation:rule="!has(self.id) || has(self.id) != has(self.subnet)",message="id and subnet cannot be set at the same time" +type PrivateNetwork struct { + // id allows to reuse an existing Private Network instead of creating a new one. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ID UUID `json:"id,omitempty"` + + // vpcID defines the ID of the VPC where the new Private Network will be created if none is provided. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + VPCID UUID `json:"vpcID,omitempty"` + + // subnet defines a subnet for the Private Network. Only used on newly created Private Networks. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Subnet CIDR `json:"subnet,omitempty"` +} + +// PublicGateway defines settings of the Public Gateway that will be created. +// +kubebuilder:validation:MinProperties=1 +type PublicGateway struct { + // type is a Public Gateway commercial offer type. + // +optional + // +kubebuilder:default="VPC-GW-S" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=20 + Type string `json:"type,omitempty"` + + // ip to use when creating a Public Gateway. + // +optional + IP IPv4 `json:"ip,omitempty"` + + // zone where to create the Public Gateway. Must be in the same region as the + // cluster. Defaults to the first zone of the region. + // +optional + Zone ScalewayZone `json:"zone,omitempty"` +} diff --git a/api/v1alpha2/condition_consts.go b/api/v1alpha2/condition_consts.go new file mode 100644 index 0000000..a686c72 --- /dev/null +++ b/api/v1alpha2/condition_consts.go @@ -0,0 +1,39 @@ +package v1alpha2 + +const ( + // PrivateNetworkReadyCondition surfaces details about the current status of the private network. + PrivateNetworkReadyCondition = "PrivateNetworkReady" + + // PublicGatewaysReadyCondition surfaces details about the current status of the public gateways. + PublicGatewaysReadyCondition = "PublicGatewaysReady" +) + +const ( + // ReadyReason surfaces when the resource is ready. + ReadyReason = "Ready" + + // ReadyReason surfaces when the resource is not ready. + NotReadyReason = "NotReady" + + // ProvisionedReason surfaces when the resource is provisioned. + ProvisionedReason = "Provisioned" + + // NoPrivateNetworkReason surfaces when no private network is defined in the spec. + NoPrivateNetworkReason = "NoPrivateNetwork" + + // PrivateNetworkNotFoundReason surfaces when the provided private network cannot be found. + PrivateNetworkNotFoundReason = "PrivateNetworkNotfound" + + // CreationFailedReason surfaces when the resource creation failed. + CreationFailedReason = "CreationFailed" + + // ReconciliationFailedReason surfaces when the resource reconciliation failed. + ReconciliationFailedReason = "ReconciliationFailed" + + // PrivateNetworkAttachmentFailedReason surfaces when the attachment of resources to the private network failed. + PrivateNetworkAttachmentFailedReason = "PrivateNetworkAttachmentFailed" + + // InternalErrorReason surfaces unexpected errors reporting by controllers. + // In most cases, it will be required to look at controllers logs to properly triage those issues. + InternalErrorReason = "InternalError" +) diff --git a/api/v1alpha2/conversion.go b/api/v1alpha2/conversion.go new file mode 100644 index 0000000..7cfcbaf --- /dev/null +++ b/api/v1alpha2/conversion.go @@ -0,0 +1,22 @@ +package v1alpha2 + +// Hub marks this type as a conversion hub. +func (*ScalewayCluster) Hub() {} + +// Hub marks this type as a conversion hub. +func (*ScalewayClusterTemplate) Hub() {} + +// Hub marks this type as a conversion hub. +func (*ScalewayMachine) Hub() {} + +// Hub marks this type as a conversion hub. +func (*ScalewayMachineTemplate) Hub() {} + +// Hub marks this type as a conversion hub. +func (*ScalewayManagedCluster) Hub() {} + +// Hub marks this type as a conversion hub. +func (*ScalewayManagedControlPlane) Hub() {} + +// Hub marks this type as a conversion hub. +func (*ScalewayManagedMachinePool) Hub() {} diff --git a/api/v1alpha2/doc.go b/api/v1alpha2/doc.go new file mode 100644 index 0000000..0e048fa --- /dev/null +++ b/api/v1alpha2/doc.go @@ -0,0 +1,4 @@ +// Package v1alpha2 contains API Schema definitions for the infrastructure v1alpha2 API group. +// +kubebuilder:object:generate=true +// +groupName=infrastructure.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..ba441dd --- /dev/null +++ b/api/v1alpha2/groupversion_info.go @@ -0,0 +1,17 @@ +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: "infrastructure.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/scalewaycluster_types.go b/api/v1alpha2/scalewaycluster_types.go new file mode 100644 index 0000000..9078d5c --- /dev/null +++ b/api/v1alpha2/scalewaycluster_types.go @@ -0,0 +1,359 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayClusterFinalizer is the finalizer that prevents deletion of a ScalewayCluster. +const ScalewayClusterFinalizer = "scalewaycluster.infrastructure.cluster.x-k8s.io/sc-protection" + +// ScalewayClusterReadyCondition reports if the ScalewayCluster is ready. +const ScalewayClusterReadyCondition = clusterv1.ReadyCondition + +// ScalewayCluster's DomainReady condition and corresponding reasons. +const ( + // DomainReadyCondition indicates whether the domain for the control plane endpoint is ready. + ScalewayClusterDomainReadyCondition = "DomainReady" + + // ScalewayClusterNoDomainReason surfaces when no domain is defined in the spec. + // In this case, the condition is set to True as there is nothing to configure. + ScalewayClusterNoDomainReason = "NoDomain" + + // ScalewayClusterDomainReconciliationFailedReason surfaces when the domain reconciliation failed. + ScalewayClusterDomainReconciliationFailedReason = ReconciliationFailedReason + + // ScalewayClusterDomainZoneConfiguredReason surfaces when the domain zone has been successfully configured. + ScalewayClusterDomainZoneConfiguredReason = "ZoneConfigured" +) + +// ScalewayCluster's LoadBalancersReady condition and corresponding reasons. +const ( + // LoadBalancersReadyCondition indicates whether the load balancers for the control plane endpoint are ready. + ScalewayClusterLoadBalancersReadyCondition = "LoadBalancersReady" + + // ScalewayClusterLoadBalancersReadyReason surfaces when the load balancers are provisioned and ready. + ScalewayClusterLoadBalancersReadyReason = ReadyReason + + // ScalewayClusterLoadBalancersNotReadyReason surfaces when one or multiple load balancers are not ready. + ScalewayClusterLoadBalancersNotReadyReason = NotReadyReason + + // ScalewayClusterLoadBalancersInternalErrorReason surfaces when an unexpected error has occurred. + ScalewayClusterLoadBalancersInternalErrorReason = InternalErrorReason + + // ScalewayClusterMainLoadBalancerReconciliationFailedReason surfaces when the main load balancer reconciliation failed. + ScalewayClusterMainLoadBalancerReconciliationFailedReason = "MainLoadBalancerReconciliationFailed" + + // ScalewayClusterExtraLoadBalancersReconciliationFailedReason surfaces when the extra load balancers reconciliation failed. + ScalewayClusterExtraLoadBalancersReconciliationFailedReason = "ExtraLoadBalancersReconciliationFailed" + + // ScalewayClusterLoadBalancerPrivateNetworkAttachmentFailedReason when the attachment of the load balancers to the private network failed. + ScalewayClusterLoadBalancerPrivateNetworkAttachmentFailedReason = PrivateNetworkAttachmentFailedReason + + // ScalewayClusterBackendReconciliationFailedReason surfaces when the backend reconciliation failed. + ScalewayClusterBackendReconciliationFailedReason = "BackendReconciliationFailed" + + // ScalewayClusterFrontendReconciliationFailedReason surfaces when the frontend reconciliation failed. + ScalewayClusterFrontendReconciliationFailedReason = "FrontendReconciliationFailed" + + // ScalewayClusterLoadBalancerACLReconciliationFailedReason surfaces when the load balancer ACL reconciliation failed. + ScalewayClusterLoadBalancerACLReconciliationFailedReason = "LoadBalancerACLReconciliationFailed" +) + +// ScalewayClusterSpec defines the desired state of ScalewayCluster. +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)", message="controlPlaneEndpoint is required once set" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneDNS)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneDNS))",message="controlPlaneDNS cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.privateNetwork)) == (has(oldSelf.network) && has(oldSelf.network.privateNetwork))",message="privateNetwork cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.private)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.private))",message="private cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.ip)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.ip))",message="ip cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.zone)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.zone))",message="zone cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.controlPlaneLoadBalancer) && has(self.network.controlPlaneLoadBalancer.privateIP)) == (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.privateIP))",message="privateIP cannot be added or removed" +type ScalewayClusterSpec struct { + // projectID is the ID of a Scaleway project where the cluster will be created. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ProjectID UUID `json:"projectID,omitempty"` + + // region represents the region where the cluster will be hosted. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Region ScalewayRegion `json:"region,omitempty"` + + // scalewaySecretName is the name of the secret that contains the Scaleway client parameters. + // The following keys are required: SCW_ACCESS_KEY, SCW_SECRET_KEY. + // The following key is optional: SCW_API_URL. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + ScalewaySecretName string `json:"scalewaySecretName,omitempty"` + + // failureDomains is a list of failure domains where the control-plane nodes will be created. + // Failure domains correspond to Scaleway zones inside the cluster region (e.g. fr-par-1). + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=3 + FailureDomains []ScalewayZone `json:"failureDomains,omitempty"` + + // network contains network related options for the cluster. + // +optional + Network ScalewayClusterNetwork `json:"network,omitempty,omitzero"` + + // controlPlaneEndpoint represents the endpoint used to communicate with the control plane. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty,omitzero"` +} + +// ScalewayClusterNetwork defines network settings for a ScalewayCluster. +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:XValidation:rule="!has(self.controlPlaneExtraLoadBalancers) || has(self.controlPlaneDNS)",message="controlPlaneDNS is required when controlPlaneExtraLoadBalancers is set" +// +kubebuilder:validation:XValidation:rule="!has(self.publicGateways) || has(self.privateNetwork) && self.privateNetwork.enabled",message="privateNetwork is required when publicGateways is set" +// +kubebuilder:validation:XValidation:rule="!has(self.controlPlaneLoadBalancer) || !has(self.controlPlaneLoadBalancer.private) || !self.controlPlaneLoadBalancer.private || has(self.privateNetwork) && self.privateNetwork.enabled",message="privateNetwork is required when private LoadBalancer is enabled" +// +kubebuilder:validation:XValidation:rule="!has(self.controlPlaneDNS) || has(self.controlPlaneDNS) && has(self.controlPlaneDNS.domain) || has(self.controlPlaneDNS) && !has(self.controlPlaneDNS.domain) && has(self.controlPlaneLoadBalancer) && has(self.controlPlaneLoadBalancer.private) && self.controlPlaneLoadBalancer.private",message=".controlPlaneDNS.domain must be set unless control plane load balancer is private" +type ScalewayClusterNetwork struct { + // controlPlaneLoadBalancer defines settings for the load balancer of the control plane. + // +optional + ControlPlaneLoadBalancer ControlPlaneLoadBalancer `json:"controlPlaneLoadBalancer,omitempty,omitzero"` + + // controlPlaneExtraLoadBalancers allows configuring additional load balancers. + // Because Scaleway load balancers are currently zonal resources, you may set + // up to 3 additional load balancers for achieving regional redundancy. It is + // mandatory to set the controlPlaneDNS field when you do so. + // NOTE: This may be removed in the future, when Scaleway supports regional LoadBalancers. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=3 + ControlPlaneExtraLoadBalancers []LoadBalancer `json:"controlPlaneExtraLoadBalancers,omitempty"` + + // controlPlaneDNS allows configuring a Scaleway Domain DNS Zone. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ControlPlaneDNS ControlPlaneDNS `json:"controlPlaneDNS,omitempty,omitzero"` + + // privateNetwork allows attaching machines of the cluster to a Private Network. + // +optional + PrivateNetwork PrivateNetworkSpec `json:"privateNetwork,omitempty,omitzero"` + + // publicGateways allows to manage Public Gateways that will be created and + // attached to the Private Network of the cluster. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=6 + PublicGateways []PublicGateway `json:"publicGateways,omitempty"` +} + +// LoadBalancer defines load balancer parameters. +// +kubebuilder:validation:MinProperties=1 +type LoadBalancer struct { + // zone where to create the load balancer. Must be in the same region as the + // cluster. Defaults to the first zone of the region. + // +optional + Zone ScalewayZone `json:"zone,omitempty"` + + // type is the load balancer commercial offer type. + // +optional + // +kubebuilder:default="LB-S" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10 + Type string `json:"type,omitempty"` + + // ip is an existing public IPv4 to use when creating a load balancer. + // +optional + IP IPv4 `json:"ip,omitempty"` + + // privateIP is an existing private IPv4 inside the Private Network to use + // when attaching a load balancer to a Private Network. It must be pre-booked + // inside the Scaleway IPAM. + // +optional + PrivateIP IPv4 `json:"privateIP,omitempty"` +} + +// ControlPlaneLoadBalancer defines control plane load balancer settings. +// +kubebuilder:validation:MinProperties=1 +type ControlPlaneLoadBalancer struct { + // +kubebuilder:validation:XValidation:rule="!has(oldSelf.ip) || self.ip == oldSelf.ip",message="ip is immutable" + // +kubebuilder:validation:XValidation:rule="!has(oldSelf.zone) || self.zone == oldSelf.zone",message="zone is immutable" + // +kubebuilder:validation:XValidation:rule="!has(oldSelf.privateIP) || self.privateIP == oldSelf.privateIP",message="privateIP is immutable" + LoadBalancer `json:",inline"` + + // allowedRanges allows to set a list of allowed IP ranges that can access + // the cluster through the load balancer. When unset, all IP ranges are allowed. + // To allow the cluster to work properly, public IPs of nodes and Public + // Gateways will automatically be allowed. However, if this field is set, + // you MUST manually allow IPs of the nodes of your management cluster. + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=30 + AllowedRanges []CIDR `json:"allowedRanges,omitempty"` + + // private disables the creation of a public IP on the load balancers when it's set to true. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Private *bool `json:"private,omitempty"` +} + +// ControlPlaneDNS defines the DNS configuration of the control plane endpoint. +type ControlPlaneDNS struct { + // domain is the DNS Zone that this record should live in. It must be pre-existing in your Scaleway account. + // The format must be a string that conforms to the definition of a subdomain in DNS (RFC 1123). + // This is optional if the control plane load balancer is private. + // +optional + // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Domain string `json:"domain,omitempty"` + + // name is the DNS short name of the record (non-FQDN). The format must consist of + // alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. + // +required + // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$ + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + Name string `json:"name,omitempty"` +} + +// IsDefined returns true if the ControlPlaneDNS is set. +func (c *ControlPlaneDNS) IsDefined() bool { + if c == nil { + return false + } + return c.Name != "" || c.Domain != "" +} + +// PrivateNetworkSpec defines Private Network settings for the cluster. +type PrivateNetworkSpec struct { + PrivateNetwork `json:",inline"` + + // enabled allows to automatically attach machines to a Private Network when it's set to true. + // The Private Network is automatically created if no existing Private + // Network ID is provided. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Enabled *bool `json:"enabled,omitempty"` +} + +// ScalewayClusterStatus defines the observed state of ScalewayCluster. +// +kubebuilder:validation:MinProperties=1 +type ScalewayClusterStatus struct { + // conditions represent the current state of the ScalewayCluster resource. + // Each condition has a unique type and reflects the status of a specific aspect of the resource. + // + // The status of each condition is one of True, False, or Unknown. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // failureDomains is a list of failure domain objects synced from the infrastructure provider. + // +optional + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + FailureDomains []clusterv1.FailureDomain `json:"failureDomains,omitempty"` + + // initialization provides observations of the ScalewayCluster initialization process. + // NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning. + // +optional + Initialization ScalewayClusterInitializationStatus `json:"initialization,omitempty,omitzero"` + + // network contains information about network resources of the cluster. + // +optional + Network ScalewayClusterNetworkStatus `json:"network,omitempty,omitzero"` +} + +// ScalewayClusterInitializationStatus provides observations of the ScalewayCluster initialization process. +// +kubebuilder:validation:MinProperties=1 +type ScalewayClusterInitializationStatus struct { + // provisioned is true when the infrastructure provider reports that the Cluster's infrastructure is fully provisioned. + // NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Cluster provisioning. + // +optional + Provisioned *bool `json:"provisioned,omitempty"` +} + +// ScalewayClusterNetworkStatus contains information about network resources of the cluster. +// +kubebuilder:validation:MinProperties=1 +type ScalewayClusterNetworkStatus struct { + // vpcID is set if the cluster has an associated Private Network. + // +optional + VPCID UUID `json:"vpcID,omitempty"` + + // privateNetworkID is set if the cluster has an associated Private Network. + // +optional + PrivateNetworkID UUID `json:"privateNetworkID,omitempty"` + + // publicGatewayIDs is a list of Public Gateway IDs. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + PublicGatewayIDs []UUID `json:"publicGatewayIDs,omitempty"` + + // loadBalancerIP is the public IP of the cluster control-plane. + // +optional + LoadBalancerIP IPv4 `json:"loadBalancerIP,omitempty"` + + // extraLoadBalancerIPs is a list of IPs of the extra loadbalancers. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + ExtraLoadBalancerIPs []IPv4 `json:"extraLoadBalancerIPs,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=scalewayclusters,scope=Namespaced,categories=cluster-api,shortName=sc +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Host",type="string",JSONPath=".spec.controlPlaneEndpoint.host",description="Host of the control plane" +// +kubebuilder:printcolumn:name="Port",type="integer",JSONPath=".spec.controlPlaneEndpoint.port",description="Port of the control plane" +// +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.region",description="Region of the cluster" +// +kubebuilder:printcolumn:name="Provisioned",type="boolean",JSONPath=".status.initialization.provisioned",description="Provisioned is true when the cluster infrastructure is fully provisioned" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=`.status.conditions[?(@.type=="Ready")].status`,description="ScalewayCluster pass all readiness checks" + +// ScalewayCluster is the Schema for the scalewayclusters API +// +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" +// +kubebuilder:validation:XValidation:rule="self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')",message="name must be a valid DNS label" +type ScalewayCluster struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayCluster + // +required + Spec ScalewayClusterSpec `json:"spec,omitempty,omitzero"` + + // status defines the observed state of ScalewayCluster + // +optional + Status ScalewayClusterStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayClusterList contains a list of ScalewayCluster +type ScalewayClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayCluster `json:"items"` +} + +// GetConditions returns the list of conditions for an ScalewayCluster API object. +func (s *ScalewayCluster) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions will set the given conditions on an ScalewayCluster object. +func (s *ScalewayCluster) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +func init() { + SchemeBuilder.Register(&ScalewayCluster{}, &ScalewayClusterList{}) +} diff --git a/api/v1alpha2/scalewayclustertemplate_types.go b/api/v1alpha2/scalewayclustertemplate_types.go new file mode 100644 index 0000000..e3d0077 --- /dev/null +++ b/api/v1alpha2/scalewayclustertemplate_types.go @@ -0,0 +1,54 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayClusterTemplateSpec defines the desired state of ScalewayClusterTemplate +type ScalewayClusterTemplateSpec struct { + // template is a ScalewayCluster template resource. + // +required + Template ScalewayClusterTemplateResource `json:"template,omitempty,omitzero"` +} + +type ScalewayClusterTemplateResource struct { + // metadata is a Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayCluster + // +required + Spec ScalewayClusterSpec `json:"spec,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=scalewayclustertemplates,scope=Namespaced,categories=cluster-api,shortName=sct +// +kubebuilder:storageversion + +// ScalewayClusterTemplate is the Schema for the scalewayclustertemplates API +type ScalewayClusterTemplate struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayClusterTemplate + // +required + Spec ScalewayClusterTemplateSpec `json:"spec,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayClusterTemplateList contains a list of ScalewayClusterTemplate +type ScalewayClusterTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayClusterTemplate `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ScalewayClusterTemplate{}, &ScalewayClusterTemplateList{}) +} diff --git a/api/v1alpha2/scalewaymachine_types.go b/api/v1alpha2/scalewaymachine_types.go new file mode 100644 index 0000000..3cda940 --- /dev/null +++ b/api/v1alpha2/scalewaymachine_types.go @@ -0,0 +1,219 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayMachineFinalizer is the finalizer that prevents deletion of a ScalewayMachine. +const ScalewayMachineFinalizer = "scalewaymachine.infrastructure.cluster.x-k8s.io/sm-protection" + +// ScalewayMachineReadyCondition reports if the ScalewayMachine is ready. +const ScalewayMachineReadyCondition = clusterv1.ReadyCondition + +// ScalewayMachine's InstanceReady condition and corresponding reasons. +const ( + // ScalewayMachineInstanceReadyCondition indicates whether the Scaleway instance is ready. + ScalewayMachineInstanceReadyCondition = "InstanceReady" + + // ScalewayMachineInstanceReadyReason surfaces when the Scaleway instance is ready. + ScalewayMachineInstanceReadyReason = ReadyReason + + // ScalewayMachineInstanceReconciliationFailedReason surfaces when there is a failure in reconciling the Scaleway instance. + ScalewayMachineInstanceReconciliationFailedReason = ReconciliationFailedReason +) + +// ScalewayMachineSpec defines the desired state of ScalewayMachine. +// +kubebuilder:validation:XValidation:rule="has(self.rootVolume) == has(oldSelf.rootVolume)",message="rootVolume cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.publicNetwork) == has(oldSelf.publicNetwork)",message="publicNetwork cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.placementGroup) == has(oldSelf.placementGroup)",message="placementGroup cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.securityGroup) == has(oldSelf.securityGroup)",message="securityGroup cannot be added or removed" +type ScalewayMachineSpec struct { + // providerID must match the provider ID as seen on the node object corresponding to this machine. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=512 + ProviderID string `json:"providerID,omitempty"` + + // commercialType of instance (e.g. PRO2-S). + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=20 + CommercialType string `json:"commercialType,omitempty"` + + // image defines an image ID, Name or Label to use to create the instance. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Image Image `json:"image,omitempty,omitzero"` + + // rootVolume defines the characteristics of the system (root) volume. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + RootVolume RootVolume `json:"rootVolume,omitempty,omitzero"` + + // publicNetwork allows attaching public IPs to the instance. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + PublicNetwork PublicNetwork `json:"publicNetwork,omitempty,omitzero"` + + // placementGroup allows attaching a Placement Group to the instance. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + PlacementGroup IDOrName `json:"placementGroup,omitempty,omitzero"` + + // securityGroup allows attaching a Security Group to the instance. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + SecurityGroup IDOrName `json:"securityGroup,omitempty,omitzero"` +} + +// Image contains an ID, Name or Label to use to create the instance. +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:MaxProperties=1 +type Image struct { + IDOrName `json:",inline"` + + // label of the image (as defined in the marketplace). + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=100 + Label string `json:"label,omitempty"` +} + +// IDOrName contains an ID or Name of an existing Scaleway resource. +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:MaxProperties=1 +type IDOrName struct { + // id of the Scaleway resource. + // +optional + ID UUID `json:"id,omitempty"` + + // name of the Scaleway resource. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=100 + Name string `json:"name,omitempty"` +} + +// RootVolume defines the characteristics of the system (root) volume. +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:XValidation:rule="!has(self.iops) || has(self.type) && self.type == 'block'",message="iops can only be set for block volumes" +type RootVolume struct { + // size of the root volume in GB. Defaults to 20 GB. + // +optional + // +kubebuilder:default=20 + // +kubebuilder:validation:Minimum=8 + // +kubebuilder:validation:Maximum=10000 + Size int64 `json:"size,omitempty"` + + // type of the root volume. Can be local or block. Note that not all types + // of instances support local volumes. + // +optional + // +kubebuilder:default="block" + // +kubebuilder:validation:Enum=local;block + Type string `json:"type,omitempty"` + + // iops is the number of IOPS requested for the disk. This is only applicable for block volumes. + // +optional + // +kubebuilder:validation:Minimum=5000 + IOPS int64 `json:"iops,omitempty"` +} + +// PublicNetwork allows enabling the attachment of public IPs to the instance. +// +kubebuilder:validation:MinProperties=1 +type PublicNetwork struct { + // enableIPv4 defines whether server should have an IPv4 created and attached. + // +optional + EnableIPv4 *bool `json:"enableIPv4,omitempty"` + + // enableIPv6 defines whether server should have an IPv6 created and attached. + // +optional + EnableIPv6 *bool `json:"enableIPv6,omitempty"` +} + +// ScalewayMachineStatus defines the observed state of ScalewayMachine. +// +kubebuilder:validation:MinProperties=1 +type ScalewayMachineStatus struct { + // conditions represent the current state of the ScalewayMachine resource. + // Each condition has a unique type and reflects the status of a specific aspect of the resource. + // + // The status of each condition is one of True, False, or Unknown. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // initialization provides observations of the ScalewayMachine initialization process. + // NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning. + // +optional + Initialization ScalewayMachineInitializationStatus `json:"initialization,omitempty,omitzero"` + + // addresses contains the associated addresses for the machine. + // +optional + // +listType=atomic + // +kubebuilder:validation:MaxItems=32 + Addresses []clusterv1.MachineAddress `json:"addresses,omitempty"` +} + +// ScalewayMachineInitializationStatus provides observations of the ScalewayMachine initialization process. +// +kubebuilder:validation:MinProperties=1 +type ScalewayMachineInitializationStatus struct { + // provisioned is true when the infrastructure provider reports that the Machine's infrastructure is fully provisioned. + // NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Machine provisioning. + // +optional + Provisioned *bool `json:"provisioned,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=scalewaymachines,scope=Namespaced,categories=cluster-api,shortName=sm +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="CommercialType",type="string",JSONPath=".spec.commercialType",description="Instance commercial type" +// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="Node provider ID" +// +kubebuilder:printcolumn:name="Provisioned",type="boolean",JSONPath=".status.initialization.provisioned",description="Provisioned is true when the machine infrastructure is fully provisioned" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=`.status.conditions[?(@.type=="Ready")].status`,description="ScalewayMachine pass all readiness checks" + +// ScalewayMachine is the Schema for the scalewaymachines API +// +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" +// +kubebuilder:validation:XValidation:rule="self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')",message="name must be a valid DNS label" +type ScalewayMachine struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayMachine + // +required + Spec ScalewayMachineSpec `json:"spec,omitempty,omitzero"` + + // status defines the observed state of ScalewayMachine + // +optional + Status ScalewayMachineStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayMachineList contains a list of ScalewayMachine +type ScalewayMachineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayMachine `json:"items"` +} + +// GetConditions returns the list of conditions for an ScalewayMachine API object. +func (s *ScalewayMachine) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions will set the given conditions on an ScalewayMachine object. +func (s *ScalewayMachine) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +func init() { + SchemeBuilder.Register(&ScalewayMachine{}, &ScalewayMachineList{}) +} diff --git a/api/v1alpha2/scalewaymachinetemplate_types.go b/api/v1alpha2/scalewaymachinetemplate_types.go new file mode 100644 index 0000000..a884307 --- /dev/null +++ b/api/v1alpha2/scalewaymachinetemplate_types.go @@ -0,0 +1,54 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayMachineTemplateSpec defines the desired state of ScalewayMachineTemplate +type ScalewayMachineTemplateSpec struct { + // template is a ScalewayMachine template resource. + // +required + Template ScalewayMachineTemplateResource `json:"template,omitempty,omitzero"` +} + +type ScalewayMachineTemplateResource struct { + // metadata is a Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // +optional + ObjectMeta clusterv1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayMachine + // +required + Spec ScalewayMachineSpec `json:"spec,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=scalewaymachinetemplates,scope=Namespaced,categories=cluster-api,shortName=smt +// +kubebuilder:storageversion + +// ScalewayMachineTemplate is the Schema for the scalewaymachinetemplates API +type ScalewayMachineTemplate struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayMachineTemplate + // +required + Spec ScalewayMachineTemplateSpec `json:"spec,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayMachineTemplateList contains a list of ScalewayMachineTemplate +type ScalewayMachineTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayMachineTemplate `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ScalewayMachineTemplate{}, &ScalewayMachineTemplateList{}) +} diff --git a/api/v1alpha2/scalewaymanagedcluster_types.go b/api/v1alpha2/scalewaymanagedcluster_types.go new file mode 100644 index 0000000..fa6e5f1 --- /dev/null +++ b/api/v1alpha2/scalewaymanagedcluster_types.go @@ -0,0 +1,153 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayManagedClusterFinalizer is the finalizer that prevents deletion of a ScalewayManagedCluster. +const ScalewayManagedClusterFinalizer = "scalewaymanagedcluster.infrastructure.cluster.x-k8s.io/smc-protection" + +// ScalewayManagedClusterReadyCondition reports if the ScalewayManagedCluster is ready. +const ScalewayManagedClusterReadyCondition = clusterv1.ReadyCondition + +// ScalewayManagedClusterSpec defines the desired state of ScalewayManagedCluster. +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)", message="controlPlaneEndpoint is required once set" +// +kubebuilder:validation:XValidation:rule="(has(self.network) && has(self.network.privateNetwork)) == (has(oldSelf.network) && has(oldSelf.network.privateNetwork))",message="privateNetwork cannot be added or removed" +type ScalewayManagedClusterSpec struct { + // region where the managed cluster will be created. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Region ScalewayRegion `json:"region,omitempty"` + + // projectID in which the managed cluster will be created. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ProjectID UUID `json:"projectID,omitempty"` + + // scalewaySecretName is the name of the secret that contains the Scaleway client parameters. + // The following keys are required: SCW_ACCESS_KEY, SCW_SECRET_KEY. + // The following key is optional: SCW_API_URL. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + ScalewaySecretName string `json:"scalewaySecretName,omitempty"` + + // network defines the network configuration of the managed cluster. + // +optional + Network ScalewayManagedClusterNetwork `json:"network,omitempty,omitzero"` + + // controlPlaneEndpoint represents the endpoint used to communicate with the control plane. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty,omitzero"` +} + +// ScalewayManagedClusterNetwork defines the network configuration of a managed cluster. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedClusterNetwork struct { + // privateNetwork allows attaching machines of the cluster to a Private Network. + // +optional + PrivateNetwork PrivateNetwork `json:"privateNetwork,omitempty,omitzero"` + + // publicGateways allows to manage Public Gateways that will be created and + // attached to the Private Network of the cluster. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=6 + PublicGateways []PublicGateway `json:"publicGateways,omitempty"` +} + +// ScalewayManagedClusterStatus defines the observed state of ScalewayManagedCluster. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedClusterStatus struct { + // conditions represent the current state of the ScalewayManagedCluster resource. + // Each condition has a unique type and reflects the status of a specific aspect of the resource. + // + // The status of each condition is one of True, False, or Unknown. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // initialization provides observations of the ScalewayManagedCluster initialization process. + // NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning. + // +optional + Initialization ScalewayManagedClusterInitializationStatus `json:"initialization,omitempty,omitzero"` + + // network contains information about currently provisioned network resources. + // +optional + Network ScalewayManagedClusterNetworkStatus `json:"network,omitempty,omitzero"` +} + +// ScalewayManagedClusterInitializationStatus provides observations of the ScalewayManagedCluster initialization process. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedClusterInitializationStatus struct { + // provisioned is true when the infrastructure provider reports that the Cluster's infrastructure is fully provisioned. + // NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Cluster provisioning. + // +optional + Provisioned *bool `json:"provisioned,omitempty"` +} + +// ScalewayManagedClusterNetworkStatus contains information about currently provisioned network resources. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedClusterNetworkStatus struct { + // privateNetworkID is the ID of the Private Network that is attached to the cluster. + // +optional + PrivateNetworkID UUID `json:"privateNetworkID,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=scalewaymanagedclusters,scope=Namespaced,categories=cluster-api,shortName=smc +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this ScalewayManagedCluster belongs" +// +kubebuilder:printcolumn:name="Provisioned",type="boolean",JSONPath=".status.initialization.provisioned",description="Provisioned is true when the cluster infrastructure is fully provisioned" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=`.status.conditions[?(@.type=="Ready")].status`,description="ScalewayManagedCluster pass all readiness checks" +// +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.region",description="Region of the managed cluster" +// +kubebuilder:printcolumn:name="Host",type="string",JSONPath=".spec.controlPlaneEndpoint.host",description="Host of the control plane" +// +kubebuilder:printcolumn:name="Port",type="integer",JSONPath=".spec.controlPlaneEndpoint.port",description="Port of the control plane" + +// ScalewayManagedCluster is the Schema for the scalewaymanagedclusters API +// +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" +// +kubebuilder:validation:XValidation:rule="self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')",message="name must be a valid DNS label" +type ScalewayManagedCluster struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayManagedCluster + // +required + Spec ScalewayManagedClusterSpec `json:"spec,omitempty,omitzero"` + + // status defines the observed state of ScalewayManagedCluster + // +optional + Status ScalewayManagedClusterStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayManagedClusterList contains a list of ScalewayManagedCluster +type ScalewayManagedClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayManagedCluster `json:"items"` +} + +// GetConditions returns the list of conditions for an ScalewayManagedCluster API object. +func (s *ScalewayManagedCluster) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions will set the given conditions on an ScalewayManagedCluster object. +func (s *ScalewayManagedCluster) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +func init() { + SchemeBuilder.Register(&ScalewayManagedCluster{}, &ScalewayManagedClusterList{}) +} diff --git a/api/v1alpha2/scalewaymanagedcontrolplane_types.go b/api/v1alpha2/scalewaymanagedcontrolplane_types.go new file mode 100644 index 0000000..9a16fb2 --- /dev/null +++ b/api/v1alpha2/scalewaymanagedcontrolplane_types.go @@ -0,0 +1,403 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayManagedControlPlaneFinalizer is the finalizer that prevents deletion of a ScalewayManagedControlPlane. +const ScalewayManagedControlPlaneFinalizer = "scalewaymanagedcontrolplane.infrastructure.cluster.x-k8s.io/smcp-protection" + +// ScalewayManagedControlPlaneReadyCondition reports if the ScalewayManagedControlPlane is ready. +const ScalewayManagedControlPlaneReadyCondition = clusterv1.ReadyCondition + +// ScalewayManagedControlPlane's ClusterReady condition and corresponding reasons. +const ( + // ScalewayManagedControlPlaneClusterReadyCondition indicates whether + // the Scaleway Kubernetes Cluster is ready. + ScalewayManagedControlPlaneClusterReadyCondition = "ClusterReady" + + // ScalewayManagedControlPlaneClusterReadyReason surfaces when the Scaleway Kubernetes Cluster is ready. + ScalewayManagedControlPlaneClusterReadyReason = ReadyReason + + // ScalewayManagedControlPlaneClusterReconciliationFailedReason surfaces + // when there is a failure in reconciling the Scaleway Kubernetes Cluster. + ScalewayManagedControlPlaneClusterReconciliationFailedReason = ReconciliationFailedReason + + // ScalewayManagedControlPlaneClusterTransientStatusReason surfaces when the + // Scaleway Kubernetes Cluster has a transient status. + ScalewayManagedControlPlaneClusterTransientStatusReason = "TransientStatus" +) + +// ScalewayManagedControlPlaneSpec defines the desired state of ScalewayManagedControlPlane. +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)", message="controlPlaneEndpoint is required once set" +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.clusterName) || has(self.clusterName) == has(oldSelf.clusterName)",message="clusterName cannot be removed once set" +// +kubebuilder:validation:XValidation:rule="has(self.cni) == has(oldSelf.cni)",message="cni cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.enablePrivateEndpoint) == has(oldSelf.enablePrivateEndpoint)",message="enablePrivateEndpoint cannot be added or removed" +type ScalewayManagedControlPlaneSpec struct { + // clusterName allows you to specify the name of the Scaleway managed cluster. + // If you don't specify a name then a default name will be created + // based on the namespace and name of the managed control plane. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=100 + ClusterName string `json:"clusterName,omitempty"` + + // type of the cluster (e.g. kapsule, multicloud, etc.). + // +optional + // +kubebuilder:default="kapsule" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=50 + Type string `json:"type,omitempty"` + + // version defines the desired Kubernetes version. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Version string `json:"version,omitempty"` + + // cni plugin running in the cluster. + // +optional + // +kubebuilder:validation:Enum=cilium;cilium_native;calico;kilo;none + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + CNI string `json:"cni,omitempty"` + + // additionalTags that will be added to the default tags. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=30 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=128 + AdditionalTags []string `json:"additionalTags,omitempty"` + + // autoscaler configuration of the cluster. + // +optional + Autoscaler Autoscaler `json:"autoscaler,omitempty,omitzero"` + + // autoUpgrade configuration of the cluster. + // +optional + AutoUpgrade AutoUpgrade `json:"autoUpgrade,omitempty,omitzero"` + + // featureGates to enable. + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=128 + FeatureGates []string `json:"featureGates,omitempty"` + + // admissionPlugins to enable. + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=128 + AdmissionPlugins []string `json:"admissionPlugins,omitempty"` + + // openIDConnect defines the OpenID Connect configuration of the Kubernetes API server. + // +optional + OpenIDConnect OpenIDConnect `json:"openIDConnect,omitempty,omitzero"` + + // apiServerCertSANs defines additional Subject Alternative Names for the + // Kubernetes API server certificate. + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=255 + APIServerCertSANs []string `json:"apiServerCertSANs,omitempty"` + + // onDelete configures the settings to apply when deleting the Scaleway managed cluster. + // +optional + OnDelete OnDelete `json:"onDelete,omitempty,omitzero"` + + // acl configures the ACLs of the managed cluster. If not set, ACLs will be set to [0.0.0.0/0]. + // +optional + ACL *ACL `json:"acl,omitempty"` + + // enablePrivateEndpoint defines whether the apiserver's internal address + // is used as the cluster endpoint. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + EnablePrivateEndpoint *bool `json:"enablePrivateEndpoint,omitempty"` + + // controlPlaneEndpoint represents the endpoint used to communicate with the control plane. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitempty,omitzero"` +} + +// Autoscaler allows you to set (to an extent) your preferred autoscaler configuration, +// which is an implementation of the cluster-autoscaler (https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler/). +// +kubebuilder:validation:MinProperties=1 +type Autoscaler struct { + // scaleDownDisabled allows to disable the cluster autoscaler. + // +optional + ScaleDownDisabled *bool `json:"scaleDownDisabled,omitempty"` + + // scaleDownDelayAfterAdd defines how long after scale up the scale down evaluation resumes. + // +optional + // +kubebuilder:validation:Format="duration" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10 + ScaleDownDelayAfterAdd string `json:"scaleDownDelayAfterAdd,omitempty"` + + // estimator is the type of resource estimator to be used in scale up. + // +optional + // +kubebuilder:validation:Enum=binpacking + Estimator string `json:"estimator,omitempty"` + + // expander is the type of node group expander to be used in scale up. + // +optional + // +kubebuilder:validation:Enum=random;most_pods;least_waste;priority;price + Expander string `json:"expander,omitempty"` + + // ignoreDaemonsetsUtilization allows to ignore DaemonSet pods when calculating + // resource utilization for scaling down. + // +optional + IgnoreDaemonsetsUtilization *bool `json:"ignoreDaemonsetsUtilization,omitempty"` + + // balanceSimilarNodeGroups allows to detect similar node groups and balance + // the number of nodes between them. + // +optional + BalanceSimilarNodeGroups *bool `json:"balanceSimilarNodeGroups,omitempty"` + + // expendablePodsPriorityCutoff defines the priority threshold below which pods + // are considered expendable. Pods with priority below cutoff will be expendable. + // They can be killed without any consideration during scale down and they won't cause scale up. + // Pods with null priority (PodPriority disabled) are non expendable. + // +optional + ExpendablePodsPriorityCutoff *int32 `json:"expendablePodsPriorityCutoff,omitempty"` + + // scaleDownUnneededTime defines how long a node should be unneeded before it + // is eligible to be scaled down. + // +optional + // +kubebuilder:validation:Format="duration" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10 + ScaleDownUnneededTime string `json:"scaleDownUnneededTime,omitempty"` + + // scaleDownUtilizationThreshold is the Node utilization level, defined as a + // sum of requested resources divided by capacity, below which a node can be + // considered for scale down. + // +optional + // +kubebuilder:validation:Format="float" + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=10 + ScaleDownUtilizationThreshold string `json:"scaleDownUtilizationThreshold,omitempty"` + + // maxGracefulTerminationSec is the maximum number of seconds the cluster autoscaler + // waits for pod termination when trying to scale down a node. + // +optional + // +kubebuilder:validation:Minimum=1 + MaxGracefulTerminationSec int32 `json:"maxGracefulTerminationSec,omitempty"` +} + +// AutoUpgrade allows to set a specific 2-hour time window in which the cluster +// can be automatically updated to the latest patch version. +type AutoUpgrade struct { + // enabled defines whether auto upgrade is enabled for the cluster. + // +required + Enabled *bool `json:"enabled,omitempty"` + + // maintenanceWindow of the cluster auto upgrades. + // +optional + MaintenanceWindow MaintenanceWindow `json:"maintenanceWindow,omitempty,omitzero"` +} + +// MaintenanceWindow defines the window of the cluster auto upgrades. +// +kubebuilder:validation:MinProperties=1 +type MaintenanceWindow struct { + // startHour is the start time of the two-hour maintenance window. + // +optional + // +kubebuilder:validation:Minimum=0 + StartHour *int32 `json:"startHour,omitempty"` + + // day of the week for the maintenance window. + // +optional + // +kubebuilder:validation:Enum=any;monday;tuesday;wednesday;thursday;friday;saturday;sunday + Day string `json:"day,omitempty"` +} + +// OpenIDConnect defines the OpenID Connect configuration of the Kubernetes API server. +type OpenIDConnect struct { + // issuerURL of the provider which allows the API server to discover public signing keys. + // Only URLs using the https:// scheme are accepted. This is typically the provider's + // discovery URL without a path, for example "https://accounts.google.com" or "https://login.salesforce.com". + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=255 + IssuerURL string `json:"issuerURL,omitempty"` + + // clientID is a client ID that all tokens must be issued for. + // +required + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=255 + ClientID string `json:"clientID,omitempty"` + + // usernameClaim is the JWT claim to use as the user name. The default is "sub", + // which is expected to be the end user's unique identifier. Admins can choose other claims, + // such as email or name, depending on their provider. However, claims other + // than email will be prefixed with the issuer URL to prevent name collision. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=100 + UsernameClaim string `json:"usernameClaim,omitempty"` + + // usernamePrefix is the prefix prepended to username claims to prevent name collision (such as "system:" users). + // For example, the value "oidc:"" will create usernames like "oidc:jane.doe". + // If this flag is not provided and "username_claim" is a value other than email, + // the prefix defaults to "( Issuer URL )#" where "( Issuer URL )" is the value of "issuer_url". + // The value "-" can be used to disable all prefixing. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=100 + UsernamePrefix string `json:"usernamePrefix,omitempty"` + + // groupsClaim is the JWT claim to use as the user's group. + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=100 + GroupsClaim []string `json:"groupsClaim,omitempty"` + + // groupsPrefix is the prefix prepended to group claims to prevent name collision (such as "system:" groups). + // For example, the value "oidc:" will create group names like "oidc:engineering" and "oidc:infra". + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=100 + GroupsPrefix string `json:"groupsPrefix,omitempty"` + + // requiredClaim is multiple key=value pairs describing a required claim in the ID token. + // If set, the claims are verified to be present in the ID token with a matching value. + // +optional + // +listType=set + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=100 + RequiredClaim []string `json:"requiredClaim,omitempty"` +} + +// OnDelete configures the settings to apply when deleting the Scaleway managed cluster. +// +kubebuilder:validation:MinProperties=1 +type OnDelete struct { + // withAdditionalResources allows to also automatically delete all volumes + // (including those with volume type "retain"), empty Private Networks and + // Load Balancers whose names start with cluster ID. + // +optional + WithAdditionalResources *bool `json:"withAdditionalResources,omitempty"` +} + +// ACL configures the ACLs of the managed cluster. +type ACL struct { + // allowedRanges is a list of allowed public IP ranges that can access + // the managed cluster. When empty, all IP ranges are DENIED. Make sure the nodes + // of your management cluster can still access the cluster by allowing their IPs. + // +kubebuilder:validation:MaxItems=30 + // +optional + // +listType=set + AllowedRanges []CIDR `json:"allowedRanges,omitempty"` +} + +// ScalewayManagedControlPlaneStatus defines the observed state of ScalewayManagedControlPlane. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedControlPlaneStatus struct { + // conditions represent the current state of the ScalewayManagedControlPlane resource. + // Each condition has a unique type and reflects the status of a specific aspect of the resource. + // + // The status of each condition is one of True, False, or Unknown. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // version defines the desired Kubernetes version for the control plane. + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + Version string `json:"version,omitempty"` + + // externalManagedControlPlane is a bool that should be set to true if the + // Node objects do not exist in the cluster. + // +optional + // +kubebuilder:default=true + ExternalManagedControlPlane *bool `json:"externalManagedControlPlane,omitempty"` + + // initialization provides observations of the ScalewayManagedControlPlane initialization process. + // NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning. + // +optional + Initialization ScalewayManagedControlPlaneInitializationStatus `json:"initialization,omitempty,omitzero"` +} + +// ScalewayManagedControlPlaneInitializationStatus provides observations of the ScalewayManagedControlPlane initialization process. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedControlPlaneInitializationStatus struct { + // controlPlaneInitialized is true when the control plane provider reports that the Kubernetes control plane is initialized; + // usually a control plane is considered initialized when it can accept requests, no matter if this happens before + // the control plane is fully provisioned or not. + // NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Cluster provisioning. + // +optional + ControlPlaneInitialized *bool `json:"controlPlaneInitialized,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=scalewaymanagedcontrolplanes,scope=Namespaced,categories=cluster-api,shortName=smcp +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this ScalewayManagedControlPlane belongs" +// +kubebuilder:printcolumn:name="Initialized",type=boolean,JSONPath=".status.initialization.controlPlaneInitialized",description="This denotes whether or not the control plane can accept requests" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=`.status.conditions[?(@.type=="Ready")].status`,description="ScalewayManagedControlPlane pass all readiness checks" +// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description="The Kubernetes version of the Scaleway control plane" +// +kubebuilder:printcolumn:name="Host",type="string",JSONPath=".spec.controlPlaneEndpoint.host",description="Host of the control plane" +// +kubebuilder:printcolumn:name="Port",type="integer",JSONPath=".spec.controlPlaneEndpoint.port",description="Port of the control plane" + +// ScalewayManagedControlPlane is the Schema for the scalewaymanagedcontrolplanes API +// +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" +// +kubebuilder:validation:XValidation:rule="self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')",message="name must be a valid DNS label" +type ScalewayManagedControlPlane struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayManagedControlPlane + // +required + Spec ScalewayManagedControlPlaneSpec `json:"spec,omitempty,omitzero"` + + // status defines the observed state of ScalewayManagedControlPlane + // +optional + Status ScalewayManagedControlPlaneStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayManagedControlPlaneList contains a list of ScalewayManagedControlPlane +type ScalewayManagedControlPlaneList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayManagedControlPlane `json:"items"` +} + +// GetConditions returns the list of conditions for an ScalewayManagedControlPlane API object. +func (s *ScalewayManagedControlPlane) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions will set the given conditions on an ScalewayManagedControlPlane object. +func (s *ScalewayManagedControlPlane) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +func init() { + SchemeBuilder.Register(&ScalewayManagedControlPlane{}, &ScalewayManagedControlPlaneList{}) +} diff --git a/api/v1alpha2/scalewaymanagedmachinepool_types.go b/api/v1alpha2/scalewaymanagedmachinepool_types.go new file mode 100644 index 0000000..228495e --- /dev/null +++ b/api/v1alpha2/scalewaymanagedmachinepool_types.go @@ -0,0 +1,239 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// ScalewayManagedMachinePoolFinalizer is the finalizer that prevents deletion of a ScalewayManagedMachinePool. +const ScalewayManagedMachinePoolFinalizer = "scalewaymanagedmachinepool.infrastructure.cluster.x-k8s.io/smmp-protection" + +// ScalewayManagedMachinePoolReadyCondition reports if the ScalewayManagedMachinePool is ready. +const ScalewayManagedMachinePoolReadyCondition = clusterv1.ReadyCondition + +// ScalewayManagedMachinePool's PoolReady condition and corresponding reasons. +const ( + // ScalewayManagedMachinePoolPoolReadyCondition indicates whether the Scaleway Kubernetes Pool is ready. + ScalewayManagedMachinePoolPoolReadyCondition = "PoolReady" + + // ScalewayManagedMachinePoolPoolReadyReason surfaces when the Scaleway Kubernetes Pool is ready. + ScalewayManagedMachinePoolPoolReadyReason = ReadyReason + + // ScalewayManagedMachinePoolPoolReconciliationFailedReason surfaces + // when there is a failure in reconciling the Scaleway Kubernetes Pool. + ScalewayManagedMachinePoolPoolReconciliationFailedReason = ReconciliationFailedReason + + // ScalewayManagedMachinePoolPoolTransientStatusReason surfaces when the + // Scaleway Kubernetes Pool has a transient status. + ScalewayManagedMachinePoolPoolTransientStatusReason = "TransientStatus" +) + +// ScalewayManagedMachinePoolSpec defines the desired state of ScalewayManagedMachinePool. +// +kubebuilder:validation:XValidation:rule="has(self.placementGroupID) == has(oldSelf.placementGroupID)",message="placementGroupID cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.rootVolumeType) == has(oldSelf.rootVolumeType)",message="rootVolumeType cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.rootVolumeSizeGB) == has(oldSelf.rootVolumeSizeGB)",message="rootVolumeSizeGB cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.publicIPDisabled) == has(oldSelf.publicIPDisabled)",message="publicIPDisabled cannot be added or removed" +// +kubebuilder:validation:XValidation:rule="has(self.securityGroupID) == has(oldSelf.securityGroupID)",message="securityGroupID cannot be added or removed" +type ScalewayManagedMachinePoolSpec struct { + // nodeType is the type of Scaleway Instance wanted for the pool. Nodes with + // insufficient memory are not eligible (DEV1-S, PLAY2-PICO, STARDUST). + // "external" is a special node type used to provision instances from other + // cloud providers in a Kosmos Cluster. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:MinLength=2 + // +kubebuilder:validation:MaxLength=30 + NodeType string `json:"nodeType,omitempty"` + + // zone in which the pool's nodes will be spawned. + // +required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Zone ScalewayZone `json:"zone,omitempty"` + + // placementGroupID in which all the nodes of the pool will be created, + // placement groups are limited to 20 instances. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + PlacementGroupID UUID `json:"placementGroupID,omitempty"` + + // scaling configures the scaling of the pool. + // +optional + Scaling Scaling `json:"scaling,omitempty,omitzero"` + + // autohealing defines whether the autohealing feature is enabled for the pool. + // +optional + Autohealing *bool `json:"autohealing,omitempty"` + + // additionalTags that will be added to the default tags. + // +optional + // +listType=atomic + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=30 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=128 + AdditionalTags []string `json:"additionalTags,omitempty"` + + // kubeletArgs defines Kubelet arguments to be used by this pool. + // +optional + KubeletArgs map[string]string `json:"kubeletArgs,omitempty"` + + // upgradePolicy defines the pool's upgrade policy. + // +optional + UpgradePolicy UpgradePolicy `json:"upgradePolicy,omitempty,omitzero"` + + // rootVolumeType is the system volume disk type. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:Enum=l_ssd;sbs_5k;sbs_15k + RootVolumeType string `json:"rootVolumeType,omitempty"` + + // rootVolumeSizeGB is the size of the System volume disk size, in GB. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +kubebuilder:validation:Minimum=20 + RootVolumeSizeGB int64 `json:"rootVolumeSizeGB,omitempty"` + + // publicIPDisabled defines if the public IP should be removed from Nodes. + // To use this feature, your Cluster must have an attached Private Network + // set up with a Public Gateway. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + PublicIPDisabled *bool `json:"publicIPDisabled,omitempty"` + + // securityGroupID in which all the nodes of the pool will be created. If unset, + // the pool will use default Kapsule security group in current zone. + // +optional + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + SecurityGroupID UUID `json:"securityGroupID,omitempty"` + + // providerIDList are the identification IDs of machine instances provided by the provider. + // This field must match the provider IDs as seen on the node objects corresponding to a machine pool's machine instances. + // +optional + // +listType=atomic + // +kubebuilder:validation:MaxItems=10000 + // +kubebuilder:validation:items:MinLength=1 + // +kubebuilder:validation:items:MaxLength=512 + ProviderIDList []string `json:"providerIDList,omitempty"` +} + +// Scaling defines the scaling parameters of the pool. +// +kubebuilder:validation:MinProperties=1 +type Scaling struct { + // autoscaling defines whether the autoscaling feature is enabled for the pool. + // +optional + Autoscaling *bool `json:"autoscaling,omitempty"` + + // minSize defines the minimum size of the pool. Note that this field is only + // used when autoscaling is enabled on the pool. + // +optional + // +kubebuilder:validation:Minimum=0 + MinSize *int32 `json:"minSize,omitempty"` + + // maxSize defines the maximum size of the pool. Note that this field is only + // used when autoscaling is enabled on the pool. + // +optional + // +kubebuilder:validation:Minimum=0 + MaxSize *int32 `json:"maxSize,omitempty"` +} + +// UpgradePolicy defines the pool's upgrade policy. +// +kubebuilder:validation:MinProperties=1 +type UpgradePolicy struct { + // maxUnavailable is the maximum number of available nodes during upgrades. + // +optional + // +kubebuilder:validation:Minimum=0 + MaxUnavailable *int32 `json:"maxUnavailable,omitempty"` + + // maxSurge is the maximum number of additional nodes that can be provisioned + // during upgrades. + // +optional + // +kubebuilder:validation:Minimum=0 + MaxSurge *int32 `json:"maxSurge,omitempty"` +} + +// ScalewayManagedMachinePoolStatus defines the observed state of ScalewayManagedMachinePool. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedMachinePoolStatus struct { + // conditions represent the current state of the ScalewayManagedMachinePool resource. + // Each condition has a unique type and reflects the status of a specific aspect of the resource. + // + // The status of each condition is one of True, False, or Unknown. + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=32 + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // ready is true when the provider resource is ready. + // Deprecated: this field is kept for now as CAPI v1.11.3 still needs it. + // The .initialization.provisioned field should be used instead. + // +optional + Ready *bool `json:"ready,omitempty"` + + // initialization provides observations of the ScalewayManagedMachinePool initialization process. + // NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial MachinePool provisioning. + // +optional + Initialization ScalewayManagedMachinePoolInitializationStatus `json:"initialization,omitempty,omitzero"` + + // replicas is the most recently observed number of replicas. + // +optional + Replicas *int32 `json:"replicas,omitempty"` +} + +// ScalewayManagedMachinePoolInitializationStatus provides observations of the ScalewayManagedMachinePool initialization process. +// +kubebuilder:validation:MinProperties=1 +type ScalewayManagedMachinePoolInitializationStatus struct { + // provisioned is true when the infrastructure provider reports that the MachinePool's infrastructure is fully provisioned. + // +optional + Provisioned *bool `json:"provisioned,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=scalewaymanagedmachinepools,scope=Namespaced,categories=cluster-api,shortName=smmp +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Provisioned",type="boolean",JSONPath=".status.initialization.provisioned",description="Provisioned is true when the machinepool infrastructure is fully provisioned" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=`.status.conditions[?(@.type=="Ready")].status`,description="ScalewayManagedMachinePool pass all readiness checks" +// +kubebuilder:printcolumn:name="Replicas",type="string",JSONPath=".status.replicas" + +// ScalewayManagedMachinePool is the Schema for the scalewaymanagedmachinepools API +// +kubebuilder:validation:XValidation:rule="self.metadata.name.size() <= 63",message="name must be between 1 and 63 characters" +// +kubebuilder:validation:XValidation:rule="self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')",message="name must be a valid DNS label" +type ScalewayManagedMachinePool struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of ScalewayManagedMachinePool + // +required + Spec ScalewayManagedMachinePoolSpec `json:"spec,omitempty,omitzero"` + + // status defines the observed state of ScalewayManagedMachinePool + // +optional + Status ScalewayManagedMachinePoolStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// ScalewayManagedMachinePoolList contains a list of ScalewayManagedMachinePool +type ScalewayManagedMachinePoolList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ScalewayManagedMachinePool `json:"items"` +} + +// GetConditions returns the list of conditions for an ScalewayManagedMachinePool API object. +func (s *ScalewayManagedMachinePool) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions will set the given conditions on an ScalewayManagedMachinePool object. +func (s *ScalewayManagedMachinePool) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +func init() { + SchemeBuilder.Register(&ScalewayManagedMachinePool{}, &ScalewayManagedMachinePoolList{}) +} diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 0000000..231150d --- /dev/null +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,1348 @@ +//go:build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/cluster-api/api/core/v1beta2" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ACL) DeepCopyInto(out *ACL) { + *out = *in + if in.AllowedRanges != nil { + in, out := &in.AllowedRanges, &out.AllowedRanges + *out = make([]CIDR, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACL. +func (in *ACL) DeepCopy() *ACL { + if in == nil { + return nil + } + out := new(ACL) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AutoUpgrade) DeepCopyInto(out *AutoUpgrade) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + in.MaintenanceWindow.DeepCopyInto(&out.MaintenanceWindow) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoUpgrade. +func (in *AutoUpgrade) DeepCopy() *AutoUpgrade { + if in == nil { + return nil + } + out := new(AutoUpgrade) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Autoscaler) DeepCopyInto(out *Autoscaler) { + *out = *in + if in.ScaleDownDisabled != nil { + in, out := &in.ScaleDownDisabled, &out.ScaleDownDisabled + *out = new(bool) + **out = **in + } + if in.IgnoreDaemonsetsUtilization != nil { + in, out := &in.IgnoreDaemonsetsUtilization, &out.IgnoreDaemonsetsUtilization + *out = new(bool) + **out = **in + } + if in.BalanceSimilarNodeGroups != nil { + in, out := &in.BalanceSimilarNodeGroups, &out.BalanceSimilarNodeGroups + *out = new(bool) + **out = **in + } + if in.ExpendablePodsPriorityCutoff != nil { + in, out := &in.ExpendablePodsPriorityCutoff, &out.ExpendablePodsPriorityCutoff + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Autoscaler. +func (in *Autoscaler) DeepCopy() *Autoscaler { + if in == nil { + return nil + } + out := new(Autoscaler) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneDNS) DeepCopyInto(out *ControlPlaneDNS) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneDNS. +func (in *ControlPlaneDNS) DeepCopy() *ControlPlaneDNS { + if in == nil { + return nil + } + out := new(ControlPlaneDNS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ControlPlaneLoadBalancer) DeepCopyInto(out *ControlPlaneLoadBalancer) { + *out = *in + out.LoadBalancer = in.LoadBalancer + if in.AllowedRanges != nil { + in, out := &in.AllowedRanges, &out.AllowedRanges + *out = make([]CIDR, len(*in)) + copy(*out, *in) + } + if in.Private != nil { + in, out := &in.Private, &out.Private + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneLoadBalancer. +func (in *ControlPlaneLoadBalancer) DeepCopy() *ControlPlaneLoadBalancer { + if in == nil { + return nil + } + out := new(ControlPlaneLoadBalancer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IDOrName) DeepCopyInto(out *IDOrName) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IDOrName. +func (in *IDOrName) DeepCopy() *IDOrName { + if in == nil { + return nil + } + out := new(IDOrName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Image) DeepCopyInto(out *Image) { + *out = *in + out.IDOrName = in.IDOrName +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Image. +func (in *Image) DeepCopy() *Image { + if in == nil { + return nil + } + out := new(Image) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancer) DeepCopyInto(out *LoadBalancer) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancer. +func (in *LoadBalancer) DeepCopy() *LoadBalancer { + if in == nil { + return nil + } + out := new(LoadBalancer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaintenanceWindow) DeepCopyInto(out *MaintenanceWindow) { + *out = *in + if in.StartHour != nil { + in, out := &in.StartHour, &out.StartHour + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaintenanceWindow. +func (in *MaintenanceWindow) DeepCopy() *MaintenanceWindow { + if in == nil { + return nil + } + out := new(MaintenanceWindow) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OnDelete) DeepCopyInto(out *OnDelete) { + *out = *in + if in.WithAdditionalResources != nil { + in, out := &in.WithAdditionalResources, &out.WithAdditionalResources + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OnDelete. +func (in *OnDelete) DeepCopy() *OnDelete { + if in == nil { + return nil + } + out := new(OnDelete) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenIDConnect) DeepCopyInto(out *OpenIDConnect) { + *out = *in + if in.GroupsClaim != nil { + in, out := &in.GroupsClaim, &out.GroupsClaim + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.RequiredClaim != nil { + in, out := &in.RequiredClaim, &out.RequiredClaim + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenIDConnect. +func (in *OpenIDConnect) DeepCopy() *OpenIDConnect { + if in == nil { + return nil + } + out := new(OpenIDConnect) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateNetwork) DeepCopyInto(out *PrivateNetwork) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateNetwork. +func (in *PrivateNetwork) DeepCopy() *PrivateNetwork { + if in == nil { + return nil + } + out := new(PrivateNetwork) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrivateNetworkSpec) DeepCopyInto(out *PrivateNetworkSpec) { + *out = *in + out.PrivateNetwork = in.PrivateNetwork + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrivateNetworkSpec. +func (in *PrivateNetworkSpec) DeepCopy() *PrivateNetworkSpec { + if in == nil { + return nil + } + out := new(PrivateNetworkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PublicGateway) DeepCopyInto(out *PublicGateway) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PublicGateway. +func (in *PublicGateway) DeepCopy() *PublicGateway { + if in == nil { + return nil + } + out := new(PublicGateway) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PublicNetwork) DeepCopyInto(out *PublicNetwork) { + *out = *in + if in.EnableIPv4 != nil { + in, out := &in.EnableIPv4, &out.EnableIPv4 + *out = new(bool) + **out = **in + } + if in.EnableIPv6 != nil { + in, out := &in.EnableIPv6, &out.EnableIPv6 + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PublicNetwork. +func (in *PublicNetwork) DeepCopy() *PublicNetwork { + if in == nil { + return nil + } + out := new(PublicNetwork) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RootVolume) DeepCopyInto(out *RootVolume) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootVolume. +func (in *RootVolume) DeepCopy() *RootVolume { + if in == nil { + return nil + } + out := new(RootVolume) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayCluster) DeepCopyInto(out *ScalewayCluster) { + *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 ScalewayCluster. +func (in *ScalewayCluster) DeepCopy() *ScalewayCluster { + if in == nil { + return nil + } + out := new(ScalewayCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayCluster) 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 *ScalewayClusterInitializationStatus) DeepCopyInto(out *ScalewayClusterInitializationStatus) { + *out = *in + if in.Provisioned != nil { + in, out := &in.Provisioned, &out.Provisioned + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterInitializationStatus. +func (in *ScalewayClusterInitializationStatus) DeepCopy() *ScalewayClusterInitializationStatus { + if in == nil { + return nil + } + out := new(ScalewayClusterInitializationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayClusterList) DeepCopyInto(out *ScalewayClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterList. +func (in *ScalewayClusterList) DeepCopy() *ScalewayClusterList { + if in == nil { + return nil + } + out := new(ScalewayClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayClusterList) 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 *ScalewayClusterNetwork) DeepCopyInto(out *ScalewayClusterNetwork) { + *out = *in + in.ControlPlaneLoadBalancer.DeepCopyInto(&out.ControlPlaneLoadBalancer) + if in.ControlPlaneExtraLoadBalancers != nil { + in, out := &in.ControlPlaneExtraLoadBalancers, &out.ControlPlaneExtraLoadBalancers + *out = make([]LoadBalancer, len(*in)) + copy(*out, *in) + } + out.ControlPlaneDNS = in.ControlPlaneDNS + in.PrivateNetwork.DeepCopyInto(&out.PrivateNetwork) + if in.PublicGateways != nil { + in, out := &in.PublicGateways, &out.PublicGateways + *out = make([]PublicGateway, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterNetwork. +func (in *ScalewayClusterNetwork) DeepCopy() *ScalewayClusterNetwork { + if in == nil { + return nil + } + out := new(ScalewayClusterNetwork) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayClusterNetworkStatus) DeepCopyInto(out *ScalewayClusterNetworkStatus) { + *out = *in + if in.PublicGatewayIDs != nil { + in, out := &in.PublicGatewayIDs, &out.PublicGatewayIDs + *out = make([]UUID, len(*in)) + copy(*out, *in) + } + if in.ExtraLoadBalancerIPs != nil { + in, out := &in.ExtraLoadBalancerIPs, &out.ExtraLoadBalancerIPs + *out = make([]IPv4, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterNetworkStatus. +func (in *ScalewayClusterNetworkStatus) DeepCopy() *ScalewayClusterNetworkStatus { + if in == nil { + return nil + } + out := new(ScalewayClusterNetworkStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayClusterSpec) DeepCopyInto(out *ScalewayClusterSpec) { + *out = *in + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make([]ScalewayZone, len(*in)) + copy(*out, *in) + } + in.Network.DeepCopyInto(&out.Network) + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterSpec. +func (in *ScalewayClusterSpec) DeepCopy() *ScalewayClusterSpec { + if in == nil { + return nil + } + out := new(ScalewayClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayClusterStatus) DeepCopyInto(out *ScalewayClusterStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make([]v1beta2.FailureDomain, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Initialization.DeepCopyInto(&out.Initialization) + in.Network.DeepCopyInto(&out.Network) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterStatus. +func (in *ScalewayClusterStatus) DeepCopy() *ScalewayClusterStatus { + if in == nil { + return nil + } + out := new(ScalewayClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayClusterTemplate) DeepCopyInto(out *ScalewayClusterTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterTemplate. +func (in *ScalewayClusterTemplate) DeepCopy() *ScalewayClusterTemplate { + if in == nil { + return nil + } + out := new(ScalewayClusterTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayClusterTemplate) 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 *ScalewayClusterTemplateList) DeepCopyInto(out *ScalewayClusterTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayClusterTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterTemplateList. +func (in *ScalewayClusterTemplateList) DeepCopy() *ScalewayClusterTemplateList { + if in == nil { + return nil + } + out := new(ScalewayClusterTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayClusterTemplateList) 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 *ScalewayClusterTemplateResource) DeepCopyInto(out *ScalewayClusterTemplateResource) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterTemplateResource. +func (in *ScalewayClusterTemplateResource) DeepCopy() *ScalewayClusterTemplateResource { + if in == nil { + return nil + } + out := new(ScalewayClusterTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayClusterTemplateSpec) DeepCopyInto(out *ScalewayClusterTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayClusterTemplateSpec. +func (in *ScalewayClusterTemplateSpec) DeepCopy() *ScalewayClusterTemplateSpec { + if in == nil { + return nil + } + out := new(ScalewayClusterTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayMachine) DeepCopyInto(out *ScalewayMachine) { + *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 ScalewayMachine. +func (in *ScalewayMachine) DeepCopy() *ScalewayMachine { + if in == nil { + return nil + } + out := new(ScalewayMachine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayMachine) 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 *ScalewayMachineInitializationStatus) DeepCopyInto(out *ScalewayMachineInitializationStatus) { + *out = *in + if in.Provisioned != nil { + in, out := &in.Provisioned, &out.Provisioned + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineInitializationStatus. +func (in *ScalewayMachineInitializationStatus) DeepCopy() *ScalewayMachineInitializationStatus { + if in == nil { + return nil + } + out := new(ScalewayMachineInitializationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayMachineList) DeepCopyInto(out *ScalewayMachineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayMachine, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineList. +func (in *ScalewayMachineList) DeepCopy() *ScalewayMachineList { + if in == nil { + return nil + } + out := new(ScalewayMachineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayMachineList) 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 *ScalewayMachineSpec) DeepCopyInto(out *ScalewayMachineSpec) { + *out = *in + out.Image = in.Image + out.RootVolume = in.RootVolume + in.PublicNetwork.DeepCopyInto(&out.PublicNetwork) + out.PlacementGroup = in.PlacementGroup + out.SecurityGroup = in.SecurityGroup +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineSpec. +func (in *ScalewayMachineSpec) DeepCopy() *ScalewayMachineSpec { + if in == nil { + return nil + } + out := new(ScalewayMachineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayMachineStatus) DeepCopyInto(out *ScalewayMachineStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Initialization.DeepCopyInto(&out.Initialization) + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]v1beta2.MachineAddress, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineStatus. +func (in *ScalewayMachineStatus) DeepCopy() *ScalewayMachineStatus { + if in == nil { + return nil + } + out := new(ScalewayMachineStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayMachineTemplate) DeepCopyInto(out *ScalewayMachineTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineTemplate. +func (in *ScalewayMachineTemplate) DeepCopy() *ScalewayMachineTemplate { + if in == nil { + return nil + } + out := new(ScalewayMachineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayMachineTemplate) 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 *ScalewayMachineTemplateList) DeepCopyInto(out *ScalewayMachineTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayMachineTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineTemplateList. +func (in *ScalewayMachineTemplateList) DeepCopy() *ScalewayMachineTemplateList { + if in == nil { + return nil + } + out := new(ScalewayMachineTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayMachineTemplateList) 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 *ScalewayMachineTemplateResource) DeepCopyInto(out *ScalewayMachineTemplateResource) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineTemplateResource. +func (in *ScalewayMachineTemplateResource) DeepCopy() *ScalewayMachineTemplateResource { + if in == nil { + return nil + } + out := new(ScalewayMachineTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayMachineTemplateSpec) DeepCopyInto(out *ScalewayMachineTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayMachineTemplateSpec. +func (in *ScalewayMachineTemplateSpec) DeepCopy() *ScalewayMachineTemplateSpec { + if in == nil { + return nil + } + out := new(ScalewayMachineTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedCluster) DeepCopyInto(out *ScalewayManagedCluster) { + *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 ScalewayManagedCluster. +func (in *ScalewayManagedCluster) DeepCopy() *ScalewayManagedCluster { + if in == nil { + return nil + } + out := new(ScalewayManagedCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayManagedCluster) 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 *ScalewayManagedClusterInitializationStatus) DeepCopyInto(out *ScalewayManagedClusterInitializationStatus) { + *out = *in + if in.Provisioned != nil { + in, out := &in.Provisioned, &out.Provisioned + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedClusterInitializationStatus. +func (in *ScalewayManagedClusterInitializationStatus) DeepCopy() *ScalewayManagedClusterInitializationStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedClusterInitializationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedClusterList) DeepCopyInto(out *ScalewayManagedClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayManagedCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedClusterList. +func (in *ScalewayManagedClusterList) DeepCopy() *ScalewayManagedClusterList { + if in == nil { + return nil + } + out := new(ScalewayManagedClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayManagedClusterList) 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 *ScalewayManagedClusterNetwork) DeepCopyInto(out *ScalewayManagedClusterNetwork) { + *out = *in + out.PrivateNetwork = in.PrivateNetwork + if in.PublicGateways != nil { + in, out := &in.PublicGateways, &out.PublicGateways + *out = make([]PublicGateway, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedClusterNetwork. +func (in *ScalewayManagedClusterNetwork) DeepCopy() *ScalewayManagedClusterNetwork { + if in == nil { + return nil + } + out := new(ScalewayManagedClusterNetwork) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedClusterNetworkStatus) DeepCopyInto(out *ScalewayManagedClusterNetworkStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedClusterNetworkStatus. +func (in *ScalewayManagedClusterNetworkStatus) DeepCopy() *ScalewayManagedClusterNetworkStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedClusterNetworkStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedClusterSpec) DeepCopyInto(out *ScalewayManagedClusterSpec) { + *out = *in + in.Network.DeepCopyInto(&out.Network) + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedClusterSpec. +func (in *ScalewayManagedClusterSpec) DeepCopy() *ScalewayManagedClusterSpec { + if in == nil { + return nil + } + out := new(ScalewayManagedClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedClusterStatus) DeepCopyInto(out *ScalewayManagedClusterStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Initialization.DeepCopyInto(&out.Initialization) + out.Network = in.Network +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedClusterStatus. +func (in *ScalewayManagedClusterStatus) DeepCopy() *ScalewayManagedClusterStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedControlPlane) DeepCopyInto(out *ScalewayManagedControlPlane) { + *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 ScalewayManagedControlPlane. +func (in *ScalewayManagedControlPlane) DeepCopy() *ScalewayManagedControlPlane { + if in == nil { + return nil + } + out := new(ScalewayManagedControlPlane) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayManagedControlPlane) 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 *ScalewayManagedControlPlaneInitializationStatus) DeepCopyInto(out *ScalewayManagedControlPlaneInitializationStatus) { + *out = *in + if in.ControlPlaneInitialized != nil { + in, out := &in.ControlPlaneInitialized, &out.ControlPlaneInitialized + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedControlPlaneInitializationStatus. +func (in *ScalewayManagedControlPlaneInitializationStatus) DeepCopy() *ScalewayManagedControlPlaneInitializationStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedControlPlaneInitializationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedControlPlaneList) DeepCopyInto(out *ScalewayManagedControlPlaneList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayManagedControlPlane, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedControlPlaneList. +func (in *ScalewayManagedControlPlaneList) DeepCopy() *ScalewayManagedControlPlaneList { + if in == nil { + return nil + } + out := new(ScalewayManagedControlPlaneList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayManagedControlPlaneList) 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 *ScalewayManagedControlPlaneSpec) DeepCopyInto(out *ScalewayManagedControlPlaneSpec) { + *out = *in + if in.AdditionalTags != nil { + in, out := &in.AdditionalTags, &out.AdditionalTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Autoscaler.DeepCopyInto(&out.Autoscaler) + in.AutoUpgrade.DeepCopyInto(&out.AutoUpgrade) + if in.FeatureGates != nil { + in, out := &in.FeatureGates, &out.FeatureGates + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AdmissionPlugins != nil { + in, out := &in.AdmissionPlugins, &out.AdmissionPlugins + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.OpenIDConnect.DeepCopyInto(&out.OpenIDConnect) + if in.APIServerCertSANs != nil { + in, out := &in.APIServerCertSANs, &out.APIServerCertSANs + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.OnDelete.DeepCopyInto(&out.OnDelete) + if in.ACL != nil { + in, out := &in.ACL, &out.ACL + *out = new(ACL) + (*in).DeepCopyInto(*out) + } + if in.EnablePrivateEndpoint != nil { + in, out := &in.EnablePrivateEndpoint, &out.EnablePrivateEndpoint + *out = new(bool) + **out = **in + } + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedControlPlaneSpec. +func (in *ScalewayManagedControlPlaneSpec) DeepCopy() *ScalewayManagedControlPlaneSpec { + if in == nil { + return nil + } + out := new(ScalewayManagedControlPlaneSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedControlPlaneStatus) DeepCopyInto(out *ScalewayManagedControlPlaneStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ExternalManagedControlPlane != nil { + in, out := &in.ExternalManagedControlPlane, &out.ExternalManagedControlPlane + *out = new(bool) + **out = **in + } + in.Initialization.DeepCopyInto(&out.Initialization) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedControlPlaneStatus. +func (in *ScalewayManagedControlPlaneStatus) DeepCopy() *ScalewayManagedControlPlaneStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedControlPlaneStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedMachinePool) DeepCopyInto(out *ScalewayManagedMachinePool) { + *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 ScalewayManagedMachinePool. +func (in *ScalewayManagedMachinePool) DeepCopy() *ScalewayManagedMachinePool { + if in == nil { + return nil + } + out := new(ScalewayManagedMachinePool) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayManagedMachinePool) 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 *ScalewayManagedMachinePoolInitializationStatus) DeepCopyInto(out *ScalewayManagedMachinePoolInitializationStatus) { + *out = *in + if in.Provisioned != nil { + in, out := &in.Provisioned, &out.Provisioned + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedMachinePoolInitializationStatus. +func (in *ScalewayManagedMachinePoolInitializationStatus) DeepCopy() *ScalewayManagedMachinePoolInitializationStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedMachinePoolInitializationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedMachinePoolList) DeepCopyInto(out *ScalewayManagedMachinePoolList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ScalewayManagedMachinePool, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedMachinePoolList. +func (in *ScalewayManagedMachinePoolList) DeepCopy() *ScalewayManagedMachinePoolList { + if in == nil { + return nil + } + out := new(ScalewayManagedMachinePoolList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ScalewayManagedMachinePoolList) 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 *ScalewayManagedMachinePoolSpec) DeepCopyInto(out *ScalewayManagedMachinePoolSpec) { + *out = *in + in.Scaling.DeepCopyInto(&out.Scaling) + if in.Autohealing != nil { + in, out := &in.Autohealing, &out.Autohealing + *out = new(bool) + **out = **in + } + if in.AdditionalTags != nil { + in, out := &in.AdditionalTags, &out.AdditionalTags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.KubeletArgs != nil { + in, out := &in.KubeletArgs, &out.KubeletArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.UpgradePolicy.DeepCopyInto(&out.UpgradePolicy) + if in.PublicIPDisabled != nil { + in, out := &in.PublicIPDisabled, &out.PublicIPDisabled + *out = new(bool) + **out = **in + } + if in.ProviderIDList != nil { + in, out := &in.ProviderIDList, &out.ProviderIDList + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedMachinePoolSpec. +func (in *ScalewayManagedMachinePoolSpec) DeepCopy() *ScalewayManagedMachinePoolSpec { + if in == nil { + return nil + } + out := new(ScalewayManagedMachinePoolSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalewayManagedMachinePoolStatus) DeepCopyInto(out *ScalewayManagedMachinePoolStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Ready != nil { + in, out := &in.Ready, &out.Ready + *out = new(bool) + **out = **in + } + in.Initialization.DeepCopyInto(&out.Initialization) + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalewayManagedMachinePoolStatus. +func (in *ScalewayManagedMachinePoolStatus) DeepCopy() *ScalewayManagedMachinePoolStatus { + if in == nil { + return nil + } + out := new(ScalewayManagedMachinePoolStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Scaling) DeepCopyInto(out *Scaling) { + *out = *in + if in.Autoscaling != nil { + in, out := &in.Autoscaling, &out.Autoscaling + *out = new(bool) + **out = **in + } + if in.MinSize != nil { + in, out := &in.MinSize, &out.MinSize + *out = new(int32) + **out = **in + } + if in.MaxSize != nil { + in, out := &in.MaxSize, &out.MaxSize + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scaling. +func (in *Scaling) DeepCopy() *Scaling { + if in == nil { + return nil + } + out := new(Scaling) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpgradePolicy) DeepCopyInto(out *UpgradePolicy) { + *out = *in + if in.MaxUnavailable != nil { + in, out := &in.MaxUnavailable, &out.MaxUnavailable + *out = new(int32) + **out = **in + } + if in.MaxSurge != nil { + in, out := &in.MaxSurge, &out.MaxSurge + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradePolicy. +func (in *UpgradePolicy) DeepCopy() *UpgradePolicy { + if in == nil { + return nil + } + out := new(UpgradePolicy) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/main.go b/cmd/main.go index 19000d9..33043aa 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -11,23 +11,29 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" + pflag "github.com/spf13/pflag" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/controllers/crdmigrator" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/certwatcher" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrlcontroller "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + // +kubebuilder:scaffold:imports + infrav1alpha1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" //nolint:staticcheck + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/controller" internalVersion "github.com/scaleway/cluster-api-provider-scaleway/internal/version" - // +kubebuilder:scaffold:imports + webhookv1 "github.com/scaleway/cluster-api-provider-scaleway/internal/webhook/v1alpha2" ) var ( @@ -38,12 +44,20 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(clusterv1.AddToScheme(scheme)) - utilruntime.Must(expclusterv1.AddToScheme(scheme)) + utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) utilruntime.Must(infrav1.AddToScheme(scheme)) + utilruntime.Must(infrav1alpha1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } +// ADD CRD RBAC for CRD Migrator. +// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch +// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions;customresourcedefinitions/status,verbs=update;patch,resourceNames=scalewayclusters.infrastructure.cluster.x-k8s.io;scalewayclustertemplates.infrastructure.cluster.x-k8s.io;scalewaymachines.infrastructure.cluster.x-k8s.io;scalewaymachinetemplates.infrastructure.cluster.x-k8s.io;scalewaymanagedclusters.infrastructure.cluster.x-k8s.io;scalewaymanagedcontrolplanes.infrastructure.cluster.x-k8s.io;scalewaymanagedmachinepools.infrastructure.cluster.x-k8s.io +// ADD CR RBAC for CRD Migrator. +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=scalewayclustertemplates,verbs=get;list;watch;patch;update +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=scalewaymachinetemplates,verbs=get;list;watch;patch;update + // nolint:gocyclo func main() { var metricsAddr string @@ -53,32 +67,36 @@ func main() { var probeAddr string var secureMetrics bool var enableHTTP2 bool + var skipCRDMigrationPhases []string var version bool var tlsOpts []func(*tls.Config) - flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + pflag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, + pflag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + pflag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - flag.BoolVar(&secureMetrics, "metrics-secure", true, + pflag.BoolVar(&secureMetrics, "metrics-secure", true, "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") - flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") - flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") - flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") - flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + pflag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + pflag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + pflag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + pflag.StringVar(&metricsCertPath, "metrics-cert-path", "", "The directory that contains the metrics server certificate.") - flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") - flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") - flag.BoolVar(&enableHTTP2, "enable-http2", false, + pflag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + pflag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") + pflag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") - flag.BoolVar(&version, "version", false, + pflag.StringArrayVar(&skipCRDMigrationPhases, "skip-crd-migration-phases", []string{}, + "List of CRD migration phases to skip. Valid values are: StorageVersionMigration, CleanupManagedFields.") + pflag.BoolVar(&version, "version", false, "If set, display version and exit") opts := zap.Options{ Development: true, } opts.BindFlags(flag.CommandLine) - flag.Parse() + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() if version { fmt.Println("cluster-api-provider-scaleway", internalVersion.Version) @@ -226,8 +244,83 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "ScalewayManagedMachinePool") os.Exit(1) } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayClusterWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayCluster") + os.Exit(1) + } + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayMachineWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayMachine") + os.Exit(1) + } + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayClusterTemplateWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayClusterTemplate") + os.Exit(1) + } + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayMachineTemplateWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayMachineTemplate") + os.Exit(1) + } + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayManagedClusterWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayManagedCluster") + os.Exit(1) + } + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayManagedControlPlaneWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayManagedControlPlane") + os.Exit(1) + } + } + // nolint:goconst + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err := webhookv1.SetupScalewayManagedMachinePoolWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ScalewayManagedMachinePool") + os.Exit(1) + } + } // +kubebuilder:scaffold:builder + crdMigratorSkipPhases := []crdmigrator.Phase{} + for _, p := range skipCRDMigrationPhases { + crdMigratorSkipPhases = append(crdMigratorSkipPhases, crdmigrator.Phase(p)) + } + if err := (&crdmigrator.CRDMigrator{ + Client: mgr.GetClient(), + APIReader: mgr.GetAPIReader(), + SkipCRDMigrationPhases: crdMigratorSkipPhases, + // Note: The kubebuilder RBAC markers above has to be kept in sync + // with the CRDs that should be migrated by this provider. + Config: map[client.Object]crdmigrator.ByObjectConfig{ + &infrav1.ScalewayCluster{}: {UseCache: true}, + &infrav1.ScalewayClusterTemplate{}: {UseCache: false}, + &infrav1.ScalewayMachine{}: {UseCache: true}, + &infrav1.ScalewayClusterTemplate{}: {UseCache: false}, + &infrav1.ScalewayManagedCluster{}: {UseCache: true}, + &infrav1.ScalewayManagedControlPlane{}: {UseCache: true}, + &infrav1.ScalewayManagedMachinePool{}: {UseCache: true}, + }, + // The CRDMigrator is run with only concurrency 1 to ensure we don't overwhelm + // the apiserver by patching a lot of CRs concurrently. + }).SetupWithManager(ctx, mgr, ctrlcontroller.Options{MaxConcurrentReconciles: 1}); err != nil { + setupLog.Error(err, "Unable to create controller", "controller", "CRDMigrator") + os.Exit(1) + } + if metricsCertWatcher != nil { setupLog.Info("Adding metrics certificate watcher to manager") if err := mgr.Add(metricsCertWatcher); err != nil { diff --git a/config/certmanager/certificate-metrics.yaml b/config/certmanager/certificate-metrics.yaml new file mode 100644 index 0000000..b871214 --- /dev/null +++ b/config/certmanager/certificate-metrics.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a metrics certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + dnsNames: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert diff --git a/config/certmanager/certificate-webhook.yaml b/config/certmanager/certificate-webhook.yaml new file mode 100644 index 0000000..c3c3765 --- /dev/null +++ b/config/certmanager/certificate-webhook.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + dnsNames: + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert diff --git a/config/certmanager/issuer.yaml b/config/certmanager/issuer.yaml new file mode 100644 index 0000000..d9aaa45 --- /dev/null +++ b/config/certmanager/issuer.yaml @@ -0,0 +1,13 @@ +# The following manifest contains a self-signed issuer CR. +# More information can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml new file mode 100644 index 0000000..fcb7498 --- /dev/null +++ b/config/certmanager/kustomization.yaml @@ -0,0 +1,7 @@ +resources: +- issuer.yaml +- certificate-webhook.yaml +- certificate-metrics.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 0000000..cf6f89e --- /dev/null +++ b/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,8 @@ +# This configuration is for teaching kustomize how to update name ref substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclusters.yaml index c7a6ce1..09d0bb7 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclusters.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewayclusters.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -35,6 +35,7 @@ spec: jsonPath: .status.ready name: Ready type: boolean + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -430,6 +431,583 @@ spec: - message: name must be a valid DNS label rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Host of the control plane + jsonPath: .spec.controlPlaneEndpoint.host + name: Host + type: string + - description: Port of the control plane + jsonPath: .spec.controlPlaneEndpoint.port + name: Port + type: integer + - description: Region of the cluster + jsonPath: .spec.region + name: Region + type: string + - description: Provisioned is true when the cluster infrastructure is fully provisioned + jsonPath: .status.initialization.provisioned + name: Provisioned + type: boolean + - description: ScalewayCluster pass all readiness checks + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayCluster is the Schema for the scalewayclusters 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: spec defines the desired state of ScalewayCluster + properties: + controlPlaneEndpoint: + description: controlPlaneEndpoint represents the endpoint used to + communicate with the control plane. + minProperties: 1 + properties: + host: + description: host is the hostname on which the API server is serving. + maxLength: 512 + minLength: 1 + type: string + port: + description: port is the port on which the API server is serving. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + failureDomains: + description: |- + failureDomains is a list of failure domains where the control-plane nodes will be created. + Failure domains correspond to Scaleway zones inside the cluster region (e.g. fr-par-1). + items: + description: ScalewayZone is a Scaleway zone (e.g. fr-par-1). + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + maxItems: 3 + minItems: 1 + type: array + x-kubernetes-list-type: set + network: + description: network contains network related options for the cluster. + minProperties: 1 + properties: + controlPlaneDNS: + description: controlPlaneDNS allows configuring a Scaleway Domain + DNS Zone. + properties: + domain: + description: |- + domain is the DNS Zone that this record should live in. It must be pre-existing in your Scaleway account. + The format must be a string that conforms to the definition of a subdomain in DNS (RFC 1123). + This is optional if the control plane load balancer is private. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: |- + name is the DNS short name of the record (non-FQDN). The format must consist of + alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$ + type: string + required: + - name + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + controlPlaneExtraLoadBalancers: + description: |- + controlPlaneExtraLoadBalancers allows configuring additional load balancers. + Because Scaleway load balancers are currently zonal resources, you may set + up to 3 additional load balancers for achieving regional redundancy. It is + mandatory to set the controlPlaneDNS field when you do so. + NOTE: This may be removed in the future, when Scaleway supports regional LoadBalancers. + items: + description: LoadBalancer defines load balancer parameters. + minProperties: 1 + properties: + ip: + description: ip is an existing public IPv4 to use when creating + a load balancer. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + privateIP: + description: |- + privateIP is an existing private IPv4 inside the Private Network to use + when attaching a load balancer to a Private Network. It must be pre-booked + inside the Scaleway IPAM. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: LB-S + description: type is the load balancer commercial offer + type. + maxLength: 10 + minLength: 1 + type: string + zone: + description: |- + zone where to create the load balancer. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + maxItems: 3 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + controlPlaneLoadBalancer: + allOf: + - minProperties: 1 + - minProperties: 1 + description: controlPlaneLoadBalancer defines settings for the + load balancer of the control plane. + properties: + allowedRanges: + description: |- + allowedRanges allows to set a list of allowed IP ranges that can access + the cluster through the load balancer. When unset, all IP ranges are allowed. + To allow the cluster to work properly, public IPs of nodes and Public + Gateways will automatically be allowed. However, if this field is set, + you MUST manually allow IPs of the nodes of your management cluster. + items: + description: CIDR is an IP address range in CIDR notation + (for example, "10.0.0.0/8" or "fd00::/8"). + maxLength: 43 + minLength: 1 + type: string + x-kubernetes-validations: + - message: value must be a valid CIDR network address + rule: isCIDR(self) + maxItems: 30 + minItems: 1 + type: array + x-kubernetes-list-type: set + ip: + description: ip is an existing public IPv4 to use when creating + a load balancer. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + private: + description: private disables the creation of a public IP + on the load balancers when it's set to true. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + privateIP: + description: |- + privateIP is an existing private IPv4 inside the Private Network to use + when attaching a load balancer to a Private Network. It must be pre-booked + inside the Scaleway IPAM. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: LB-S + description: type is the load balancer commercial offer type. + maxLength: 10 + minLength: 1 + type: string + zone: + description: |- + zone where to create the load balancer. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + x-kubernetes-validations: + - message: ip is immutable + rule: '!has(oldSelf.ip) || self.ip == oldSelf.ip' + - message: zone is immutable + rule: '!has(oldSelf.zone) || self.zone == oldSelf.zone' + - message: privateIP is immutable + rule: '!has(oldSelf.privateIP) || self.privateIP == oldSelf.privateIP' + privateNetwork: + description: privateNetwork allows attaching machines of the cluster + to a Private Network. + minProperties: 1 + properties: + enabled: + description: |- + enabled allows to automatically attach machines to a Private Network when it's set to true. + The Private Network is automatically created if no existing Private + Network ID is provided. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + id: + description: id allows to reuse an existing Private Network + instead of creating a new one. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + subnet: + description: subnet defines a subnet for the Private Network. + Only used on newly created Private Networks. + maxLength: 43 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + - message: value must be a valid CIDR network address + rule: isCIDR(self) + vpcID: + description: vpcID defines the ID of the VPC where the new + Private Network will be created if none is provided. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + required: + - enabled + type: object + x-kubernetes-validations: + - message: vpcID cannot be added or removed + rule: has(self.vpcID) == has(oldSelf.vpcID) + - message: id cannot be added or removed + rule: has(self.id) == has(oldSelf.id) + - message: subnet cannot be added or removed + rule: has(self.subnet) == has(oldSelf.subnet) + - message: id and vpcID cannot be set at the same time + rule: '!has(self.id) || has(self.id) != has(self.vpcID)' + - message: id and subnet cannot be set at the same time + rule: '!has(self.id) || has(self.id) != has(self.subnet)' + publicGateways: + description: |- + publicGateways allows to manage Public Gateways that will be created and + attached to the Private Network of the cluster. + items: + description: PublicGateway defines settings of the Public Gateway + that will be created. + minProperties: 1 + properties: + ip: + description: ip to use when creating a Public Gateway. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: VPC-GW-S + description: type is a Public Gateway commercial offer type. + maxLength: 20 + minLength: 1 + type: string + zone: + description: |- + zone where to create the Public Gateway. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + maxItems: 6 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-validations: + - message: controlPlaneDNS is required when controlPlaneExtraLoadBalancers + is set + rule: '!has(self.controlPlaneExtraLoadBalancers) || has(self.controlPlaneDNS)' + - message: privateNetwork is required when publicGateways is set + rule: '!has(self.publicGateways) || has(self.privateNetwork) && + self.privateNetwork.enabled' + - message: privateNetwork is required when private LoadBalancer is + enabled + rule: '!has(self.controlPlaneLoadBalancer) || !has(self.controlPlaneLoadBalancer.private) + || !self.controlPlaneLoadBalancer.private || has(self.privateNetwork) + && self.privateNetwork.enabled' + - message: .controlPlaneDNS.domain must be set unless control plane + load balancer is private + rule: '!has(self.controlPlaneDNS) || has(self.controlPlaneDNS) && + has(self.controlPlaneDNS.domain) || has(self.controlPlaneDNS) + && !has(self.controlPlaneDNS.domain) && has(self.controlPlaneLoadBalancer) + && has(self.controlPlaneLoadBalancer.private) && self.controlPlaneLoadBalancer.private' + projectID: + description: projectID is the ID of a Scaleway project where the cluster + will be created. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + region: + description: region represents the region where the cluster will be + hosted. + maxLength: 6 + minLength: 6 + pattern: ^[a-z]{2}-[a-z]{3}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + scalewaySecretName: + description: |- + scalewaySecretName is the name of the secret that contains the Scaleway client parameters. + The following keys are required: SCW_ACCESS_KEY, SCW_SECRET_KEY. + The following key is optional: SCW_API_URL. + maxLength: 253 + minLength: 1 + type: string + required: + - projectID + - region + - scalewaySecretName + type: object + x-kubernetes-validations: + - message: controlPlaneEndpoint is required once set + rule: '!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)' + - message: controlPlaneDNS cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneDNS)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneDNS)) + - message: privateNetwork cannot be added or removed + rule: (has(self.network) && has(self.network.privateNetwork)) == (has(oldSelf.network) + && has(oldSelf.network.privateNetwork)) + - message: private cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.private)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.private)) + - message: ip cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.ip)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.ip)) + - message: zone cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.zone)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.zone)) + - message: privateIP cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.privateIP)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.privateIP)) + status: + description: status defines the observed state of ScalewayCluster + minProperties: 1 + properties: + conditions: + description: |- + conditions represent the current state of the ScalewayCluster resource. + Each condition has a unique type and reflects the status of a specific aspect of the resource. + + The status of each condition is one of True, False, or Unknown. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + failureDomains: + description: failureDomains is a list of failure domain objects synced + from the infrastructure provider. + items: + description: |- + FailureDomain is the Schema for Cluster API failure domains. + It allows controllers to understand how many failure domains a cluster can optionally span across. + properties: + attributes: + additionalProperties: + type: string + description: attributes is a free form map of attributes an + infrastructure provider might use or require. + type: object + controlPlane: + description: controlPlane determines if this failure domain + is suitable for use by control plane machines. + type: boolean + name: + description: name is the name of the failure domain. + maxLength: 256 + minLength: 1 + type: string + required: + - name + type: object + maxItems: 100 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initialization: + description: |- + initialization provides observations of the ScalewayCluster initialization process. + NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning. + minProperties: 1 + properties: + provisioned: + description: |- + provisioned is true when the infrastructure provider reports that the Cluster's infrastructure is fully provisioned. + NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Cluster provisioning. + type: boolean + type: object + network: + description: network contains information about network resources + of the cluster. + minProperties: 1 + properties: + extraLoadBalancerIPs: + description: extraLoadBalancerIPs is a list of IPs of the extra + loadbalancers. + items: + description: IPv4 is a valid IPv4. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + loadBalancerIP: + description: loadBalancerIP is the public IP of the cluster control-plane. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + privateNetworkID: + description: privateNetworkID is set if the cluster has an associated + Private Network. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + publicGatewayIDs: + description: publicGatewayIDs is a list of Public Gateway IDs. + items: + description: UUID is a valid UUID for a Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + vpcID: + description: vpcID is set if the cluster has an associated Private + Network. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + type: object + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: name must be between 1 and 63 characters + rule: self.metadata.name.size() <= 63 + - message: name must be a valid DNS label + rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclustertemplates.yaml index 2bc5fbb..108fb91 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewayclustertemplates.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewayclustertemplates.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -18,7 +18,8 @@ spec: singular: scalewayclustertemplate scope: Namespaced versions: - - name: v1alpha1 + - deprecated: true + name: v1alpha1 schema: openAPIV3Schema: description: ScalewayClusterTemplate is the Schema for the scalewayclustertemplates @@ -378,4 +379,436 @@ spec: type: object type: object served: true + storage: false + - name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayClusterTemplate is the Schema for the scalewayclustertemplates + 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: spec defines the desired state of ScalewayClusterTemplate + properties: + template: + description: template is a ScalewayCluster template resource. + properties: + metadata: + description: |- + metadata is a Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + minProperties: 1 + properties: + annotations: + additionalProperties: + type: string + description: |- + annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + type: object + labels: + additionalProperties: + type: string + description: |- + labels is a map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels + type: object + type: object + spec: + description: spec defines the desired state of ScalewayCluster + properties: + controlPlaneEndpoint: + description: controlPlaneEndpoint represents the endpoint + used to communicate with the control plane. + minProperties: 1 + properties: + host: + description: host is the hostname on which the API server + is serving. + maxLength: 512 + minLength: 1 + type: string + port: + description: port is the port on which the API server + is serving. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + failureDomains: + description: |- + failureDomains is a list of failure domains where the control-plane nodes will be created. + Failure domains correspond to Scaleway zones inside the cluster region (e.g. fr-par-1). + items: + description: ScalewayZone is a Scaleway zone (e.g. fr-par-1). + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + maxItems: 3 + minItems: 1 + type: array + x-kubernetes-list-type: set + network: + description: network contains network related options for + the cluster. + minProperties: 1 + properties: + controlPlaneDNS: + description: controlPlaneDNS allows configuring a Scaleway + Domain DNS Zone. + properties: + domain: + description: |- + domain is the DNS Zone that this record should live in. It must be pre-existing in your Scaleway account. + The format must be a string that conforms to the definition of a subdomain in DNS (RFC 1123). + This is optional if the control plane load balancer is private. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: |- + name is the DNS short name of the record (non-FQDN). The format must consist of + alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$ + type: string + required: + - name + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + controlPlaneExtraLoadBalancers: + description: |- + controlPlaneExtraLoadBalancers allows configuring additional load balancers. + Because Scaleway load balancers are currently zonal resources, you may set + up to 3 additional load balancers for achieving regional redundancy. It is + mandatory to set the controlPlaneDNS field when you do so. + NOTE: This may be removed in the future, when Scaleway supports regional LoadBalancers. + items: + description: LoadBalancer defines load balancer parameters. + minProperties: 1 + properties: + ip: + description: ip is an existing public IPv4 to use + when creating a load balancer. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + privateIP: + description: |- + privateIP is an existing private IPv4 inside the Private Network to use + when attaching a load balancer to a Private Network. It must be pre-booked + inside the Scaleway IPAM. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: LB-S + description: type is the load balancer commercial + offer type. + maxLength: 10 + minLength: 1 + type: string + zone: + description: |- + zone where to create the load balancer. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + maxItems: 3 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + controlPlaneLoadBalancer: + allOf: + - minProperties: 1 + - minProperties: 1 + description: controlPlaneLoadBalancer defines settings + for the load balancer of the control plane. + properties: + allowedRanges: + description: |- + allowedRanges allows to set a list of allowed IP ranges that can access + the cluster through the load balancer. When unset, all IP ranges are allowed. + To allow the cluster to work properly, public IPs of nodes and Public + Gateways will automatically be allowed. However, if this field is set, + you MUST manually allow IPs of the nodes of your management cluster. + items: + description: CIDR is an IP address range in CIDR + notation (for example, "10.0.0.0/8" or "fd00::/8"). + maxLength: 43 + minLength: 1 + type: string + x-kubernetes-validations: + - message: value must be a valid CIDR network address + rule: isCIDR(self) + maxItems: 30 + minItems: 1 + type: array + x-kubernetes-list-type: set + ip: + description: ip is an existing public IPv4 to use + when creating a load balancer. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + private: + description: private disables the creation of a public + IP on the load balancers when it's set to true. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + privateIP: + description: |- + privateIP is an existing private IPv4 inside the Private Network to use + when attaching a load balancer to a Private Network. It must be pre-booked + inside the Scaleway IPAM. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: LB-S + description: type is the load balancer commercial + offer type. + maxLength: 10 + minLength: 1 + type: string + zone: + description: |- + zone where to create the load balancer. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + x-kubernetes-validations: + - message: ip is immutable + rule: '!has(oldSelf.ip) || self.ip == oldSelf.ip' + - message: zone is immutable + rule: '!has(oldSelf.zone) || self.zone == oldSelf.zone' + - message: privateIP is immutable + rule: '!has(oldSelf.privateIP) || self.privateIP == + oldSelf.privateIP' + privateNetwork: + description: privateNetwork allows attaching machines + of the cluster to a Private Network. + minProperties: 1 + properties: + enabled: + description: |- + enabled allows to automatically attach machines to a Private Network when it's set to true. + The Private Network is automatically created if no existing Private + Network ID is provided. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + id: + description: id allows to reuse an existing Private + Network instead of creating a new one. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + subnet: + description: subnet defines a subnet for the Private + Network. Only used on newly created Private Networks. + maxLength: 43 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + - message: value must be a valid CIDR network address + rule: isCIDR(self) + vpcID: + description: vpcID defines the ID of the VPC where + the new Private Network will be created if none + is provided. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + required: + - enabled + type: object + x-kubernetes-validations: + - message: vpcID cannot be added or removed + rule: has(self.vpcID) == has(oldSelf.vpcID) + - message: id cannot be added or removed + rule: has(self.id) == has(oldSelf.id) + - message: subnet cannot be added or removed + rule: has(self.subnet) == has(oldSelf.subnet) + - message: id and vpcID cannot be set at the same time + rule: '!has(self.id) || has(self.id) != has(self.vpcID)' + - message: id and subnet cannot be set at the same time + rule: '!has(self.id) || has(self.id) != has(self.subnet)' + publicGateways: + description: |- + publicGateways allows to manage Public Gateways that will be created and + attached to the Private Network of the cluster. + items: + description: PublicGateway defines settings of the Public + Gateway that will be created. + minProperties: 1 + properties: + ip: + description: ip to use when creating a Public Gateway. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: VPC-GW-S + description: type is a Public Gateway commercial + offer type. + maxLength: 20 + minLength: 1 + type: string + zone: + description: |- + zone where to create the Public Gateway. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + maxItems: 6 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-validations: + - message: controlPlaneDNS is required when controlPlaneExtraLoadBalancers + is set + rule: '!has(self.controlPlaneExtraLoadBalancers) || has(self.controlPlaneDNS)' + - message: privateNetwork is required when publicGateways + is set + rule: '!has(self.publicGateways) || has(self.privateNetwork) + && self.privateNetwork.enabled' + - message: privateNetwork is required when private LoadBalancer + is enabled + rule: '!has(self.controlPlaneLoadBalancer) || !has(self.controlPlaneLoadBalancer.private) + || !self.controlPlaneLoadBalancer.private || has(self.privateNetwork) + && self.privateNetwork.enabled' + - message: .controlPlaneDNS.domain must be set unless control + plane load balancer is private + rule: '!has(self.controlPlaneDNS) || has(self.controlPlaneDNS) + && has(self.controlPlaneDNS.domain) || has(self.controlPlaneDNS) + && !has(self.controlPlaneDNS.domain) && has(self.controlPlaneLoadBalancer) + && has(self.controlPlaneLoadBalancer.private) && self.controlPlaneLoadBalancer.private' + projectID: + description: projectID is the ID of a Scaleway project where + the cluster will be created. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + region: + description: region represents the region where the cluster + will be hosted. + maxLength: 6 + minLength: 6 + pattern: ^[a-z]{2}-[a-z]{3}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + scalewaySecretName: + description: |- + scalewaySecretName is the name of the secret that contains the Scaleway client parameters. + The following keys are required: SCW_ACCESS_KEY, SCW_SECRET_KEY. + The following key is optional: SCW_API_URL. + maxLength: 253 + minLength: 1 + type: string + required: + - projectID + - region + - scalewaySecretName + type: object + x-kubernetes-validations: + - message: controlPlaneEndpoint is required once set + rule: '!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)' + - message: controlPlaneDNS cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneDNS)) + == (has(oldSelf.network) && has(oldSelf.network.controlPlaneDNS)) + - message: privateNetwork cannot be added or removed + rule: (has(self.network) && has(self.network.privateNetwork)) + == (has(oldSelf.network) && has(oldSelf.network.privateNetwork)) + - message: private cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.private)) == + (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) + && has(oldSelf.network.controlPlaneLoadBalancer.private)) + - message: ip cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.ip)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.ip)) + - message: zone cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.zone)) == (has(oldSelf.network) + && has(oldSelf.network.controlPlaneLoadBalancer) && has(oldSelf.network.controlPlaneLoadBalancer.zone)) + - message: privateIP cannot be added or removed + rule: (has(self.network) && has(self.network.controlPlaneLoadBalancer) + && has(self.network.controlPlaneLoadBalancer.privateIP)) == + (has(oldSelf.network) && has(oldSelf.network.controlPlaneLoadBalancer) + && has(oldSelf.network.controlPlaneLoadBalancer.privateIP)) + required: + - spec + type: object + required: + - template + type: object + required: + - spec + type: object + served: true storage: true diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachines.yaml index 7d0c669..f81ce9c 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachines.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewaymachines.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -31,6 +31,7 @@ spec: jsonPath: .status.ready name: Ready type: boolean + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -223,6 +224,320 @@ spec: - message: name must be a valid DNS label rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Instance commercial type + jsonPath: .spec.commercialType + name: CommercialType + type: string + - description: Node provider ID + jsonPath: .spec.providerID + name: ProviderID + type: string + - description: Provisioned is true when the machine infrastructure is fully provisioned + jsonPath: .status.initialization.provisioned + name: Provisioned + type: boolean + - description: ScalewayMachine pass all readiness checks + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayMachine is the Schema for the scalewaymachines 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: spec defines the desired state of ScalewayMachine + properties: + commercialType: + description: commercialType of instance (e.g. PRO2-S). + maxLength: 20 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + image: + allOf: + - maxProperties: 1 + minProperties: 1 + - maxProperties: 1 + minProperties: 1 + description: image defines an image ID, Name or Label to use to create + the instance. + properties: + id: + description: id of the Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + label: + description: label of the image (as defined in the marketplace). + maxLength: 100 + minLength: 1 + type: string + name: + description: name of the Scaleway resource. + maxLength: 100 + minLength: 1 + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + placementGroup: + description: placementGroup allows attaching a Placement Group to + the instance. + maxProperties: 1 + minProperties: 1 + properties: + id: + description: id of the Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + name: + description: name of the Scaleway resource. + maxLength: 100 + minLength: 1 + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + providerID: + description: providerID must match the provider ID as seen on the + node object corresponding to this machine. + maxLength: 512 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + publicNetwork: + description: publicNetwork allows attaching public IPs to the instance. + minProperties: 1 + properties: + enableIPv4: + description: enableIPv4 defines whether server should have an + IPv4 created and attached. + type: boolean + enableIPv6: + description: enableIPv6 defines whether server should have an + IPv6 created and attached. + type: boolean + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + rootVolume: + description: rootVolume defines the characteristics of the system + (root) volume. + minProperties: 1 + properties: + iops: + description: iops is the number of IOPS requested for the disk. + This is only applicable for block volumes. + format: int64 + minimum: 5000 + type: integer + size: + default: 20 + description: size of the root volume in GB. Defaults to 20 GB. + format: int64 + maximum: 10000 + minimum: 8 + type: integer + type: + default: block + description: |- + type of the root volume. Can be local or block. Note that not all types + of instances support local volumes. + enum: + - local + - block + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + - message: iops can only be set for block volumes + rule: '!has(self.iops) || has(self.type) && self.type == ''block''' + securityGroup: + description: securityGroup allows attaching a Security Group to the + instance. + maxProperties: 1 + minProperties: 1 + properties: + id: + description: id of the Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + name: + description: name of the Scaleway resource. + maxLength: 100 + minLength: 1 + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + required: + - commercialType + - image + type: object + x-kubernetes-validations: + - message: rootVolume cannot be added or removed + rule: has(self.rootVolume) == has(oldSelf.rootVolume) + - message: publicNetwork cannot be added or removed + rule: has(self.publicNetwork) == has(oldSelf.publicNetwork) + - message: placementGroup cannot be added or removed + rule: has(self.placementGroup) == has(oldSelf.placementGroup) + - message: securityGroup cannot be added or removed + rule: has(self.securityGroup) == has(oldSelf.securityGroup) + status: + description: status defines the observed state of ScalewayMachine + minProperties: 1 + properties: + addresses: + description: addresses contains the associated addresses for the machine. + items: + description: MachineAddress contains information for the node's + address. + properties: + address: + description: address is the machine address. + maxLength: 256 + minLength: 1 + type: string + type: + description: type is the machine address type, one of Hostname, + ExternalIP, InternalIP, ExternalDNS or InternalDNS. + enum: + - Hostname + - ExternalIP + - InternalIP + - ExternalDNS + - InternalDNS + type: string + required: + - address + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-type: atomic + conditions: + description: |- + conditions represent the current state of the ScalewayMachine resource. + Each condition has a unique type and reflects the status of a specific aspect of the resource. + + The status of each condition is one of True, False, or Unknown. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + initialization: + description: |- + initialization provides observations of the ScalewayMachine initialization process. + NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Machine provisioning. + minProperties: 1 + properties: + provisioned: + description: |- + provisioned is true when the infrastructure provider reports that the Machine's infrastructure is fully provisioned. + NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Machine provisioning. + type: boolean + type: object + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: name must be between 1 and 63 characters + rule: self.metadata.name.size() <= 63 + - message: name must be a valid DNS label + rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachinetemplates.yaml index d1ba4e4..e8947f5 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymachinetemplates.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewaymachinetemplates.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -18,7 +18,8 @@ spec: singular: scalewaymachinetemplate scope: Namespaced versions: - - name: v1alpha1 + - deprecated: true + name: v1alpha1 schema: openAPIV3Schema: description: ScalewayMachineTemplate is the Schema for the scalewaymachinetemplates @@ -190,4 +191,225 @@ spec: type: object type: object served: true + storage: false + - name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayMachineTemplate is the Schema for the scalewaymachinetemplates + 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: spec defines the desired state of ScalewayMachineTemplate + properties: + template: + description: template is a ScalewayMachine template resource. + properties: + metadata: + description: |- + metadata is a Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + minProperties: 1 + properties: + annotations: + additionalProperties: + type: string + description: |- + annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + type: object + labels: + additionalProperties: + type: string + description: |- + labels is a map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels + type: object + type: object + spec: + description: spec defines the desired state of ScalewayMachine + properties: + commercialType: + description: commercialType of instance (e.g. PRO2-S). + maxLength: 20 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + image: + allOf: + - maxProperties: 1 + minProperties: 1 + - maxProperties: 1 + minProperties: 1 + description: image defines an image ID, Name or Label to use + to create the instance. + properties: + id: + description: id of the Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + label: + description: label of the image (as defined in the marketplace). + maxLength: 100 + minLength: 1 + type: string + name: + description: name of the Scaleway resource. + maxLength: 100 + minLength: 1 + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + placementGroup: + description: placementGroup allows attaching a Placement Group + to the instance. + maxProperties: 1 + minProperties: 1 + properties: + id: + description: id of the Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + name: + description: name of the Scaleway resource. + maxLength: 100 + minLength: 1 + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + providerID: + description: providerID must match the provider ID as seen + on the node object corresponding to this machine. + maxLength: 512 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + publicNetwork: + description: publicNetwork allows attaching public IPs to + the instance. + minProperties: 1 + properties: + enableIPv4: + description: enableIPv4 defines whether server should + have an IPv4 created and attached. + type: boolean + enableIPv6: + description: enableIPv6 defines whether server should + have an IPv6 created and attached. + type: boolean + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + rootVolume: + description: rootVolume defines the characteristics of the + system (root) volume. + minProperties: 1 + properties: + iops: + description: iops is the number of IOPS requested for + the disk. This is only applicable for block volumes. + format: int64 + minimum: 5000 + type: integer + size: + default: 20 + description: size of the root volume in GB. Defaults to + 20 GB. + format: int64 + maximum: 10000 + minimum: 8 + type: integer + type: + default: block + description: |- + type of the root volume. Can be local or block. Note that not all types + of instances support local volumes. + enum: + - local + - block + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + - message: iops can only be set for block volumes + rule: '!has(self.iops) || has(self.type) && self.type == + ''block''' + securityGroup: + description: securityGroup allows attaching a Security Group + to the instance. + maxProperties: 1 + minProperties: 1 + properties: + id: + description: id of the Scaleway resource. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + name: + description: name of the Scaleway resource. + maxLength: 100 + minLength: 1 + type: string + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + required: + - commercialType + - image + type: object + x-kubernetes-validations: + - message: rootVolume cannot be added or removed + rule: has(self.rootVolume) == has(oldSelf.rootVolume) + - message: publicNetwork cannot be added or removed + rule: has(self.publicNetwork) == has(oldSelf.publicNetwork) + - message: placementGroup cannot be added or removed + rule: has(self.placementGroup) == has(oldSelf.placementGroup) + - message: securityGroup cannot be added or removed + rule: has(self.securityGroup) == has(oldSelf.securityGroup) + required: + - spec + type: object + required: + - template + type: object + required: + - spec + type: object + served: true storage: true diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedclusters.yaml index 7ff66bb..c91cb19 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedclusters.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewaymanagedclusters.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -39,6 +39,7 @@ spec: jsonPath: .spec.controlPlaneEndpoint.port name: Port type: integer + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -210,6 +211,309 @@ spec: - message: name must be a valid DNS label rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Cluster to which this ScalewayManagedCluster belongs + jsonPath: .metadata.labels.cluster\.x-k8s\.io/cluster-name + name: Cluster + type: string + - description: Provisioned is true when the cluster infrastructure is fully provisioned + jsonPath: .status.initialization.provisioned + name: Provisioned + type: boolean + - description: ScalewayManagedCluster pass all readiness checks + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - description: Region of the managed cluster + jsonPath: .spec.region + name: Region + type: string + - description: Host of the control plane + jsonPath: .spec.controlPlaneEndpoint.host + name: Host + type: string + - description: Port of the control plane + jsonPath: .spec.controlPlaneEndpoint.port + name: Port + type: integer + name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayManagedCluster is the Schema for the scalewaymanagedclusters + 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: spec defines the desired state of ScalewayManagedCluster + properties: + controlPlaneEndpoint: + description: controlPlaneEndpoint represents the endpoint used to + communicate with the control plane. + minProperties: 1 + properties: + host: + description: host is the hostname on which the API server is serving. + maxLength: 512 + minLength: 1 + type: string + port: + description: port is the port on which the API server is serving. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + network: + description: network defines the network configuration of the managed + cluster. + minProperties: 1 + properties: + privateNetwork: + description: privateNetwork allows attaching machines of the cluster + to a Private Network. + minProperties: 1 + properties: + id: + description: id allows to reuse an existing Private Network + instead of creating a new one. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + subnet: + description: subnet defines a subnet for the Private Network. + Only used on newly created Private Networks. + maxLength: 43 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + - message: value must be a valid CIDR network address + rule: isCIDR(self) + vpcID: + description: vpcID defines the ID of the VPC where the new + Private Network will be created if none is provided. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + type: object + x-kubernetes-validations: + - message: vpcID cannot be added or removed + rule: has(self.vpcID) == has(oldSelf.vpcID) + - message: id cannot be added or removed + rule: has(self.id) == has(oldSelf.id) + - message: subnet cannot be added or removed + rule: has(self.subnet) == has(oldSelf.subnet) + - message: id and vpcID cannot be set at the same time + rule: '!has(self.id) || has(self.id) != has(self.vpcID)' + - message: id and subnet cannot be set at the same time + rule: '!has(self.id) || has(self.id) != has(self.subnet)' + publicGateways: + description: |- + publicGateways allows to manage Public Gateways that will be created and + attached to the Private Network of the cluster. + items: + description: PublicGateway defines settings of the Public Gateway + that will be created. + minProperties: 1 + properties: + ip: + description: ip to use when creating a Public Gateway. + format: ipv4 + maxLength: 15 + minLength: 1 + type: string + type: + default: VPC-GW-S + description: type is a Public Gateway commercial offer type. + maxLength: 20 + minLength: 1 + type: string + zone: + description: |- + zone where to create the Public Gateway. Must be in the same region as the + cluster. Defaults to the first zone of the region. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + type: object + maxItems: 6 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + type: object + projectID: + description: projectID in which the managed cluster will be created. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + region: + description: region where the managed cluster will be created. + maxLength: 6 + minLength: 6 + pattern: ^[a-z]{2}-[a-z]{3}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + scalewaySecretName: + description: |- + scalewaySecretName is the name of the secret that contains the Scaleway client parameters. + The following keys are required: SCW_ACCESS_KEY, SCW_SECRET_KEY. + The following key is optional: SCW_API_URL. + maxLength: 253 + minLength: 1 + type: string + required: + - projectID + - region + - scalewaySecretName + type: object + x-kubernetes-validations: + - message: controlPlaneEndpoint is required once set + rule: '!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)' + - message: privateNetwork cannot be added or removed + rule: (has(self.network) && has(self.network.privateNetwork)) == (has(oldSelf.network) + && has(oldSelf.network.privateNetwork)) + status: + description: status defines the observed state of ScalewayManagedCluster + minProperties: 1 + properties: + conditions: + description: |- + conditions represent the current state of the ScalewayManagedCluster resource. + Each condition has a unique type and reflects the status of a specific aspect of the resource. + + The status of each condition is one of True, False, or Unknown. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + initialization: + description: |- + initialization provides observations of the ScalewayManagedCluster initialization process. + NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning. + minProperties: 1 + properties: + provisioned: + description: |- + provisioned is true when the infrastructure provider reports that the Cluster's infrastructure is fully provisioned. + NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Cluster provisioning. + type: boolean + type: object + network: + description: network contains information about currently provisioned + network resources. + minProperties: 1 + properties: + privateNetworkID: + description: privateNetworkID is the ID of the Private Network + that is attached to the cluster. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + type: object + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: name must be between 1 and 63 characters + rule: self.metadata.name.size() <= 63 + - message: name must be a valid DNS label + rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedcontrolplanes.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedcontrolplanes.yaml index 8c411aa..5439b64 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedcontrolplanes.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedcontrolplanes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewaymanagedcontrolplanes.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -39,6 +39,7 @@ spec: jsonPath: .spec.controlPlaneEndpoint.port name: Port type: integer + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -337,7 +338,6 @@ spec: description: |- Initialized is true when the control plane is available for initial contact. This may occur before the control plane is fully ready. - In the AzureManagedControlPlane implementation, these are identical. type: boolean ready: description: Ready is true when the provider resource is ready. @@ -356,6 +356,492 @@ spec: - message: name must be a valid DNS label rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Cluster to which this ScalewayManagedControlPlane belongs + jsonPath: .metadata.labels.cluster\.x-k8s\.io/cluster-name + name: Cluster + type: string + - description: This denotes whether or not the control plane can accept requests + jsonPath: .status.initialization.controlPlaneInitialized + name: Initialized + type: boolean + - description: ScalewayManagedControlPlane pass all readiness checks + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - description: The Kubernetes version of the Scaleway control plane + jsonPath: .status.version + name: Version + type: string + - description: Host of the control plane + jsonPath: .spec.controlPlaneEndpoint.host + name: Host + type: string + - description: Port of the control plane + jsonPath: .spec.controlPlaneEndpoint.port + name: Port + type: integer + name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayManagedControlPlane is the Schema for the scalewaymanagedcontrolplanes + 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: spec defines the desired state of ScalewayManagedControlPlane + properties: + acl: + description: acl configures the ACLs of the managed cluster. If not + set, ACLs will be set to [0.0.0.0/0]. + properties: + allowedRanges: + description: |- + allowedRanges is a list of allowed public IP ranges that can access + the managed cluster. When empty, all IP ranges are DENIED. Make sure the nodes + of your management cluster can still access the cluster by allowing their IPs. + items: + description: CIDR is an IP address range in CIDR notation (for + example, "10.0.0.0/8" or "fd00::/8"). + maxLength: 43 + minLength: 1 + type: string + x-kubernetes-validations: + - message: value must be a valid CIDR network address + rule: isCIDR(self) + maxItems: 30 + type: array + x-kubernetes-list-type: set + type: object + additionalTags: + description: additionalTags that will be added to the default tags. + items: + maxLength: 128 + minLength: 1 + type: string + maxItems: 30 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + admissionPlugins: + description: admissionPlugins to enable. + items: + maxLength: 128 + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: set + apiServerCertSANs: + description: |- + apiServerCertSANs defines additional Subject Alternative Names for the + Kubernetes API server certificate. + items: + maxLength: 255 + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: set + autoUpgrade: + description: autoUpgrade configuration of the cluster. + properties: + enabled: + description: enabled defines whether auto upgrade is enabled for + the cluster. + type: boolean + maintenanceWindow: + description: maintenanceWindow of the cluster auto upgrades. + minProperties: 1 + properties: + day: + description: day of the week for the maintenance window. + enum: + - any + - monday + - tuesday + - wednesday + - thursday + - friday + - saturday + - sunday + type: string + startHour: + description: startHour is the start time of the two-hour maintenance + window. + format: int32 + minimum: 0 + type: integer + type: object + required: + - enabled + type: object + autoscaler: + description: autoscaler configuration of the cluster. + minProperties: 1 + properties: + balanceSimilarNodeGroups: + description: |- + balanceSimilarNodeGroups allows to detect similar node groups and balance + the number of nodes between them. + type: boolean + estimator: + description: estimator is the type of resource estimator to be + used in scale up. + enum: + - binpacking + type: string + expander: + description: expander is the type of node group expander to be + used in scale up. + enum: + - random + - most_pods + - least_waste + - priority + - price + type: string + expendablePodsPriorityCutoff: + description: |- + expendablePodsPriorityCutoff defines the priority threshold below which pods + are considered expendable. Pods with priority below cutoff will be expendable. + They can be killed without any consideration during scale down and they won't cause scale up. + Pods with null priority (PodPriority disabled) are non expendable. + format: int32 + type: integer + ignoreDaemonsetsUtilization: + description: |- + ignoreDaemonsetsUtilization allows to ignore DaemonSet pods when calculating + resource utilization for scaling down. + type: boolean + maxGracefulTerminationSec: + description: |- + maxGracefulTerminationSec is the maximum number of seconds the cluster autoscaler + waits for pod termination when trying to scale down a node. + format: int32 + minimum: 1 + type: integer + scaleDownDelayAfterAdd: + description: scaleDownDelayAfterAdd defines how long after scale + up the scale down evaluation resumes. + format: duration + maxLength: 10 + minLength: 1 + type: string + scaleDownDisabled: + description: scaleDownDisabled allows to disable the cluster autoscaler. + type: boolean + scaleDownUnneededTime: + description: |- + scaleDownUnneededTime defines how long a node should be unneeded before it + is eligible to be scaled down. + format: duration + maxLength: 10 + minLength: 1 + type: string + scaleDownUtilizationThreshold: + description: |- + scaleDownUtilizationThreshold is the Node utilization level, defined as a + sum of requested resources divided by capacity, below which a node can be + considered for scale down. + format: float + maxLength: 10 + minLength: 1 + type: string + type: object + clusterName: + description: |- + clusterName allows you to specify the name of the Scaleway managed cluster. + If you don't specify a name then a default name will be created + based on the namespace and name of the managed control plane. + maxLength: 100 + minLength: 1 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + cni: + description: cni plugin running in the cluster. + enum: + - cilium + - cilium_native + - calico + - kilo + - none + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + controlPlaneEndpoint: + description: controlPlaneEndpoint represents the endpoint used to + communicate with the control plane. + minProperties: 1 + properties: + host: + description: host is the hostname on which the API server is serving. + maxLength: 512 + minLength: 1 + type: string + port: + description: port is the port on which the API server is serving. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + enablePrivateEndpoint: + description: |- + enablePrivateEndpoint defines whether the apiserver's internal address + is used as the cluster endpoint. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + featureGates: + description: featureGates to enable. + items: + maxLength: 128 + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: set + onDelete: + description: onDelete configures the settings to apply when deleting + the Scaleway managed cluster. + minProperties: 1 + properties: + withAdditionalResources: + description: |- + withAdditionalResources allows to also automatically delete all volumes + (including those with volume type "retain"), empty Private Networks and + Load Balancers whose names start with cluster ID. + type: boolean + type: object + openIDConnect: + description: openIDConnect defines the OpenID Connect configuration + of the Kubernetes API server. + properties: + clientID: + description: clientID is a client ID that all tokens must be issued + for. + maxLength: 255 + minLength: 1 + type: string + groupsClaim: + description: groupsClaim is the JWT claim to use as the user's + group. + items: + maxLength: 100 + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: set + groupsPrefix: + description: |- + groupsPrefix is the prefix prepended to group claims to prevent name collision (such as "system:" groups). + For example, the value "oidc:" will create group names like "oidc:engineering" and "oidc:infra". + maxLength: 100 + minLength: 1 + type: string + issuerURL: + description: |- + issuerURL of the provider which allows the API server to discover public signing keys. + Only URLs using the https:// scheme are accepted. This is typically the provider's + discovery URL without a path, for example "https://accounts.google.com" or "https://login.salesforce.com". + maxLength: 255 + minLength: 1 + type: string + requiredClaim: + description: |- + requiredClaim is multiple key=value pairs describing a required claim in the ID token. + If set, the claims are verified to be present in the ID token with a matching value. + items: + maxLength: 100 + minLength: 1 + type: string + maxItems: 10 + minItems: 1 + type: array + x-kubernetes-list-type: set + usernameClaim: + description: |- + usernameClaim is the JWT claim to use as the user name. The default is "sub", + which is expected to be the end user's unique identifier. Admins can choose other claims, + such as email or name, depending on their provider. However, claims other + than email will be prefixed with the issuer URL to prevent name collision. + maxLength: 100 + minLength: 1 + type: string + usernamePrefix: + description: |- + usernamePrefix is the prefix prepended to username claims to prevent name collision (such as "system:" users). + For example, the value "oidc:"" will create usernames like "oidc:jane.doe". + If this flag is not provided and "username_claim" is a value other than email, + the prefix defaults to "( Issuer URL )#" where "( Issuer URL )" is the value of "issuer_url". + The value "-" can be used to disable all prefixing. + maxLength: 100 + minLength: 1 + type: string + required: + - clientID + - issuerURL + type: object + type: + default: kapsule + description: type of the cluster (e.g. kapsule, multicloud, etc.). + maxLength: 50 + minLength: 1 + type: string + version: + description: version defines the desired Kubernetes version. + maxLength: 256 + minLength: 1 + type: string + required: + - version + type: object + x-kubernetes-validations: + - message: controlPlaneEndpoint is required once set + rule: '!has(oldSelf.controlPlaneEndpoint) || has(self.controlPlaneEndpoint)' + - message: clusterName cannot be removed once set + rule: '!has(oldSelf.clusterName) || has(self.clusterName) == has(oldSelf.clusterName)' + - message: cni cannot be added or removed + rule: has(self.cni) == has(oldSelf.cni) + - message: enablePrivateEndpoint cannot be added or removed + rule: has(self.enablePrivateEndpoint) == has(oldSelf.enablePrivateEndpoint) + status: + description: status defines the observed state of ScalewayManagedControlPlane + minProperties: 1 + properties: + conditions: + description: |- + conditions represent the current state of the ScalewayManagedControlPlane resource. + Each condition has a unique type and reflects the status of a specific aspect of the resource. + + The status of each condition is one of True, False, or Unknown. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + externalManagedControlPlane: + default: true + description: |- + externalManagedControlPlane is a bool that should be set to true if the + Node objects do not exist in the cluster. + type: boolean + initialization: + description: |- + initialization provides observations of the ScalewayManagedControlPlane initialization process. + NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial Cluster provisioning. + minProperties: 1 + properties: + controlPlaneInitialized: + description: |- + controlPlaneInitialized is true when the control plane provider reports that the Kubernetes control plane is initialized; + usually a control plane is considered initialized when it can accept requests, no matter if this happens before + the control plane is fully provisioned or not. + NOTE: this field is part of the Cluster API contract, and it is used to orchestrate initial Cluster provisioning. + type: boolean + type: object + version: + description: version defines the desired Kubernetes version for the + control plane. + maxLength: 256 + minLength: 1 + type: string + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: name must be between 1 and 63 characters + rule: self.metadata.name.size() <= 63 + - message: name must be a valid DNS label + rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') + served: true storage: true subresources: status: {} diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedmachinepools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedmachinepools.yaml index 77c3c5b..fefd939 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedmachinepools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_scalewaymanagedmachinepools.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.19.0 name: scalewaymanagedmachinepools.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -25,6 +25,7 @@ spec: - jsonPath: .status.replicas name: Replicas type: string + deprecated: true name: v1alpha1 schema: openAPIV3Schema: @@ -211,6 +212,306 @@ spec: - message: name must be a valid DNS label rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Provisioned is true when the machinepool infrastructure is fully + provisioned + jsonPath: .status.initialization.provisioned + name: Provisioned + type: boolean + - description: ScalewayManagedMachinePool pass all readiness checks + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.replicas + name: Replicas + type: string + name: v1alpha2 + schema: + openAPIV3Schema: + description: ScalewayManagedMachinePool is the Schema for the scalewaymanagedmachinepools + 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: spec defines the desired state of ScalewayManagedMachinePool + properties: + additionalTags: + description: additionalTags that will be added to the default tags. + items: + maxLength: 128 + minLength: 1 + type: string + maxItems: 30 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + autohealing: + description: autohealing defines whether the autohealing feature is + enabled for the pool. + type: boolean + kubeletArgs: + additionalProperties: + type: string + description: kubeletArgs defines Kubelet arguments to be used by this + pool. + type: object + nodeType: + description: |- + nodeType is the type of Scaleway Instance wanted for the pool. Nodes with + insufficient memory are not eligible (DEV1-S, PLAY2-PICO, STARDUST). + "external" is a special node type used to provision instances from other + cloud providers in a Kosmos Cluster. + maxLength: 30 + minLength: 2 + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + placementGroupID: + description: |- + placementGroupID in which all the nodes of the pool will be created, + placement groups are limited to 20 instances. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + providerIDList: + description: |- + providerIDList are the identification IDs of machine instances provided by the provider. + This field must match the provider IDs as seen on the node objects corresponding to a machine pool's machine instances. + items: + maxLength: 512 + minLength: 1 + type: string + maxItems: 10000 + type: array + x-kubernetes-list-type: atomic + publicIPDisabled: + description: |- + publicIPDisabled defines if the public IP should be removed from Nodes. + To use this feature, your Cluster must have an attached Private Network + set up with a Public Gateway. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + rootVolumeSizeGB: + description: rootVolumeSizeGB is the size of the System volume disk + size, in GB. + format: int64 + minimum: 20 + type: integer + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + rootVolumeType: + description: rootVolumeType is the system volume disk type. + enum: + - l_ssd + - sbs_5k + - sbs_15k + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + scaling: + description: scaling configures the scaling of the pool. + minProperties: 1 + properties: + autoscaling: + description: autoscaling defines whether the autoscaling feature + is enabled for the pool. + type: boolean + maxSize: + description: |- + maxSize defines the maximum size of the pool. Note that this field is only + used when autoscaling is enabled on the pool. + format: int32 + minimum: 0 + type: integer + minSize: + description: |- + minSize defines the minimum size of the pool. Note that this field is only + used when autoscaling is enabled on the pool. + format: int32 + minimum: 0 + type: integer + type: object + securityGroupID: + description: |- + securityGroupID in which all the nodes of the pool will be created. If unset, + the pool will use default Kapsule security group in current zone. + maxLength: 36 + minLength: 36 + pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + upgradePolicy: + description: upgradePolicy defines the pool's upgrade policy. + minProperties: 1 + properties: + maxSurge: + description: |- + maxSurge is the maximum number of additional nodes that can be provisioned + during upgrades. + format: int32 + minimum: 0 + type: integer + maxUnavailable: + description: maxUnavailable is the maximum number of available + nodes during upgrades. + format: int32 + minimum: 0 + type: integer + type: object + zone: + description: zone in which the pool's nodes will be spawned. + maxLength: 9 + minLength: 8 + pattern: ^[a-z]{2}-[a-z]{3}-[0-9]{0,2}$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + required: + - nodeType + - zone + type: object + x-kubernetes-validations: + - message: placementGroupID cannot be added or removed + rule: has(self.placementGroupID) == has(oldSelf.placementGroupID) + - message: rootVolumeType cannot be added or removed + rule: has(self.rootVolumeType) == has(oldSelf.rootVolumeType) + - message: rootVolumeSizeGB cannot be added or removed + rule: has(self.rootVolumeSizeGB) == has(oldSelf.rootVolumeSizeGB) + - message: publicIPDisabled cannot be added or removed + rule: has(self.publicIPDisabled) == has(oldSelf.publicIPDisabled) + - message: securityGroupID cannot be added or removed + rule: has(self.securityGroupID) == has(oldSelf.securityGroupID) + status: + description: status defines the observed state of ScalewayManagedMachinePool + minProperties: 1 + properties: + conditions: + description: |- + conditions represent the current state of the ScalewayManagedMachinePool resource. + Each condition has a unique type and reflects the status of a specific aspect of the resource. + + The status of each condition is one of True, False, or Unknown. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + initialization: + description: |- + initialization provides observations of the ScalewayManagedMachinePool initialization process. + NOTE: Fields in this struct are part of the Cluster API contract and are used to orchestrate initial MachinePool provisioning. + minProperties: 1 + properties: + provisioned: + description: provisioned is true when the infrastructure provider + reports that the MachinePool's infrastructure is fully provisioned. + type: boolean + type: object + ready: + description: |- + ready is true when the provider resource is ready. + Deprecated: this field is kept for now as CAPI v1.11.3 still needs it. + The .initialization.provisioned field should be used instead. + type: boolean + replicas: + description: replicas is the most recently observed number of replicas. + format: int32 + type: integer + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: name must be between 1 and 63 characters + rule: self.metadata.name.size() <= 63 + - message: name must be a valid DNS label + rule: self.metadata.name.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$') + served: true storage: true subresources: status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 50e048f..58924c9 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -1,5 +1,7 @@ -commonLabels: - cluster.x-k8s.io/v1beta1: v1alpha1 +labels: +- pairs: + cluster.x-k8s.io/v1beta1: v1alpha1 + cluster.x-k8s.io/v1beta2: v1alpha2 # 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. @@ -17,9 +19,16 @@ resources: patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD +- path: patches/webhook_in_scalewayclusters.yaml +- path: patches/webhook_in_scalewaymachines.yaml +- path: patches/webhook_in_scalewayclustertemplates.yaml +- path: patches/webhook_in_scalewaymachinetemplates.yaml +- path: patches/webhook_in_scalewaymanagedclusters.yaml +- path: patches/webhook_in_scalewaymanagedcontrolplanes.yaml +- path: patches/webhook_in_scalewaymanagedmachinepools.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. -#configurations: -#- kustomizeconfig.yaml +configurations: +- kustomizeconfig.yaml diff --git a/config/crd/patches/webhook_in_scalewayclusters.yaml b/config/crd/patches/webhook_in_scalewayclusters.yaml new file mode 100644 index 0000000..4773c32 --- /dev/null +++ b/config/crd/patches/webhook_in_scalewayclusters.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewayclusters.infrastructure.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_scalewayclustertemplates.yaml b/config/crd/patches/webhook_in_scalewayclustertemplates.yaml new file mode 100644 index 0000000..98e862b --- /dev/null +++ b/config/crd/patches/webhook_in_scalewayclustertemplates.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewayclustertemplates.infrastructure.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_scalewaymachines.yaml b/config/crd/patches/webhook_in_scalewaymachines.yaml new file mode 100644 index 0000000..0eb1f01 --- /dev/null +++ b/config/crd/patches/webhook_in_scalewaymachines.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewaymachines.infrastructure.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_scalewaymachinetemplates.yaml b/config/crd/patches/webhook_in_scalewaymachinetemplates.yaml new file mode 100644 index 0000000..319b677 --- /dev/null +++ b/config/crd/patches/webhook_in_scalewaymachinetemplates.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewaymachinetemplates.infrastructure.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_scalewaymanagedclusters.yaml b/config/crd/patches/webhook_in_scalewaymanagedclusters.yaml new file mode 100644 index 0000000..d3cf187 --- /dev/null +++ b/config/crd/patches/webhook_in_scalewaymanagedclusters.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewaymanagedclusters.infrastructure.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_scalewaymanagedcontrolplanes.yaml b/config/crd/patches/webhook_in_scalewaymanagedcontrolplanes.yaml new file mode 100644 index 0000000..b810413 --- /dev/null +++ b/config/crd/patches/webhook_in_scalewaymanagedcontrolplanes.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewaymanagedcontrolplanes.infrastructure.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_scalewaymanagedmachinepools.yaml b/config/crd/patches/webhook_in_scalewaymanagedmachinepools.yaml new file mode 100644 index 0000000..5f7dfca --- /dev/null +++ b/config/crd/patches/webhook_in_scalewaymanagedmachinepools.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: scalewaymanagedmachinepools.infrastructure.cluster.x-k8s.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 7ac69cd..de9e816 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -20,9 +20,9 @@ resources: - ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- ../webhook +- ../webhook # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager +- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus # [METRICS] Expose the controller manager metrics service. @@ -50,13 +50,13 @@ patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- path: manager_webhook_patch.yaml -# target: -# kind: Deployment +- path: manager_webhook_patch.yaml + target: + kind: Deployment # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations -#replacements: +replacements: # - source: # Uncomment the following block to enable certificates for metrics # kind: Service # version: v1 @@ -117,42 +117,42 @@ patches: # index: 1 # create: true # -# - source: # Uncomment the following block if you have any webhook -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # Name of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # Namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true + - source: # Uncomment the following block if you have any webhook + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.name # Name of the service + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPaths: + - .spec.dnsNames.0 + - .spec.dnsNames.1 + options: + delimiter: '.' + index: 0 + create: true + - source: + kind: Service + version: v1 + name: webhook-service + fieldPath: .metadata.namespace # Namespace of the service + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPaths: + - .spec.dnsNames.0 + - .spec.dnsNames.1 + options: + delimiter: '.' + index: 1 + create: true # # - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) # kind: Certificate @@ -185,50 +185,176 @@ patches: # index: 1 # create: true # -# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true + - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPath: .metadata.namespace # Namespace of the certificate CR + targets: + - select: + kind: MutatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - source: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPath: .metadata.name + targets: + - select: + kind: MutatingWebhookConfiguration + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true # -# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. + - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPath: .metadata.namespace # Namespace of the certificate CR + targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. + - select: + kind: CustomResourceDefinition + name: scalewayclusters.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymachines.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + name: scalewayclustertemplates.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymachinetemplates.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymanagedclusters.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymanagedcontrolplanes.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymanagedmachinepools.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 0 + create: true # +kubebuilder:scaffold:crdkustomizecainjectionns -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. + - source: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert + fieldPath: .metadata.name + targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. + - select: + kind: CustomResourceDefinition + name: scalewayclusters.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymachines.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + name: scalewayclustertemplates.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymachinetemplates.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymanagedclusters.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymanagedcontrolplanes.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true + - select: + kind: CustomResourceDefinition + name: scalewaymanagedmachinepools.infrastructure.cluster.x-k8s.io + fieldPaths: + - .metadata.annotations.[cert-manager.io/inject-ca-from] + options: + delimiter: '/' + index: 1 + create: true # +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 0000000..963c8a4 --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,31 @@ +# This patch ensures the webhook certificates are properly mounted in the manager container. +# It configures the necessary arguments, volumes, volume mounts, and container ports. + +# Add the --webhook-cert-path argument for configuring the webhook certificate path +- op: add + path: /spec/template/spec/containers/0/args/- + value: --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs + +# Add the volumeMount for the webhook certificates +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + +# Add the port configuration for the webhook server +- op: add + path: /spec/template/spec/containers/0/ports/- + value: + containerPort: 9443 + name: webhook-server + protocol: TCP + +# Add the volume configuration for the webhook certificates +- op: add + path: /spec/template/spec/volumes/- + value: + name: webhook-certs + secret: + secretName: webhook-server-cert diff --git a/config/network-policy/allow-webhook-traffic.yaml b/config/network-policy/allow-webhook-traffic.yaml new file mode 100644 index 0000000..6ff3f33 --- /dev/null +++ b/config/network-policy/allow-webhook-traffic.yaml @@ -0,0 +1,27 @@ +# This NetworkPolicy allows ingress traffic to your webhook server running +# as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks +# will only work when applied in namespaces labeled with 'webhook: enabled' +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: allow-webhook-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: cluster-api-provider-scaleway + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label webhook: enabled + - from: + - namespaceSelector: + matchLabels: + webhook: enabled # Only from namespaces with this label + ports: + - port: 443 + protocol: TCP diff --git a/config/network-policy/kustomization.yaml b/config/network-policy/kustomization.yaml index ec0fb5e..0872bee 100644 --- a/config/network-policy/kustomization.yaml +++ b/config/network-policy/kustomization.yaml @@ -1,2 +1,3 @@ resources: +- allow-webhook-traffic.yaml - allow-metrics-traffic.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 1a83336..3a1d73e 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -16,6 +16,30 @@ rules: - patch - update - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch +- apiGroups: + - apiextensions.k8s.io + resourceNames: + - scalewayclusters.infrastructure.cluster.x-k8s.io + - scalewayclustertemplates.infrastructure.cluster.x-k8s.io + - scalewaymachines.infrastructure.cluster.x-k8s.io + - scalewaymachinetemplates.infrastructure.cluster.x-k8s.io + - scalewaymanagedclusters.infrastructure.cluster.x-k8s.io + - scalewaymanagedcontrolplanes.infrastructure.cluster.x-k8s.io + - scalewaymanagedmachinepools.infrastructure.cluster.x-k8s.io + resources: + - customresourcedefinitions + - customresourcedefinitions/status + verbs: + - patch + - update - apiGroups: - cluster.x-k8s.io resources: @@ -67,3 +91,14 @@ rules: - get - patch - update +- apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - scalewayclustertemplates + - scalewaymachinetemplates + verbs: + - get + - list + - patch + - update + - watch diff --git a/config/samples/infrastructure_v1alpha2_scalewaycluster.yaml b/config/samples/infrastructure_v1alpha2_scalewaycluster.yaml new file mode 100644 index 0000000..f4d3be2 --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewaycluster.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayCluster +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewaycluster-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/infrastructure_v1alpha2_scalewayclustertemplate.yaml b/config/samples/infrastructure_v1alpha2_scalewayclustertemplate.yaml new file mode 100644 index 0000000..9298ceb --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewayclustertemplate.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayClusterTemplate +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewayclustertemplate-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/infrastructure_v1alpha2_scalewaymachine.yaml b/config/samples/infrastructure_v1alpha2_scalewaymachine.yaml new file mode 100644 index 0000000..a3f0ce3 --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewaymachine.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayMachine +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewaymachine-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/infrastructure_v1alpha2_scalewaymachinetemplate.yaml b/config/samples/infrastructure_v1alpha2_scalewaymachinetemplate.yaml new file mode 100644 index 0000000..703b269 --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewaymachinetemplate.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayMachineTemplate +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewaymachinetemplate-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/infrastructure_v1alpha2_scalewaymanagedcluster.yaml b/config/samples/infrastructure_v1alpha2_scalewaymanagedcluster.yaml new file mode 100644 index 0000000..408471f --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewaymanagedcluster.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayManagedCluster +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewaymanagedcluster-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/infrastructure_v1alpha2_scalewaymanagedcontrolplane.yaml b/config/samples/infrastructure_v1alpha2_scalewaymanagedcontrolplane.yaml new file mode 100644 index 0000000..b6c8ebd --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewaymanagedcontrolplane.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayManagedControlPlane +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewaymanagedcontrolplane-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/infrastructure_v1alpha2_scalewaymanagedmachinepool.yaml b/config/samples/infrastructure_v1alpha2_scalewaymanagedmachinepool.yaml new file mode 100644 index 0000000..d4861a8 --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_scalewaymanagedmachinepool.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: ScalewayManagedMachinePool +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: scalewaymanagedmachinepool-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index d8b61d5..3042240 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -7,4 +7,11 @@ resources: - infrastructure_v1alpha1_scalewaymanagedcluster.yaml - infrastructure_v1alpha1_scalewaymanagedcontrolplane.yaml - infrastructure_v1alpha1_scalewaymanagedmachinepool.yaml +- infrastructure_v1alpha2_scalewaycluster.yaml +- infrastructure_v1alpha2_scalewaymachine.yaml +- infrastructure_v1alpha2_scalewayclustertemplate.yaml +- infrastructure_v1alpha2_scalewaymachinetemplate.yaml +- infrastructure_v1alpha2_scalewaymanagedcluster.yaml +- infrastructure_v1alpha2_scalewaymanagedcontrolplane.yaml +- infrastructure_v1alpha2_scalewaymanagedmachinepool.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 0000000..9cf2613 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 0000000..206316e --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,22 @@ +# the following config is for teaching kustomize where to look at when substituting nameReference. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 0000000..304351d --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-infrastructure-cluster-x-k8s-io-v1alpha2-scalewaymanagedcontrolplane + failurePolicy: Fail + name: mscalewaymanagedcontrolplane-v1alpha2.kb.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - scalewaymanagedcontrolplanes + sideEffects: None diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 0000000..cd8999a --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-scaleway + app.kubernetes.io/managed-by: kustomize + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager + app.kubernetes.io/name: cluster-api-provider-scaleway diff --git a/docs/advanced.md b/docs/advanced.md index cbc16f3..18e58d3 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -16,7 +16,7 @@ of the node instead. Here is an example of `KubeadmControlPlane` configuration: ```yaml -apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +apiVersion: controlplane.cluster.x-k8s.io/v1beta2 kind: KubeadmControlPlane metadata: name: my-kubeadm-controlplane @@ -28,21 +28,23 @@ spec: advertiseAddress: "[[[ .NodeIP ]]]" nodeRegistration: kubeletExtraArgs: - node-ip: "[[[ .NodeIP ]]]" + - name: node-ip + value: "[[[ .NodeIP ]]]" joinConfiguration: controlPlane: localAPIEndpoint: advertiseAddress: "[[[ .NodeIP ]]]" nodeRegistration: kubeletExtraArgs: - node-ip: "[[[ .NodeIP ]]]" + - name: node-ip + value: "[[[ .NodeIP ]]]" # important: some fields were omitted... ``` Here is an example of `KubeadmConfigTemplate` configuration: ```yaml -apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 kind: KubeadmConfigTemplate metadata: name: my-kubeadmconfig-template @@ -53,6 +55,7 @@ spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: - node-ip: "[[[ .NodeIP ]]]" + - name: node-ip + value: "[[[ .NodeIP ]]]" # important: some fields were omitted... ``` diff --git a/docs/scalewaycluster.md b/docs/scalewaycluster.md index ade7d81..19306d9 100644 --- a/docs/scalewaycluster.md +++ b/docs/scalewaycluster.md @@ -13,7 +13,7 @@ important features on a `ScalewayCluster`. The `ScalewayCluster` with the minimum options looks like this: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -54,7 +54,7 @@ map[fr-par-1:map[controlPlane:true] fr-par-2:map[controlPlane:true] fr-par-3:map Here is an example of `ScalewayCluster` with the failure domains set to `fr-par-1` and `fr-par-2`: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -72,19 +72,20 @@ spec: ### DNS The Scaleway provider can manage a DNS zone and maintain a set of records up-to-date -with the addresses of the control-plane Load Balancers. Note that this can be a -*public* or a *private* Scaleway DNS zone, but **not both**. +with the addresses of the control-plane Load Balancers, using the `network.controlPlaneDNS` field. +If the Load Balancer of the control plane is private, this will configure the DNS zone +of the VPC. Otherwise, this will configure a *public* Scaleway DNS zone. It is not possible +to configure both a public and private zone. -#### Public DNS +The `network.controlPlaneDNS` field is **immutable**, it cannot be updated after creation. -Set the `network.controlPlaneDNS` field to automatically configure DNS records that -will point to the Load Balancer address(es) of your workload cluster. +#### Public DNS In this example, the FQDN `my-cluster.subdomain.your-domain.com` will have `A` record(s) configured to point to the Load Balancer IP address(es). ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -94,6 +95,8 @@ spec: controlPlaneDNS: domain: subdomain.your-domain.com name: my-cluster + controlPlaneLoadBalancer: + private: false # This MUST not be true. # some fields were omitted... ``` @@ -102,24 +105,21 @@ The `domain` must be an existing Scaleway DNS Zone. Please refer to the for more information. You may register an external domain by following [this documentation](https://www.scaleway.com/en/docs/domains-and-dns/how-to/add-external-domain/). -The `network.controlPlaneDNS` field is **immutable**, it cannot be updated after creation. - #### Private DNS -The `network.controlPlanePrivateDNS` field is only available when `network.controlPlaneLoadBalancer.private` -is set to `true`. Here is an example of configuration: +When `network.controlPlaneLoadBalancer.private` is true, the DNS zone of the VPC +is configured instead. In this situation, you should omit the `domain` field. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster namespace: default spec: network: - controlPlanePrivateDNS: + controlPlaneDNS: name: my-cluster - # ... controlPlaneLoadBalancer: private: true # This MUST be set to true privateNetwork: @@ -158,7 +158,7 @@ The kube-apiserver Load Balancer's frontend port can be set at the cluster creat Here is an example of a Load Balancer frontend port configuration: ```yaml -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: my-cluster @@ -180,7 +180,7 @@ The main Load Balancer is always created by default, it is not possible to disab Here is an example of main Load Balancer configuration: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -219,7 +219,7 @@ or `network.controlPlanePrivateDNS` field. Here is an example that configures two extra Load Balancers in `nl-ams-2` and `nl-ams-3`. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -254,7 +254,7 @@ in CIDR format. By default, when the `allowedRanges` is unset or set to an empty all network ranges are allowed. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -280,7 +280,7 @@ To automatically attach the resources of the cluster to a Private Network, simpl set the `network.privateNetwork.enabled` field to true: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster @@ -309,7 +309,7 @@ at least one Public Gateway that advertises its default route. You can configure manually or let the provider configure that for you: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: my-cluster diff --git a/docs/scalewaymachine.md b/docs/scalewaymachine.md index 6728dba..257ac22 100644 --- a/docs/scalewaymachine.md +++ b/docs/scalewaymachine.md @@ -13,7 +13,7 @@ Scaleway availability zone that is based on the associated `Machine`'s `failureD You will usually never create a `ScalewayMachine` directly, `ScalewayMachineTemplate` should be used instead: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachineTemplate metadata: name: my-machine-template @@ -54,7 +54,7 @@ The `image` field must contain one of the following: - An image ID: ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -68,7 +68,7 @@ The `image` field must contain one of the following: - An image name: ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -89,7 +89,7 @@ The `image` field must contain one of the following: - An image [Marketplace label](https://www.scaleway.com/en/developers/api/marketplace/): ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -107,7 +107,7 @@ To build your own image, you can use the [Kubernetes image-builder project](http During machine creation, a root volume will be provisioned based on the chosen image. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -144,7 +144,7 @@ The `publicNetwork` field defines if an IPv4 and/or IPv6 should be created and a during the Instance server creation. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -187,7 +187,7 @@ The `placementGroup` field must contain one of the following: - A placement group ID: ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -201,7 +201,7 @@ The `placementGroup` field must contain one of the following: - A placement group name: ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -233,7 +233,7 @@ The `securityGroup` field must contain one of the following: - A security group ID: ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine @@ -247,7 +247,7 @@ The `securityGroup` field must contain one of the following: - A security group name: ```yaml - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachine metadata: name: my-machine diff --git a/docs/scalewaymanagedcluster.md b/docs/scalewaymanagedcluster.md index d6220c3..af243a0 100644 --- a/docs/scalewaymanagedcluster.md +++ b/docs/scalewaymanagedcluster.md @@ -12,7 +12,7 @@ important features on a `ScalewayManagedCluster`. The `ScalewayManagedCluster` with the minimum options looks like this: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedCluster metadata: name: my-cluster @@ -42,7 +42,7 @@ It is possible to re-use an existing Private Network or configure the VPC where Private Network will be created. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedCluster metadata: name: my-cluster @@ -70,7 +70,7 @@ at least one Public Gateway that advertises its default route. You can configure manually or let the provider configure that for you: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedCluster metadata: name: my-cluster diff --git a/docs/scalewaymanagedcontrolplane.md b/docs/scalewaymanagedcontrolplane.md index 9f07d41..c5fae3d 100644 --- a/docs/scalewaymanagedcontrolplane.md +++ b/docs/scalewaymanagedcontrolplane.md @@ -10,7 +10,7 @@ This document describes the various configuration options you can set to configu The `ScalewayManagedControlPlane` with the minimum options looks like this: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -35,7 +35,7 @@ downgrade the version of a cluster. You can configure additional tags that will be set on the created Scaleway Managed Kubernetes cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -56,7 +56,7 @@ spec: You can configure the IPs allowed to access the public endpoint of the cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -80,7 +80,7 @@ IP range `0.0.0.0/0` is set on the cluster. You can configure the autoscaler of the cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -102,7 +102,7 @@ spec: You can set the auto upgrade configuration of the cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -121,7 +121,7 @@ spec: You can enable Kubernetes feature gates on the cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -140,7 +140,7 @@ You can use the Scaleway CLI to list the available feature gates: `$ scw k8s ver You can enable Kubernetes admission plugins on the cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -159,7 +159,7 @@ You can use the Scaleway CLI to list the available admission plugins: `$ scw k8s You can add additional API Server Cert SANs: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -175,7 +175,7 @@ spec: You can set the OIDC configuration of the cluster: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane @@ -200,7 +200,7 @@ You can enable the deletion of additional resources (e.g. Load Balancers, Persis when the cluster is deleted: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: my-cluster-control-plane diff --git a/docs/scalewaymanagedmachinepool.md b/docs/scalewaymanagedmachinepool.md index 9201525..728fff6 100644 --- a/docs/scalewaymanagedmachinepool.md +++ b/docs/scalewaymanagedmachinepool.md @@ -9,7 +9,7 @@ This document describes the various configuration options you can set to configu The `ScalewayManagedMachinePool` with the minimum options looks like this: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -24,7 +24,7 @@ spec: You can configure additional tags that will be set on the created pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -45,7 +45,7 @@ spec: You can enable autohealing in the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -60,7 +60,7 @@ spec: You can set specify a Security Group and Placement Group ID during the creation of the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -76,7 +76,7 @@ spec: You can enable autoscaling on the pool and set the min/max size of the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -94,7 +94,7 @@ spec: You can set the upgrade policy of the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -111,7 +111,7 @@ spec: You can set Kubelet args on the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -120,7 +120,7 @@ spec: # some fields were omitted... kubeletArgs: containerLogMaxFiles: "10" - registryPullQPS: "10 + registryPullQPS: "10" ``` You can use the Scaleway CLI to list the available kubelet args: `$ scw k8s version list -o json | jq`. @@ -130,7 +130,7 @@ You can use the Scaleway CLI to list the available kubelet args: `$ scw k8s vers You can configure the root volume of the nodes of the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool @@ -146,7 +146,7 @@ spec: You can disable adding a public IP on the nodes of the pool: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: my-cluster-managed-machine-pool diff --git a/go.mod b/go.mod index 91ece86..5360c65 100644 --- a/go.mod +++ b/go.mod @@ -3,43 +3,43 @@ module github.com/scaleway/cluster-api-provider-scaleway go 1.24.0 require ( - github.com/Masterminds/semver/v3 v3.3.0 - github.com/onsi/ginkgo/v2 v2.23.4 - github.com/onsi/gomega v1.37.0 - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.34.0.20250808131040-125b97d90073 - go.uber.org/mock v0.5.2 - golang.org/x/crypto v0.40.0 - k8s.io/api v0.32.6 - k8s.io/apimachinery v0.32.6 - k8s.io/client-go v0.32.6 + github.com/Masterminds/semver/v3 v3.4.0 + github.com/onsi/ginkgo/v2 v2.27.2 + github.com/onsi/gomega v1.38.2 + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 + github.com/spf13/pflag v1.0.7 + go.uber.org/mock v0.6.0 + golang.org/x/crypto v0.44.0 + k8s.io/api v0.33.3 + k8s.io/apiextensions-apiserver v0.33.3 + k8s.io/apimachinery v0.33.3 + k8s.io/client-go v0.33.3 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 - sigs.k8s.io/cluster-api v1.10.2 - sigs.k8s.io/cluster-api/test v1.10.2 - sigs.k8s.io/controller-runtime v0.20.4 + sigs.k8s.io/cluster-api v1.11.3 + sigs.k8s.io/cluster-api/test v1.11.3 + sigs.k8s.io/controller-runtime v0.21.0 ) require ( al.essio.dev/pkg/shellescape v1.5.1 // indirect cel.dev/expr v0.19.1 // indirect - dario.cat/mergo v1.0.1 // indirect github.com/BurntSushi/toml v1.4.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v28.0.2+incompatible // indirect + github.com/docker/docker v28.3.3+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect @@ -49,7 +49,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -59,19 +59,15 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gobuffalo/flect v1.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.22.0 // indirect + github.com/google/cel-go v0.23.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v53 v53.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect - github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect - github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -79,9 +75,8 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -97,56 +92,54 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.2 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect - github.com/shopspring/decimal v1.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.20.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/valyala/fastjson v1.6.4 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.42.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.35.0 // indirect + golang.org/x/tools v0.38.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect - google.golang.org/grpc v1.68.1 // indirect - google.golang.org/protobuf v1.36.5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.71.3 // indirect + google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.32.6 // indirect - k8s.io/apiserver v0.32.6 // indirect - k8s.io/cluster-bootstrap v0.32.3 // indirect - k8s.io/component-base v0.32.6 // indirect + k8s.io/apiserver v0.33.3 // indirect + k8s.io/cluster-bootstrap v0.33.3 // indirect + k8s.io/component-base v0.33.3 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/kind v0.27.0 // indirect + sigs.k8s.io/kind v0.30.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index e2178f8..3769d0a 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= @@ -24,8 +24,6 @@ github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -38,17 +36,16 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.26 h1:xiiEkVB1Dwolb24pkeDUDBfygV9/XsOSq79yFCrhptY= -github.com/coredns/corefile-migration v1.0.26/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= -github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= -github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= -github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coredns/corefile-migration v1.0.29 h1:g4cPYMXXDDs9uLE2gFYrJaPBuUAR07eEMGyh9JBE13w= +github.com/coredns/corefile-migration v1.0.29/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -59,8 +56,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8= -github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= @@ -83,9 +80,15 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -104,14 +107,16 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= -github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -128,14 +133,10 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI= -github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= @@ -144,6 +145,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -161,6 +164,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -169,12 +174,18 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -188,10 +199,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -202,11 +213,8 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -223,8 +231,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.34.0.20250808131040-125b97d90073 h1:4muj/o8l5ONVGzmzaGgQtniCG9Zvnn3Teek28wcOMpg= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.34.0.20250808131040-125b97d90073/go.mod h1:2Cfo14o/ZO3hZg9GjbyD/BHKbyri3K5BiEHq4fBcUHY= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 h1:8xfn1RzeI9yoCUuEwDy08F+No6PcKZGEDOQ6hrRyLts= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35/go.mod h1:47B1d/YXmSAxlJxUJxClzHR6b3T4M1WyCvwENPQNBWc= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -238,15 +246,18 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= -github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -256,74 +267,78 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= -github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/etcd/api/v3 v3.5.20 h1:aKfz3nPZECWoZJXMSH9y6h2adXjtOHaHTGEVCuCmaz0= -go.etcd.io/etcd/api/v3 v3.5.20/go.mod h1:QqKGViq4KTgOG43dr/uH0vmGWIaoJY3ggFi6ZH0TH/U= -go.etcd.io/etcd/client/pkg/v3 v3.5.20 h1:sZIAtra+xCo56gdf6BR62to/hiie5Bwl7hQIqMzVTEM= -go.etcd.io/etcd/client/pkg/v3 v3.5.20/go.mod h1:qaOi1k4ZA9lVLejXNvyPABrVEe7VymMF2433yyRQ7O0= -go.etcd.io/etcd/client/v3 v3.5.20 h1:jMT2MwQEhyvhQg49Cec+1ZHJzfUf6ZgcmV0GjPv0tIQ= -go.etcd.io/etcd/client/v3 v3.5.20/go.mod h1:J5lbzYRMUR20YolS5UjlqqMcu3/wdEvG5VNBhzyo3m0= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -334,37 +349,37 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.71.3 h1:iEhneYTxOruJyZAxdAv8Y0iRZvsc5M6KoW7UA0/7jn0= +google.golang.org/grpc v1.71.3/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -379,20 +394,20 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -k8s.io/api v0.32.6 h1:UiBAMRzTP24Tz9UT1uhhmAv1auGTT9PT/npywSk9JrU= -k8s.io/api v0.32.6/go.mod h1:+iFCyQN34v2rsL53iQEN9lYE03mFdgPvgSXvATIDteg= -k8s.io/apiextensions-apiserver v0.32.6 h1:B9zv1tpW+090Prav3GP53A4W2Bv908AAouZYJWp0fy8= -k8s.io/apiextensions-apiserver v0.32.6/go.mod h1:3lAgylV3582qpXg8NWW4NOLdzxLC8mTcfPqqjAzOSTs= -k8s.io/apimachinery v0.32.6 h1:odtEUjg7OT3132sBFsFn4Arj4Gd+BplYekmLQP8L3ak= -k8s.io/apimachinery v0.32.6/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.6 h1:SKt+2e4klvHes4nw3moBI3mCPuh3RFp0XtYzsjZOOjk= -k8s.io/apiserver v0.32.6/go.mod h1:CleW9S9cdw3EAevI/RCFc7RtHTEgbcTFhZV28IEdRtU= -k8s.io/client-go v0.32.6 h1:Q+O+Sd9LKKFnsGZNVX2q1RDILYRpQZX+ea2RoIgjKlM= -k8s.io/client-go v0.32.6/go.mod h1:yqL9XJ2cTXy3WdJwdeyob3O6xiLwWrh9DP7SeszniW0= -k8s.io/cluster-bootstrap v0.32.3 h1:AqIpsUhB6MUeaAsl1WvaUw54AHRd2hfZrESlKChtd8s= -k8s.io/cluster-bootstrap v0.32.3/go.mod h1:CHbBwgOb6liDV6JFUTkx5t85T2xidy0sChBDoyYw344= -k8s.io/component-base v0.32.6 h1:LwKaAlUcTyRouaqUdpQ+JLvtwyZlrWrNey1axNbE0ac= -k8s.io/component-base v0.32.6/go.mod h1:fFJq5U4s+BAjmTV5gnT9CIfRbVujyITuO93ambmplcE= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs= +k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.3 h1:Wv0hGc+QFdMJB4ZSiHrCgN3zL3QRatu56+rpccKC3J4= +k8s.io/apiserver v0.33.3/go.mod h1:05632ifFEe6TxwjdAIrwINHWE2hLwyADFk5mBsQa15E= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/cluster-bootstrap v0.33.3 h1:u2NTxJ5CFSBFXaDxLQoOWMly8eni31psVso+caq6uwI= +k8s.io/cluster-bootstrap v0.33.3/go.mod h1:p970f8u8jf273zyQ5raD8WUu2XyAl0SAWOY82o7i/ds= +k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= +k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= @@ -401,20 +416,21 @@ k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6J k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/cluster-api v1.10.2 h1:xfvtNu4Fy/41grL0ryH5xSKQjpJEWdO8HiV2lPCCozQ= -sigs.k8s.io/cluster-api v1.10.2/go.mod h1:/b9Un5Imprib6S7ZOcJitC2ep/5wN72b0pXpMQFfbTw= -sigs.k8s.io/cluster-api/test v1.10.2 h1:y6vSdS9FSAi/DNoFE2fZo2fed0m1cgW+ueBazk1g4i8= -sigs.k8s.io/cluster-api/test v1.10.2/go.mod h1:KLeRjNtQS8k5jIPvQF0QxOti/ATu5euwSusb6iFBga8= -sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= -sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= +sigs.k8s.io/cluster-api v1.11.3 h1:apxfugbP1X8AG7THCM74CTarCOW4H2oOc6hlbm1hY80= +sigs.k8s.io/cluster-api v1.11.3/go.mod h1:CA471SACi81M8DzRKTlWpHV33G0cfWEj7sC4fALFVok= +sigs.k8s.io/cluster-api/test v1.11.3 h1:My8N5vHyWL1XAdwfP69bIajox8IJd7XhaSD8DRu8AHs= +sigs.k8s.io/cluster-api/test v1.11.3/go.mod h1:COviHWIKTcip0VADeIh8Rm5bjqzyZ1LuzKBW1EqjJRc= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/kind v0.27.0 h1:PQ3f0iAWNIj66LYkZ1ivhEg/+Zb6UPMbO+qVei/INZA= -sigs.k8s.io/kind v0.27.0/go.mod h1:RZVFmy6qcwlSWwp6xeIUv7kXCPF3i8MXsEXxW/J+gJY= +sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= +sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/internal/controller/helpers.go b/internal/controller/helpers.go index f9f9ec3..a7b660b 100644 --- a/internal/controller/helpers.go +++ b/internal/controller/helpers.go @@ -5,7 +5,6 @@ import ( "fmt" "slices" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/cluster-api/util" @@ -13,6 +12,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) var ( @@ -100,3 +101,14 @@ func releaseScalewaySecret(ctx context.Context, c client.Client, owner client.Ob return secretHelper.Patch(ctx, secret) } + +func migrateFinalizer(o client.Object, old, finalizer string) bool { + // Attempt to remove old finalizer. + if !controllerutil.RemoveFinalizer(o, old) { + return false + } + + // Add the up-to-date finalizer. + controllerutil.AddFinalizer(o, finalizer) + return true +} diff --git a/internal/controller/scalewaycluster_controller.go b/internal/controller/scalewaycluster_controller.go index 5dbe17e..b5a591d 100644 --- a/internal/controller/scalewaycluster_controller.go +++ b/internal/controller/scalewaycluster_controller.go @@ -6,7 +6,8 @@ import ( "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/predicates" @@ -17,7 +18,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" ) @@ -116,18 +117,16 @@ func (r *ScalewayClusterReconciler) reconcileDelete(ctx context.Context, cluster if err := r.createScalewayClusterService(clusterScope).Delete(ctx); err != nil { // Handle transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayCluster, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayCluster, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to delete cluster services: %w", err) } // Cluster is deleted so remove the finalizer. - controllerutil.RemoveFinalizer(scalewayCluster, infrav1.ClusterFinalizer) + controllerutil.RemoveFinalizer(scalewayCluster, infrav1.ScalewayClusterFinalizer) if err := releaseScalewaySecret(ctx, r, scalewayCluster, scalewayCluster.Spec.ScalewaySecretName); err != nil { return ctrl.Result{}, err @@ -143,7 +142,7 @@ func (r *ScalewayClusterReconciler) reconcileNormal(ctx context.Context, cluster scalewayCluster := clusterScope.ScalewayCluster // Register our finalizer immediately to avoid orphaning Scaleway resources on delete - if controllerutil.AddFinalizer(scalewayCluster, infrav1.ClusterFinalizer) { + if controllerutil.AddFinalizer(scalewayCluster, infrav1.ScalewayClusterFinalizer) { if err := clusterScope.PatchObject(ctx); err != nil { return ctrl.Result{}, err } @@ -152,14 +151,9 @@ func (r *ScalewayClusterReconciler) reconcileNormal(ctx context.Context, cluster if err := r.createScalewayClusterService(clusterScope).Reconcile(ctx); err != nil { // Handle terminal & transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTerminal() { - log.Error(err, "Failed to reconcile ScalewayCluster") - return ctrl.Result{}, nil - } else if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayCluster, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayCluster, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to reconcile cluster services: %w", err) @@ -179,7 +173,7 @@ func (r *ScalewayClusterReconciler) reconcileNormal(ctx context.Context, cluster } // No errors, so mark us ready so the Cluster API Cluster Controller can pull it - scalewayCluster.Status.Ready = true + scalewayCluster.Status.Initialization.Provisioned = ptr.To(true) return ctrl.Result{}, nil } diff --git a/internal/controller/scalewaycluster_controller_test.go b/internal/controller/scalewaycluster_controller_test.go index a47e8a0..79711b7 100644 --- a/internal/controller/scalewaycluster_controller_test.go +++ b/internal/controller/scalewaycluster_controller_test.go @@ -8,19 +8,22 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/scaleway-sdk-go/scw" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" ) var _ = Describe("ScalewayCluster Controller", func() { @@ -43,8 +46,9 @@ var _ = Describe("ScalewayCluster Controller", func() { Namespace: "default", }, Spec: infrav1.ScalewayClusterSpec{ - ProjectID: "11111111-1111-1111-1111-111111111111", - Region: string(scw.RegionFrPar), + ProjectID: "11111111-1111-1111-1111-111111111111", + Region: infrav1.ScalewayRegion(scw.RegionFrPar), + ScalewaySecretName: "my-secret", }, } Expect(k8sClient.Create(ctx, resource)).To(Succeed()) @@ -96,7 +100,7 @@ var _ = Describe("ScalewayCluster", func() { }, Spec: infrav1.ScalewayClusterSpec{ ProjectID: "11111111-1111-1111-1111-111111111111", - Region: string(scw.RegionFrPar), + Region: infrav1.ScalewayRegion(scw.RegionFrPar), ScalewaySecretName: "my-secret", }, } @@ -149,9 +153,9 @@ var _ = Describe("ScalewayCluster", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - resource.Spec.Network = &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + resource.Spec.Network = infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, } Expect(k8sClient.Update(ctx, resource)).NotTo(Succeed()) @@ -178,11 +182,12 @@ var _ = Describe("ScalewayCluster", func() { }, Spec: infrav1.ScalewayClusterSpec{ ProjectID: "11111111-1111-1111-1111-111111111111", - Region: string(scw.RegionFrPar), + Region: infrav1.ScalewayRegion(scw.RegionFrPar), ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: "42.42.42.42", Port: 6443, }, + ScalewaySecretName: "my-secret", }, } Expect(k8sClient.Create(ctx, resource)).To(Succeed()) @@ -271,8 +276,8 @@ func TestScalewayClusterReconciler_Reconcile(t *testing.T) { return &scalewayClusterService{ scope: clusterScope, Reconcile: func(ctx context.Context) error { - clusterScope.ScalewayCluster.Status.Network = &infrav1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr("42.42.42.42"), + clusterScope.ScalewayCluster.Status.Network = infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4("42.42.42.42"), } return nil }, @@ -325,10 +330,11 @@ func TestScalewayClusterReconciler_Reconcile(t *testing.T) { // ScalewayCluster checks sc := &infrav1.ScalewayCluster{} g.Expect(c.Get(context.TODO(), scalewayClusterNamespacedName, sc)).To(Succeed()) - g.Expect(sc.Status.Ready).To(BeTrue()) + g.Expect(sc.Status.Initialization.Provisioned).NotTo(BeNil()) + g.Expect(*sc.Status.Initialization.Provisioned).To(BeTrue()) g.Expect(sc.Spec.ControlPlaneEndpoint.Host).NotTo(BeEmpty()) g.Expect(sc.Spec.ControlPlaneEndpoint.Port).NotTo(BeZero()) - g.Expect(sc.Finalizers).To(ContainElement(infrav1.ClusterFinalizer)) + g.Expect(sc.Finalizers).To(ContainElement(infrav1.ScalewayClusterFinalizer)) // Secret checks s := &corev1.Secret{} @@ -365,7 +371,7 @@ func TestScalewayClusterReconciler_Reconcile(t *testing.T) { APIVersion: clusterv1.GroupVersion.String(), }, }, - Finalizers: []string{infrav1.ClusterFinalizer}, + Finalizers: []string{infrav1.ScalewayClusterFinalizer}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: infrav1.ScalewayClusterSpec{ diff --git a/internal/controller/scalewaycluster_reconciler.go b/internal/controller/scalewaycluster_reconciler.go index 2c3dc35..7e3dc5d 100644 --- a/internal/controller/scalewaycluster_reconciler.go +++ b/internal/controller/scalewaycluster_reconciler.go @@ -5,13 +5,14 @@ import ( "fmt" "slices" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/domain" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/vpc" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/vpcgw" - "github.com/scaleway/scaleway-sdk-go/scw" ) type scalewayClusterService struct { @@ -43,7 +44,7 @@ func newScalewayClusterService(s *scope.Cluster) *scalewayClusterService { // Reconcile reconciles all the services in a predetermined order. func (s *scalewayClusterService) reconcile(ctx context.Context) error { if err := s.setFailureDomainsForLocation(); err != nil { - return scaleway.WithTerminalError(fmt.Errorf("failed to set failure domains in status: %w", err)) + return fmt.Errorf("failed to set failure domains in status: %w", err) } for _, service := range s.services { @@ -76,7 +77,7 @@ func (s *scalewayClusterService) setFailureDomainsForLocation() error { if len(s.scope.ScalewayCluster.Spec.FailureDomains) > 0 { for _, failureDomain := range s.scope.ScalewayCluster.Spec.FailureDomains { - requestedZone, err := scw.ParseZone(failureDomain) + requestedZone, err := scw.ParseZone(string(failureDomain)) if err != nil { return fmt.Errorf("failed to parse failureDomain %s as Scaleway zone: %w", failureDomain, err) } diff --git a/internal/controller/scalewaymachine_controller.go b/internal/controller/scalewaymachine_controller.go index f5c1d99..f916159 100644 --- a/internal/controller/scalewaymachine_controller.go +++ b/internal/controller/scalewaymachine_controller.go @@ -7,7 +7,8 @@ import ( "time" apierrors "k8s.io/apimachinery/pkg/api/errors" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" ctrl "sigs.k8s.io/controller-runtime" @@ -19,7 +20,8 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + infrav1alpha1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" //nolint:staticcheck + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" ) @@ -126,6 +128,13 @@ func (r *ScalewayMachineReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } + // Replace legacy finalizer with the up-to-date one. + if migrateFinalizer(scalewayMachine, infrav1alpha1.MachineFinalizer, infrav1.ScalewayMachineFinalizer) { + if err := machineScope.PatchObject(ctx); err != nil { + return ctrl.Result{}, err + } + } + // Handle deleted machines if !scalewayMachine.ObjectMeta.DeletionTimestamp.IsZero() { return r.reconcileDelete(ctx, machineScope) @@ -143,14 +152,14 @@ func (r *ScalewayMachineReconciler) reconcileNormal(ctx context.Context, machine scalewayMachine := machineScope.ScalewayMachine // Register our finalizer immediately to avoid orphaning Scaleway resources on delete - if controllerutil.AddFinalizer(scalewayMachine, infrav1.MachineFinalizer) { + if controllerutil.AddFinalizer(scalewayMachine, infrav1.ScalewayMachineFinalizer) { if err := machineScope.PatchObject(ctx); err != nil { return ctrl.Result{}, err } } // Make sure the Cluster Infrastructure is ready. - if !clusterScope.Cluster.Status.InfrastructureReady { + if !ptr.Deref(clusterScope.Cluster.Status.Initialization.InfrastructureProvisioned, false) { log.Info("Cluster infrastructure is not ready yet") return ctrl.Result{RequeueAfter: time.Second}, nil } @@ -164,20 +173,15 @@ func (r *ScalewayMachineReconciler) reconcileNormal(ctx context.Context, machine if err := r.createScalewayMachineService(machineScope).Reconcile(ctx); err != nil { // Handle terminal & transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTerminal() { - log.Error(err, "Failed to reconcile ScalewayMachine") - return ctrl.Result{}, nil - } else if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayMachine, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayMachine, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to reconcile machine services: %w", err) } - scalewayMachine.Status.Ready = true + scalewayMachine.Status.Initialization.Provisioned = ptr.To(true) return ctrl.Result{}, nil } @@ -190,18 +194,16 @@ func (r *ScalewayMachineReconciler) reconcileDelete(ctx context.Context, machine if err := r.createScalewayMachineService(machineScope).Delete(ctx); err != nil { // Handle transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayMachine, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayMachine, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to delete machine services: %w", err) } // Machine is deleted so remove the finalizer. - controllerutil.RemoveFinalizer(machineScope.ScalewayMachine, infrav1.MachineFinalizer) + controllerutil.RemoveFinalizer(machineScope.ScalewayMachine, infrav1.ScalewayMachineFinalizer) return ctrl.Result{}, nil } @@ -215,14 +217,14 @@ func (r *ScalewayMachineReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &clusterv1.Machine{}, handler.EnqueueRequestsFromMapFunc(util.MachineToInfrastructureMapFunc(infrav1.GroupVersion.WithKind("ScalewayMachine"))), - builder.WithPredicates(MachineUpdateNodeRefAvailable()), + builder.WithPredicates(machineUpdateNodeRefAvailable()), ). Named("scalewaymachine"). Complete(r) } -// MachineUpdateNodeRefAvailable is a predicate that checks if the Machine's NodeRef has become available. -func MachineUpdateNodeRefAvailable() predicate.Funcs { +// machineUpdateNodeRefAvailable is a predicate that checks if the Machine's NodeRef has become available. +func machineUpdateNodeRefAvailable() predicate.Funcs { return predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { oldCluster, ok := e.ObjectOld.(*clusterv1.Machine) @@ -235,7 +237,7 @@ func MachineUpdateNodeRefAvailable() predicate.Funcs { return false } - return oldCluster.Status.NodeRef == nil && newCluster.Status.NodeRef != nil + return !oldCluster.Status.NodeRef.IsDefined() && newCluster.Status.NodeRef.IsDefined() }, CreateFunc: func(event.CreateEvent) bool { return false }, DeleteFunc: func(event.DeleteEvent) bool { return false }, diff --git a/internal/controller/scalewaymachine_controller_test.go b/internal/controller/scalewaymachine_controller_test.go index faffc1f..3428157 100644 --- a/internal/controller/scalewaymachine_controller_test.go +++ b/internal/controller/scalewaymachine_controller_test.go @@ -8,19 +8,22 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/scaleway-sdk-go/scw" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" ) var _ = Describe("ScalewayMachine Controller", func() { @@ -45,8 +48,9 @@ var _ = Describe("ScalewayMachine Controller", func() { Namespace: "default", }, Spec: infrav1.ScalewayMachineSpec{ - Image: infrav1.ImageSpec{ - Label: scw.StringPtr("ubuntu_focal"), + CommercialType: "PRO2-S", + Image: infrav1.Image{ + Label: "ubuntu_focal", }, }, } @@ -148,12 +152,14 @@ func TestScalewayMachineReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - InfrastructureRef: &corev1.ObjectReference{ + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ Name: scalewayClusterNamespacedName.Name, }, }, Status: clusterv1.ClusterStatus{ - InfrastructureReady: true, + Initialization: clusterv1.ClusterInitializationStatus{ + InfrastructureProvisioned: ptr.To(true), + }, }, }, &corev1.Secret{ @@ -189,7 +195,7 @@ func TestScalewayMachineReconciler_Reconcile(t *testing.T) { }, Spec: clusterv1.MachineSpec{ Bootstrap: clusterv1.Bootstrap{ - DataSecretName: scw.StringPtr("bootstrap"), + DataSecretName: ptr.To("bootstrap"), }, }, }, @@ -198,8 +204,9 @@ func TestScalewayMachineReconciler_Reconcile(t *testing.T) { // ScalewayMachine checks sc := &infrav1.ScalewayMachine{} g.Expect(c.Get(context.TODO(), scalewayMachineNamespacedName, sc)).To(Succeed()) - g.Expect(sc.Status.Ready).To(BeTrue()) - g.Expect(sc.Finalizers).To(ContainElement(infrav1.MachineFinalizer)) + g.Expect(sc.Status.Initialization.Provisioned).NotTo(BeNil()) + g.Expect(*sc.Status.Initialization.Provisioned).To(BeTrue()) + g.Expect(sc.Finalizers).To(ContainElement(infrav1.ScalewayMachineFinalizer)) }, }, { @@ -244,12 +251,14 @@ func TestScalewayMachineReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - InfrastructureRef: &corev1.ObjectReference{ + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ Name: scalewayClusterNamespacedName.Name, }, }, Status: clusterv1.ClusterStatus{ - InfrastructureReady: true, + Initialization: clusterv1.ClusterInitializationStatus{ + InfrastructureProvisioned: ptr.To(true), + }, }, }, &corev1.Secret{ @@ -273,7 +282,7 @@ func TestScalewayMachineReconciler_Reconcile(t *testing.T) { APIVersion: clusterv1.GroupVersion.String(), }, }, - Finalizers: []string{infrav1.MachineFinalizer}, + Finalizers: []string{infrav1.ScalewayMachineFinalizer}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, }, @@ -287,7 +296,7 @@ func TestScalewayMachineReconciler_Reconcile(t *testing.T) { }, Spec: clusterv1.MachineSpec{ Bootstrap: clusterv1.Bootstrap{ - DataSecretName: scw.StringPtr("bootstrap"), + DataSecretName: ptr.To("bootstrap"), }, }, }, diff --git a/internal/controller/scalewaymanagedcluster_controller.go b/internal/controller/scalewaymanagedcluster_controller.go index cfa57ae..40324d7 100644 --- a/internal/controller/scalewaymanagedcluster_controller.go +++ b/internal/controller/scalewaymanagedcluster_controller.go @@ -7,7 +7,8 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/predicates" @@ -18,7 +19,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + infrav1alpha1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" //nolint:staticcheck + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" ) @@ -77,7 +79,7 @@ func (r *ScalewayManagedClusterReconciler) Reconcile(ctx context.Context, req ct log = log.WithValues("cluster", cluster.Name) - if cluster.Spec.ControlPlaneRef == nil { + if !cluster.Spec.ControlPlaneRef.IsDefined() { return ctrl.Result{}, errors.New("missing controlPlaneRef in cluster spec") } controlPlane := &infrav1.ScalewayManagedControlPlane{} @@ -115,6 +117,13 @@ func (r *ScalewayManagedClusterReconciler) Reconcile(ctx context.Context, req ct return ctrl.Result{}, fmt.Errorf("unable to claim ScalewaySecret: %w", err) } + // Replace legacy finalizer with the up-to-date one. + if migrateFinalizer(managedCluster, infrav1alpha1.ManagedClusterFinalizer, infrav1.ScalewayManagedClusterFinalizer) { + if err := managedClusterScope.PatchObject(ctx); err != nil { + return ctrl.Result{}, err + } + } + if !managedCluster.DeletionTimestamp.IsZero() { return r.reconcileDelete(ctx, managedClusterScope) } @@ -126,10 +135,10 @@ func (r *ScalewayManagedClusterReconciler) reconcileNormal(ctx context.Context, log := logf.FromContext(ctx) log.Info("Reconciling ScalewayManagedCluster") - managedCluster := s.ManagedCluster + managedCluster := s.ScalewayManagedCluster // Register our finalizer immediately to avoid orphaning Scaleway resources on delete - if controllerutil.AddFinalizer(managedCluster, infrav1.ManagedClusterFinalizer) { + if controllerutil.AddFinalizer(managedCluster, infrav1.ScalewayManagedClusterFinalizer) { if err := s.PatchObject(ctx); err != nil { return ctrl.Result{}, err } @@ -138,14 +147,9 @@ func (r *ScalewayManagedClusterReconciler) reconcileNormal(ctx context.Context, if err := r.createScalewayManagedClusterService(s).Reconcile(ctx); err != nil { // Handle terminal & transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTerminal() { - log.Error(err, "Failed to reconcile ScalewayManagedCluster") - return ctrl.Result{}, nil - } else if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedCluster, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedCluster, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to reconcile cluster services: %w", err) @@ -153,8 +157,8 @@ func (r *ScalewayManagedClusterReconciler) reconcileNormal(ctx context.Context, // Infrastructure must be ready before control plane. We should also enqueue // requests from control plane to infra cluster to keep control plane endpoint accurate. - s.ManagedCluster.Status.Ready = true - s.ManagedCluster.Spec.ControlPlaneEndpoint = s.ManagedControlPlane.Spec.ControlPlaneEndpoint + s.ScalewayManagedCluster.Status.Initialization.Provisioned = ptr.To(true) + s.ScalewayManagedCluster.Spec.ControlPlaneEndpoint = s.ScalewayManagedControlPlane.Spec.ControlPlaneEndpoint return ctrl.Result{}, nil } @@ -173,28 +177,26 @@ func (r *ScalewayManagedClusterReconciler) reconcileDelete(ctx context.Context, return ctrl.Result{RequeueAfter: DefaultRetryTime}, nil } - if s.ManagedControlPlane != nil { + if s.ScalewayManagedControlPlane != nil { log.Info("ScalewayManagedControlPlane not deleted yet, retry later") return ctrl.Result{RequeueAfter: DefaultRetryTime}, nil } - managedCluster := s.ManagedCluster + managedCluster := s.ScalewayManagedCluster if err := r.createScalewayManagedClusterService(s).Delete(ctx); err != nil { // Handle transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedCluster, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedCluster, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to delete cluster services: %w", err) } // Cluster is deleted so remove the finalizer. - controllerutil.RemoveFinalizer(managedCluster, infrav1.ManagedClusterFinalizer) + controllerutil.RemoveFinalizer(managedCluster, infrav1.ScalewayManagedClusterFinalizer) if err := releaseScalewaySecret(ctx, r, managedCluster, managedCluster.Spec.ScalewaySecretName); err != nil { return ctrl.Result{}, err @@ -224,7 +226,7 @@ func (r *ScalewayManagedClusterReconciler) SetupWithManager(ctx context.Context, } func (r *ScalewayManagedClusterReconciler) dependencyCount(ctx context.Context, clusterScope *scope.ManagedCluster) (int, error) { - clusterName, clusterNamespace := clusterScope.ManagedCluster.Name, clusterScope.ManagedCluster.Namespace + clusterName, clusterNamespace := clusterScope.ScalewayManagedCluster.Name, clusterScope.ScalewayManagedCluster.Namespace listOptions := []client.ListOption{ client.InNamespace(clusterNamespace), @@ -264,7 +266,7 @@ func (r *ScalewayManagedClusterReconciler) managedControlPlaneMapper() handler.M } managedClusterRef := cluster.Spec.InfrastructureRef - if managedClusterRef == nil || managedClusterRef.Kind != "ScalewayManagedCluster" { + if managedClusterRef.Kind != "ScalewayManagedCluster" { return nil } @@ -272,7 +274,7 @@ func (r *ScalewayManagedClusterReconciler) managedControlPlaneMapper() handler.M { NamespacedName: types.NamespacedName{ Name: managedClusterRef.Name, - Namespace: managedClusterRef.Namespace, + Namespace: cluster.Namespace, }, }, } diff --git a/internal/controller/scalewaymanagedcluster_controller_test.go b/internal/controller/scalewaymanagedcluster_controller_test.go index 8fd2dc3..41ad8ab 100644 --- a/internal/controller/scalewaymanagedcluster_controller_test.go +++ b/internal/controller/scalewaymanagedcluster_controller_test.go @@ -8,19 +8,21 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/scaleway-sdk-go/scw" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" ) var _ = Describe("ScalewayManagedCluster Controller", func() { @@ -45,7 +47,7 @@ var _ = Describe("ScalewayManagedCluster Controller", func() { Namespace: "default", }, Spec: infrav1.ScalewayManagedClusterSpec{ - Region: string(scw.RegionFrPar), + Region: infrav1.ScalewayRegion(scw.RegionFrPar), ProjectID: "11111111-1111-1111-1111-111111111111", ScalewaySecretName: "test-secret", }, @@ -99,7 +101,7 @@ var _ = Describe("ScalewayManagedCluster", func() { Namespace: "default", }, Spec: infrav1.ScalewayManagedClusterSpec{ - Region: string(scw.RegionFrPar), + Region: infrav1.ScalewayRegion(scw.RegionFrPar), ProjectID: "11111111-1111-1111-1111-111111111111", ScalewaySecretName: "test-secret", }, @@ -153,9 +155,9 @@ var _ = Describe("ScalewayManagedCluster", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - resource.Spec.Network = &infrav1.ManagedNetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkParams{ - ID: scw.StringPtr("11111111-1111-1111-1111-111111111111"), + resource.Spec.Network = infrav1.ScalewayManagedClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetwork{ + ID: "11111111-1111-1111-1111-111111111111", }, } Expect(k8sClient.Update(ctx, resource)).NotTo(Succeed()) @@ -182,7 +184,7 @@ var _ = Describe("ScalewayManagedCluster", func() { }, Spec: infrav1.ScalewayManagedClusterSpec{ ProjectID: "11111111-1111-1111-1111-111111111111", - Region: string(scw.RegionFrPar), + Region: infrav1.ScalewayRegion(scw.RegionFrPar), ScalewaySecretName: "secret", ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: "42.42.42.42", @@ -324,9 +326,8 @@ func TestScalewayManagedClusterReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - ControlPlaneRef: &corev1.ObjectReference{ - Name: scalewayManagedControlPlaneNamespacedName.Name, - Namespace: scalewayManagedControlPlaneNamespacedName.Namespace, + ControlPlaneRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedControlPlaneNamespacedName.Name, }, }, }, @@ -345,10 +346,11 @@ func TestScalewayManagedClusterReconciler_Reconcile(t *testing.T) { // ScalewayManagedCluster checks sc := &infrav1.ScalewayManagedCluster{} g.Expect(c.Get(context.TODO(), scalewayManagedClusterNamespacedName, sc)).To(Succeed()) - g.Expect(sc.Status.Ready).To(BeTrue()) + g.Expect(sc.Status.Initialization.Provisioned).NotTo(BeNil()) + g.Expect(*sc.Status.Initialization.Provisioned).To(BeTrue()) g.Expect(sc.Spec.ControlPlaneEndpoint.Host).To(Equal(managedEndpoint.Host)) g.Expect(sc.Spec.ControlPlaneEndpoint.Port).To(Equal(managedEndpoint.Port)) - g.Expect(sc.Finalizers).To(ContainElement(infrav1.ManagedClusterFinalizer)) + g.Expect(sc.Finalizers).To(ContainElement(infrav1.ScalewayManagedClusterFinalizer)) // Secret checks s := &corev1.Secret{} @@ -385,7 +387,7 @@ func TestScalewayManagedClusterReconciler_Reconcile(t *testing.T) { APIVersion: clusterv1.GroupVersion.String(), }, }, - Finalizers: []string{infrav1.ManagedClusterFinalizer}, + Finalizers: []string{infrav1.ScalewayManagedClusterFinalizer}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: infrav1.ScalewayManagedClusterSpec{ @@ -400,9 +402,8 @@ func TestScalewayManagedClusterReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - ControlPlaneRef: &corev1.ObjectReference{ - Name: scalewayManagedControlPlaneNamespacedName.Name, - Namespace: scalewayManagedControlPlaneNamespacedName.Namespace, + ControlPlaneRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedControlPlaneNamespacedName.Name, }, }, }, diff --git a/internal/controller/scalewaymanagedcontrolplane_controller.go b/internal/controller/scalewaymanagedcontrolplane_controller.go index 0d5be05..63c7aa3 100644 --- a/internal/controller/scalewaymanagedcontrolplane_controller.go +++ b/internal/controller/scalewaymanagedcontrolplane_controller.go @@ -7,7 +7,8 @@ import ( "time" apierrors "k8s.io/apimachinery/pkg/api/errors" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/predicates" @@ -18,7 +19,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + infrav1alpha1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" //nolint:staticcheck + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" ) @@ -106,6 +108,13 @@ func (r *ScalewayManagedControlPlaneReconciler) Reconcile(ctx context.Context, r } }() + // Replace legacy finalizer with the up-to-date one. + if migrateFinalizer(managedControlPlane, infrav1alpha1.ManagedControlPlaneFinalizer, infrav1.ScalewayManagedControlPlaneFinalizer) { + if err := managedControlPlaneScope.PatchObject(ctx); err != nil { + return ctrl.Result{}, err + } + } + // Handle deleted clusters if !managedControlPlane.DeletionTimestamp.IsZero() { return r.reconcileDelete(ctx, managedControlPlaneScope) @@ -118,16 +127,16 @@ func (r *ScalewayManagedControlPlaneReconciler) reconcileNormal(ctx context.Cont log := logf.FromContext(ctx) log.Info("Reconciling ScalewayManagedControlPlane") - managedControlPlane := s.ManagedControlPlane + managedControlPlane := s.ScalewayManagedControlPlane // Register our finalizer immediately to avoid orphaning Scaleway resources on delete - if controllerutil.AddFinalizer(managedControlPlane, infrav1.ManagedControlPlaneFinalizer) { + if controllerutil.AddFinalizer(managedControlPlane, infrav1.ScalewayManagedControlPlaneFinalizer) { if err := s.PatchObject(ctx); err != nil { return ctrl.Result{}, err } } - if !s.ManagedCluster.Status.Ready { + if !ptr.Deref(s.ScalewayManagedCluster.Status.Initialization.Provisioned, false) { log.Info("ScalewayManagedCluster not ready yet, retry later") return ctrl.Result{RequeueAfter: time.Second}, nil } @@ -135,23 +144,17 @@ func (r *ScalewayManagedControlPlaneReconciler) reconcileNormal(ctx context.Cont if err := r.createScalewayManagedControlPlaneService(s).Reconcile(ctx); err != nil { // Handle terminal & transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTerminal() { - log.Error(err, "Failed to reconcile ScalewayManagedControlPlane") - return ctrl.Result{}, nil - } else if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedControlPlane, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedControlPlane, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to reconcile cluster services: %w", err) } - s.ManagedControlPlane.Status.Initialized = true - s.ManagedControlPlane.Status.Ready = true - s.ManagedControlPlane.Status.ExternalManagedControlPlane = true - s.ManagedControlPlane.Spec.Version = s.FixedVersion() + s.ScalewayManagedControlPlane.Status.Initialization.ControlPlaneInitialized = ptr.To(true) + s.ScalewayManagedControlPlane.Status.ExternalManagedControlPlane = ptr.To(true) + s.ScalewayManagedControlPlane.Spec.Version = s.FixedVersion() return ctrl.Result{}, nil } @@ -161,23 +164,21 @@ func (r *ScalewayManagedControlPlaneReconciler) reconcileDelete(ctx context.Cont log.Info("Reconciling ScalewayManagedControlPlane delete") - managedControlPlane := s.ManagedControlPlane + managedControlPlane := s.ScalewayManagedControlPlane if err := r.createScalewayManagedControlPlaneService(s).Delete(ctx); err != nil { // Handle transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedControlPlane, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedControlPlane, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to delete cluster services: %w", err) } // Cluster is deleted so remove the finalizer. - controllerutil.RemoveFinalizer(managedControlPlane, infrav1.ManagedControlPlaneFinalizer) + controllerutil.RemoveFinalizer(managedControlPlane, infrav1.ScalewayManagedControlPlaneFinalizer) return ctrl.Result{}, nil } @@ -192,7 +193,7 @@ func (r *ScalewayManagedControlPlaneReconciler) SetupWithManager(ctx context.Con Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(util.ClusterToInfrastructureMapFunc(ctx, infrav1.GroupVersion.WithKind("ScalewayManagedControlPlane"), mgr.GetClient(), &infrav1.ScalewayManagedControlPlane{})), - builder.WithPredicates(predicates.ClusterPausedTransitionsOrInfrastructureReady(mgr.GetScheme(), mgr.GetLogger())), + builder.WithPredicates(predicates.ClusterPausedTransitionsOrInfrastructureProvisioned(mgr.GetScheme(), mgr.GetLogger())), ). Complete(r) } diff --git a/internal/controller/scalewaymanagedcontrolplane_controller_test.go b/internal/controller/scalewaymanagedcontrolplane_controller_test.go index c7f43b0..13ff967 100644 --- a/internal/controller/scalewaymanagedcontrolplane_controller_test.go +++ b/internal/controller/scalewaymanagedcontrolplane_controller_test.go @@ -8,19 +8,22 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/scaleway-sdk-go/scw" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" ) var _ = Describe("ScalewayManagedControlPlane Controller", func() { @@ -121,7 +124,7 @@ var _ = Describe("ScalewayManagedControlPlane", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - resource.Spec.CNI = scw.StringPtr("calico") + resource.Spec.CNI = "calico" Expect(k8sClient.Update(ctx, resource)).NotTo(Succeed()) }) }) @@ -252,7 +255,9 @@ func TestScalewayManagedControlPlaneReconciler_Reconcile(t *testing.T) { ProjectID: "11111111-1111-1111-1111-111111111111", }, Status: infrav1.ScalewayManagedClusterStatus{ - Ready: true, + Initialization: infrav1.ScalewayManagedClusterInitializationStatus{ + Provisioned: ptr.To(true), + }, }, }, &infrav1.ScalewayManagedControlPlane{ @@ -282,13 +287,11 @@ func TestScalewayManagedControlPlaneReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - ControlPlaneRef: &corev1.ObjectReference{ - Name: scalewayManagedControlPlaneNamespacedName.Name, - Namespace: scalewayManagedControlPlaneNamespacedName.Namespace, + ControlPlaneRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedControlPlaneNamespacedName.Name, }, - InfrastructureRef: &corev1.ObjectReference{ - Name: scalewayManagedClusterNamespacedName.Name, - Namespace: scalewayManagedClusterNamespacedName.Namespace, + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedClusterNamespacedName.Name, }, }, }, @@ -307,10 +310,10 @@ func TestScalewayManagedControlPlaneReconciler_Reconcile(t *testing.T) { // ScalewayManagedControlPlane checks smcp := &infrav1.ScalewayManagedControlPlane{} g.Expect(c.Get(context.TODO(), scalewayManagedControlPlaneNamespacedName, smcp)).To(Succeed()) - g.Expect(smcp.Status.Ready).To(BeTrue()) - g.Expect(smcp.Status.Initialized).To(BeTrue()) - g.Expect(smcp.Status.ExternalManagedControlPlane).To(BeTrue()) - g.Expect(smcp.Finalizers).To(ContainElement(infrav1.ManagedControlPlaneFinalizer)) + g.Expect(smcp.Status.Initialization.ControlPlaneInitialized).NotTo(BeNil()) + g.Expect(*smcp.Status.Initialization.ControlPlaneInitialized).To(BeTrue()) + g.Expect(*smcp.Status.ExternalManagedControlPlane).To(BeTrue()) + g.Expect(smcp.Finalizers).To(ContainElement(infrav1.ScalewayManagedControlPlaneFinalizer)) }, }, { @@ -348,7 +351,9 @@ func TestScalewayManagedControlPlaneReconciler_Reconcile(t *testing.T) { ProjectID: "11111111-1111-1111-1111-111111111111", }, Status: infrav1.ScalewayManagedClusterStatus{ - Ready: true, + Initialization: infrav1.ScalewayManagedClusterInitializationStatus{ + Provisioned: ptr.To(true), + }, }, }, &infrav1.ScalewayManagedControlPlane{ @@ -362,7 +367,7 @@ func TestScalewayManagedControlPlaneReconciler_Reconcile(t *testing.T) { APIVersion: clusterv1.GroupVersion.String(), }, }, - Finalizers: []string{infrav1.ManagedControlPlaneFinalizer}, + Finalizers: []string{infrav1.ScalewayManagedControlPlaneFinalizer}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: infrav1.ScalewayManagedControlPlaneSpec{ @@ -380,13 +385,11 @@ func TestScalewayManagedControlPlaneReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - ControlPlaneRef: &corev1.ObjectReference{ - Name: scalewayManagedControlPlaneNamespacedName.Name, - Namespace: scalewayManagedControlPlaneNamespacedName.Namespace, + ControlPlaneRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedControlPlaneNamespacedName.Name, }, - InfrastructureRef: &corev1.ObjectReference{ - Name: scalewayManagedClusterNamespacedName.Name, - Namespace: scalewayManagedClusterNamespacedName.Namespace, + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedClusterNamespacedName.Name, }, }, }, diff --git a/internal/controller/scalewaymanagedmachinepool_controller.go b/internal/controller/scalewaymanagedmachinepool_controller.go index f2b2ec5..298c4bd 100644 --- a/internal/controller/scalewaymanagedmachinepool_controller.go +++ b/internal/controller/scalewaymanagedmachinepool_controller.go @@ -8,8 +8,8 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/annotations" "sigs.k8s.io/cluster-api/util/predicates" @@ -20,7 +20,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + infrav1alpha1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" //nolint:staticcheck + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" ) @@ -121,6 +122,13 @@ func (r *ScalewayManagedMachinePoolReconciler) Reconcile(ctx context.Context, re } }() + // Replace legacy finalizer with the up-to-date one. + if migrateFinalizer(managedMachinePool, infrav1alpha1.ManagedMachinePoolFinalizer, infrav1.ScalewayManagedMachinePoolFinalizer) { + if err := managedMachinePoolScope.PatchObject(ctx); err != nil { + return ctrl.Result{}, err + } + } + // Handle deleted machine pool if !managedMachinePool.DeletionTimestamp.IsZero() { return r.reconcileDelete(ctx, managedMachinePoolScope) @@ -134,10 +142,10 @@ func (r *ScalewayManagedMachinePoolReconciler) reconcileNormal(ctx context.Conte log := logf.FromContext(ctx) log.Info("Reconciling ScalewayManagedMachinePool") - managedMachinePool := s.ManagedMachinePool + managedMachinePool := s.ScalewayManagedMachinePool // Register our finalizer immediately to avoid orphaning Scaleway resources on delete - if controllerutil.AddFinalizer(managedMachinePool, infrav1.ManagedMachinePoolFinalizer) { + if controllerutil.AddFinalizer(managedMachinePool, infrav1.ScalewayManagedMachinePoolFinalizer) { if err := s.PatchObject(ctx); err != nil { return ctrl.Result{}, err } @@ -146,20 +154,16 @@ func (r *ScalewayManagedMachinePoolReconciler) reconcileNormal(ctx context.Conte if err := r.createScalewayManagedMachinePoolService(s).Reconcile(ctx); err != nil { // Handle terminal & transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTerminal() { - log.Error(err, "Failed to reconcile ScalewayManagedMachinePool") - return ctrl.Result{}, nil - } else if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedMachinePool, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedMachinePool, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to reconcile cluster services: %w", err) } - s.ManagedMachinePool.Status.Ready = true + s.ScalewayManagedMachinePool.Status.Initialization.Provisioned = ptr.To(true) + s.ScalewayManagedMachinePool.Status.Ready = ptr.To(true) return ctrl.Result{}, nil } @@ -169,23 +173,21 @@ func (r *ScalewayManagedMachinePoolReconciler) reconcileDelete(ctx context.Conte log.Info("Reconciling ScalewayManagedMachinePool delete") - managedMachinePool := s.ManagedMachinePool + managedMachinePool := s.ScalewayManagedMachinePool if err := r.createScalewayManagedMachinePoolService(s).Delete(ctx); err != nil { // Handle transient errors var reconcileError *scaleway.ReconcileError - if errors.As(err, &reconcileError) { - if reconcileError.IsTransient() { - log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedMachinePool, retrying: %s", reconcileError.Error())) - return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil - } + if errors.As(err, &reconcileError) && reconcileError.RequeueAfter() != 0 { + log.Info(fmt.Sprintf("Transient failure to reconcile ScalewayManagedMachinePool, retrying: %s", reconcileError.Error())) + return ctrl.Result{RequeueAfter: reconcileError.RequeueAfter()}, nil } return ctrl.Result{}, fmt.Errorf("failed to delete services: %w", err) } // Pool is deleted so remove the finalizer. - controllerutil.RemoveFinalizer(managedMachinePool, infrav1.ManagedMachinePoolFinalizer) + controllerutil.RemoveFinalizer(managedMachinePool, infrav1.ScalewayManagedMachinePoolFinalizer) return ctrl.Result{}, nil } @@ -203,7 +205,7 @@ func (r *ScalewayManagedMachinePoolReconciler) SetupWithManager(ctx context.Cont WithEventFilter(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())). // watch for changes in CAPI MachinePool resources Watches( - &expclusterv1.MachinePool{}, + &clusterv1.MachinePool{}, handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(infrav1.GroupVersion.WithKind("ScalewayManagedMachinePool"))), ). // watch for changes in ScalewayManagedControlPlanes @@ -215,13 +217,13 @@ func (r *ScalewayManagedMachinePoolReconciler) SetupWithManager(ctx context.Cont Watches( &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(scalewayManagedMachinePoolMapper), - builder.WithPredicates(predicates.ClusterPausedTransitionsOrInfrastructureReady(mgr.GetScheme(), mgr.GetLogger())), + builder.WithPredicates(predicates.ClusterPausedTransitionsOrInfrastructureProvisioned(mgr.GetScheme(), mgr.GetLogger())), ). Complete(r) } // getOwnerMachinePool returns the MachinePool object owning the current resource. -func getOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*expclusterv1.MachinePool, error) { +func getOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.MachinePool, error) { for _, ref := range obj.OwnerReferences { if ref.Kind != "MachinePool" { continue @@ -230,7 +232,7 @@ func getOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.Object if err != nil { return nil, fmt.Errorf("failed to parse group version: %w", err) } - if gv.Group == expclusterv1.GroupVersion.Group { + if gv.Group == clusterv1.GroupVersion.Group { return getMachinePoolByName(ctx, c, obj.Namespace, ref.Name) } } @@ -238,8 +240,8 @@ func getOwnerMachinePool(ctx context.Context, c client.Client, obj metav1.Object } // getMachinePoolByName finds and return a Machine object using the specified params. -func getMachinePoolByName(ctx context.Context, c client.Client, namespace, name string) (*expclusterv1.MachinePool, error) { - m := &expclusterv1.MachinePool{} +func getMachinePoolByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.MachinePool, error) { + m := &clusterv1.MachinePool{} key := client.ObjectKey{Name: name, Namespace: namespace} if err := c.Get(ctx, key, m); err != nil { return nil, err @@ -251,7 +253,7 @@ func getMachinePoolByName(ctx context.Context, c client.Client, namespace, name // MachinePool events and returns reconciliation requests for an infrastructure provider object. func machinePoolToInfrastructureMapFunc(gvk schema.GroupVersionKind) handler.MapFunc { return func(_ context.Context, o client.Object) []ctrl.Request { - m, ok := o.(*expclusterv1.MachinePool) + m, ok := o.(*clusterv1.MachinePool) if !ok { return nil } @@ -259,7 +261,7 @@ func machinePoolToInfrastructureMapFunc(gvk schema.GroupVersionKind) handler.Map gk := gvk.GroupKind() ref := m.Spec.Template.Spec.InfrastructureRef // Return early if the GroupKind doesn't match what we expect. - infraGK := ref.GroupVersionKind().GroupKind() + infraGK := ref.GroupKind() if gk != infraGK { return nil } @@ -317,7 +319,7 @@ func managedControlPlaneToManagedMachinePoolMapFunc(ctx context.Context, c clien return nil } - managedPoolForClusterList := expclusterv1.MachinePoolList{} + managedPoolForClusterList := clusterv1.MachinePoolList{} if err := c.List( ctx, &managedPoolForClusterList, client.InNamespace(clusterKey.Namespace), client.MatchingLabels{clusterv1.ClusterNameLabel: clusterKey.Name}, ); err != nil { diff --git a/internal/controller/scalewaymanagedmachinepool_controller_test.go b/internal/controller/scalewaymanagedmachinepool_controller_test.go index 997e6d8..1a02ce9 100644 --- a/internal/controller/scalewaymanagedmachinepool_controller_test.go +++ b/internal/controller/scalewaymanagedmachinepool_controller_test.go @@ -8,20 +8,22 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/scaleway-sdk-go/scw" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" ) var _ = Describe("ScalewayManagedMachinePool Controller", func() { @@ -47,7 +49,7 @@ var _ = Describe("ScalewayManagedMachinePool Controller", func() { }, Spec: infrav1.ScalewayManagedMachinePoolSpec{ NodeType: "DEV1-S", - Zone: string(scw.ZoneFrPar1), + Zone: infrav1.ScalewayZone(scw.ZoneFrPar1), }, } Expect(k8sClient.Create(ctx, resource)).To(Succeed()) @@ -100,7 +102,7 @@ var _ = Describe("ScalewayManagedMachinePool", func() { }, Spec: infrav1.ScalewayManagedMachinePoolSpec{ NodeType: "DEV1-S", - Zone: string(scw.ZoneFrPar1), + Zone: infrav1.ScalewayZone(scw.ZoneFrPar1), }, } Expect(k8sClient.Create(ctx, resource)).To(Succeed()) @@ -132,7 +134,7 @@ var _ = Describe("ScalewayManagedMachinePool", func() { err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) - resource.Spec.Zone = string(scw.ZoneFrPar2) + resource.Spec.Zone = infrav1.ScalewayZone(scw.ZoneFrPar2) Expect(k8sClient.Update(ctx, resource)).NotTo(Succeed()) }) }) @@ -185,7 +187,7 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { }, want: reconcile.Result{}, objects: []client.Object{ - &expclusterv1.MachinePool{ + &clusterv1.MachinePool{ ObjectMeta: metav1.ObjectMeta{ Name: machinePoolNamespacedName.Name, Namespace: machinePoolNamespacedName.Namespace, @@ -193,14 +195,13 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { clusterv1.ClusterNameLabel: clusterNamespacedName.Name, }, }, - Spec: expclusterv1.MachinePoolSpec{ + Spec: clusterv1.MachinePoolSpec{ ClusterName: clusterNamespacedName.Name, Template: clusterv1.MachineTemplateSpec{ Spec: clusterv1.MachineSpec{ ClusterName: clusterNamespacedName.Name, - InfrastructureRef: corev1.ObjectReference{ - Name: scalewayManagedMachinePoolNamespacedName.Name, - Namespace: scalewayManagedMachinePoolNamespacedName.Namespace, + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedMachinePoolNamespacedName.Name, }, }, }, @@ -214,13 +215,13 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { { Name: machinePoolNamespacedName.Name, Kind: "MachinePool", - APIVersion: expclusterv1.GroupVersion.String(), + APIVersion: clusterv1.GroupVersion.String(), }, }, }, Spec: infrav1.ScalewayManagedMachinePoolSpec{ NodeType: "DEV1-S", - Zone: scw.ZoneFrPar1.String(), + Zone: infrav1.ScalewayZone(scw.ZoneFrPar1), }, }, &infrav1.ScalewayManagedCluster{ @@ -241,7 +242,9 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { ProjectID: "11111111-1111-1111-1111-111111111111", }, Status: infrav1.ScalewayManagedClusterStatus{ - Ready: true, + Initialization: infrav1.ScalewayManagedClusterInitializationStatus{ + Provisioned: ptr.To(true), + }, }, }, &infrav1.ScalewayManagedControlPlane{ @@ -271,13 +274,11 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - ControlPlaneRef: &corev1.ObjectReference{ - Name: scalewayManagedControlPlaneNamespacedName.Name, - Namespace: scalewayManagedControlPlaneNamespacedName.Namespace, + ControlPlaneRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedControlPlaneNamespacedName.Name, }, - InfrastructureRef: &corev1.ObjectReference{ - Name: scalewayManagedClusterNamespacedName.Name, - Namespace: scalewayManagedClusterNamespacedName.Namespace, + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedClusterNamespacedName.Name, }, }, }, @@ -296,8 +297,11 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { // ScalewayManagedMachinePool checks smmp := &infrav1.ScalewayManagedMachinePool{} g.Expect(c.Get(context.TODO(), scalewayManagedMachinePoolNamespacedName, smmp)).To(Succeed()) - g.Expect(smmp.Status.Ready).To(BeTrue()) - g.Expect(smmp.Finalizers).To(ContainElement(infrav1.ManagedMachinePoolFinalizer)) + g.Expect(smmp.Status.Ready).NotTo(BeNil()) + g.Expect(*smmp.Status.Ready).To(BeTrue()) + g.Expect(smmp.Status.Initialization.Provisioned).NotTo(BeNil()) + g.Expect(*smmp.Status.Initialization.Provisioned).To(BeTrue()) + g.Expect(smmp.Finalizers).To(ContainElement(infrav1.ScalewayManagedMachinePoolFinalizer)) }, }, { @@ -317,7 +321,7 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { }, want: reconcile.Result{}, objects: []client.Object{ - &expclusterv1.MachinePool{ + &clusterv1.MachinePool{ ObjectMeta: metav1.ObjectMeta{ Name: machinePoolNamespacedName.Name, Namespace: machinePoolNamespacedName.Namespace, @@ -325,14 +329,13 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { clusterv1.ClusterNameLabel: clusterNamespacedName.Name, }, }, - Spec: expclusterv1.MachinePoolSpec{ + Spec: clusterv1.MachinePoolSpec{ ClusterName: clusterNamespacedName.Name, Template: clusterv1.MachineTemplateSpec{ Spec: clusterv1.MachineSpec{ ClusterName: clusterNamespacedName.Name, - InfrastructureRef: corev1.ObjectReference{ - Name: scalewayManagedMachinePoolNamespacedName.Name, - Namespace: scalewayManagedMachinePoolNamespacedName.Namespace, + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedMachinePoolNamespacedName.Name, }, }, }, @@ -346,15 +349,15 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { { Name: machinePoolNamespacedName.Name, Kind: "MachinePool", - APIVersion: expclusterv1.GroupVersion.String(), + APIVersion: clusterv1.GroupVersion.String(), }, }, - Finalizers: []string{infrav1.ManagedMachinePoolFinalizer}, + Finalizers: []string{infrav1.ScalewayManagedMachinePoolFinalizer}, DeletionTimestamp: &metav1.Time{Time: time.Now()}, }, Spec: infrav1.ScalewayManagedMachinePoolSpec{ NodeType: "DEV1-S", - Zone: scw.ZoneFrPar1.String(), + Zone: infrav1.ScalewayZone(scw.ZoneFrPar1), }, }, &infrav1.ScalewayManagedCluster{ @@ -375,7 +378,9 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { ProjectID: "11111111-1111-1111-1111-111111111111", }, Status: infrav1.ScalewayManagedClusterStatus{ - Ready: true, + Initialization: infrav1.ScalewayManagedClusterInitializationStatus{ + Provisioned: ptr.To(true), + }, }, }, &infrav1.ScalewayManagedControlPlane{ @@ -405,13 +410,11 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { Namespace: clusterNamespacedName.Namespace, }, Spec: clusterv1.ClusterSpec{ - ControlPlaneRef: &corev1.ObjectReference{ - Name: scalewayManagedControlPlaneNamespacedName.Name, - Namespace: scalewayManagedControlPlaneNamespacedName.Namespace, + ControlPlaneRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedControlPlaneNamespacedName.Name, }, - InfrastructureRef: &corev1.ObjectReference{ - Name: scalewayManagedClusterNamespacedName.Name, - Namespace: scalewayManagedClusterNamespacedName.Namespace, + InfrastructureRef: clusterv1.ContractVersionedObjectReference{ + Name: scalewayManagedClusterNamespacedName.Name, }, }, }, @@ -442,7 +445,6 @@ func TestScalewayManagedMachinePoolReconciler_Reconcile(t *testing.T) { corev1.AddToScheme, clusterv1.AddToScheme, infrav1.AddToScheme, - expclusterv1.AddToScheme, ) s := runtime.NewScheme() diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 68c449b..f8a56c3 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -15,8 +15,8 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" // +kubebuilder:scaffold:imports + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to diff --git a/internal/scope/cluster.go b/internal/scope/cluster.go index db948bd..7d6bfd6 100644 --- a/internal/scope/cluster.go +++ b/internal/scope/cluster.go @@ -5,14 +5,18 @@ import ( "errors" "fmt" "slices" + "strings" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/scaleway-sdk-go/scw" - - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) // defaultFrontendControlPlanePort is the default port for the control plane @@ -37,27 +41,41 @@ type ClusterParams struct { // NewCluster creates a new Cluster scope. func NewCluster(ctx context.Context, params *ClusterParams) (*Cluster, error) { - c, err := newScalewayClientForScalewayCluster(ctx, params.Client, params.ScalewayCluster) - if err != nil { - return nil, err - } - helper, err := patch.NewHelper(params.ScalewayCluster, params.Client) if err != nil { return nil, fmt.Errorf("failed to create patch helper for ScalewayCluster: %w", err) } - return &Cluster{ + scope := &Cluster{ patchHelper: helper, ScalewayCluster: params.ScalewayCluster, - ScalewayClient: c, Cluster: params.Cluster, - }, nil + } + + scope.ScalewayClient, err = newScalewayClientForScalewayCluster(ctx, params.Client, params.ScalewayCluster) + if err != nil { + return nil, errors.Join(err, scope.Close(ctx)) + } + + return scope, nil } // PatchObject patches the ScalewayCluster object. func (c *Cluster) PatchObject(ctx context.Context) error { - return c.patchHelper.Patch(ctx, c.ScalewayCluster) + summaryConditions := []string{ + infrav1.PrivateNetworkReadyCondition, + infrav1.PublicGatewaysReadyCondition, + infrav1.ScalewayClusterLoadBalancersReadyCondition, + infrav1.ScalewayClusterDomainReadyCondition, + } + + if err := conditions.SetSummaryCondition(c.ScalewayCluster, c.ScalewayCluster, infrav1.ScalewayClusterReadyCondition, conditions.ForConditionTypes(summaryConditions)); err != nil { + return err + } + + return c.patchHelper.Patch(ctx, c.ScalewayCluster, patch.WithOwnedConditions{ + Conditions: append(summaryConditions, infrav1.ScalewayClusterReadyCondition), + }) } // Close closes the Cluster scope by patching the ScalewayCluster object. @@ -93,17 +111,12 @@ func (c *Cluster) Cloud() scwClient.Interface { // HasPrivateNetwork returns true if the cluster has a Private Network. func (c *Cluster) HasPrivateNetwork() bool { - return c.ScalewayCluster.Spec.Network != nil && - c.ScalewayCluster.Spec.Network.PrivateNetwork.Enabled + return ptr.Deref(c.ScalewayCluster.Spec.Network.PrivateNetwork.Enabled, false) } // PrivateNetworkParams returns the private network parameters. -func (c *Cluster) PrivateNetworkParams() infrav1.PrivateNetworkParams { - if c.ScalewayCluster.Spec.Network == nil || c.ScalewayCluster.Spec.Network.PrivateNetwork == nil { - return infrav1.PrivateNetworkParams{} - } - - return c.ScalewayCluster.Spec.Network.PrivateNetwork.PrivateNetworkParams +func (c *Cluster) PrivateNetwork() infrav1.PrivateNetwork { + return c.ScalewayCluster.Spec.Network.PrivateNetwork.PrivateNetwork } // PrivateNetworkID returns the PrivateNetwork ID of the cluster, obtained from @@ -113,11 +126,11 @@ func (c *Cluster) PrivateNetworkID() (string, error) { return "", errors.New("cluster has no Private Network") } - if c.ScalewayCluster.Status.Network == nil || c.ScalewayCluster.Status.Network.PrivateNetworkID == nil { + if c.ScalewayCluster.Status.Network.PrivateNetworkID == "" { return "", errors.New("PrivateNetworkID not found in ScalewayCluster status") } - return *c.ScalewayCluster.Status.Network.PrivateNetworkID, nil + return string(c.ScalewayCluster.Status.Network.PrivateNetworkID), nil } // ControlPlaneLoadBalancerPort returns the port to use for the control plane @@ -125,9 +138,8 @@ func (c *Cluster) PrivateNetworkID() (string, error) { func (c *Cluster) ControlPlaneLoadBalancerPort() int32 { var port int32 = defaultFrontendControlPlanePort - if c.Cluster.Spec.ClusterNetwork != nil && - c.Cluster.Spec.ClusterNetwork.APIServerPort != nil { - port = *c.Cluster.Spec.ClusterNetwork.APIServerPort + if c.Cluster.Spec.ClusterNetwork.APIServerPort != 0 { + port = c.Cluster.Spec.ClusterNetwork.APIServerPort } return port @@ -136,91 +148,65 @@ func (c *Cluster) ControlPlaneLoadBalancerPort() int32 { // ControlPlaneLoadBalancerAllowedRanges returns the control plane loadbalancer // allowed ranges. func (c *Cluster) ControlPlaneLoadBalancerAllowedRanges() []string { - var result []string - if c.ScalewayCluster.Spec.Network != nil && - c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil { - for _, cidr := range c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.AllowedRanges { - result = append(result, string(cidr)) - } + result := make([]string, 0, len(c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.AllowedRanges)) + + for _, cidr := range c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.AllowedRanges { + result = append(result, string(cidr)) } return result } -// HasControlPlaneDNS returns true if the cluster has an associated domain (public or private). -func (c *Cluster) HasControlPlaneDNS() bool { - return c.hasControlPlaneDNS() || c.hasControlPlanePrivateDNS() -} - -// hasControlPlaneDNS returns true if the cluster has an associated domain that is public. -func (c *Cluster) hasControlPlaneDNS() bool { - return c.ScalewayCluster.Spec.Network != nil && - c.ScalewayCluster.Spec.Network.ControlPlaneDNS != nil -} - -// hasControlPlanePrivateDNS returns true if the cluster has an associated domain that is private. -func (c *Cluster) hasControlPlanePrivateDNS() bool { - return c.ControlPlaneLoadBalancerPrivate() && - c.ScalewayCluster.Spec.Network.ControlPlanePrivateDNS != nil -} - // ControlPlaneDNSZoneAndName returns the DNS zone and the name of the records // that should be updated. func (c *Cluster) ControlPlaneDNSZoneAndName() (string, string, error) { - if c.hasControlPlanePrivateDNS() { - if c.ScalewayCluster.Status.Network == nil { - return "", "", errors.New("missing network field in status") - } + cpDNS := c.ScalewayCluster.Spec.Network.ControlPlaneDNS + if !cpDNS.IsDefined() { + return "", "", errors.New("control plane has no zone or domain") + } - if c.ScalewayCluster.Status.Network.VPCID == nil { + if c.ControlPlaneLoadBalancerPrivate() { + if c.ScalewayCluster.Status.Network.VPCID == "" { return "", "", errors.New("missing vpcID in status") } - if c.ScalewayCluster.Status.Network.PrivateNetworkID == nil { + if c.ScalewayCluster.Status.Network.PrivateNetworkID == "" { return "", "", errors.New("missing privateNetworkID in status") } + // The domain field does not need to be set for the configuration of the + // private zone. As a special case, we use this field to override the private + // zone suffix. + zoneSuffix := "privatedns" + if cpDNS.Domain != "" && !strings.Contains(cpDNS.Domain, ".") { + zoneSuffix = cpDNS.Domain + } + zone := fmt.Sprintf( - "%s.%s.privatedns", - *c.ScalewayCluster.Status.Network.PrivateNetworkID, - *c.ScalewayCluster.Status.Network.VPCID, + "%s.%s.%s", + c.ScalewayCluster.Status.Network.PrivateNetworkID, + c.ScalewayCluster.Status.Network.VPCID, + zoneSuffix, ) - return zone, c.ScalewayCluster.Spec.Network.ControlPlanePrivateDNS.Name, nil + return zone, cpDNS.Name, nil } - if c.hasControlPlaneDNS() { - return c.ScalewayCluster.Spec.Network.ControlPlaneDNS.Domain, - c.ScalewayCluster.Spec.Network.ControlPlaneDNS.Name, nil - } - - return "", "", errors.New("control plane has no zone or domain") + return c.ScalewayCluster.Spec.Network.ControlPlaneDNS.Domain, c.ScalewayCluster.Spec.Network.ControlPlaneDNS.Name, nil } // ControlPlaneHost returns the control plane host. func (c *Cluster) ControlPlaneHost() (string, error) { - if c.hasControlPlanePrivateDNS() { - if c.ScalewayCluster.Status.Network == nil { - return "", errors.New("missing network field in status") - } + if cpDNS := c.ScalewayCluster.Spec.Network.ControlPlaneDNS; cpDNS.IsDefined() { + if c.ControlPlaneLoadBalancerPrivate() { + if c.ScalewayCluster.Status.Network.PrivateNetworkID == "" { + return "", errors.New("missing privateNetworkID in status") + } - if c.ScalewayCluster.Status.Network.PrivateNetworkID == nil { - return "", errors.New("missing privateNetworkID in status") + return fmt.Sprintf("%s.%s.internal", cpDNS.Name, c.ScalewayCluster.Status.Network.PrivateNetworkID), nil } - return fmt.Sprintf( - "%s.%s.internal", - c.ScalewayCluster.Spec.Network.ControlPlanePrivateDNS.Name, - *c.ScalewayCluster.Status.Network.PrivateNetworkID, - ), nil - } - - if c.hasControlPlaneDNS() { - return fmt.Sprintf( - "%s.%s", - c.ScalewayCluster.Spec.Network.ControlPlaneDNS.Name, - c.ScalewayCluster.Spec.Network.ControlPlaneDNS.Domain, - ), nil + return fmt.Sprintf("%s.%s", cpDNS.Name, cpDNS.Domain), nil } if ips := c.ControlPlaneLoadBalancerIPs(); len(ips) != 0 { @@ -234,12 +220,12 @@ func (c *Cluster) ControlPlaneHost() (string, error) { func (c *Cluster) ControlPlaneLoadBalancerIPs() []string { ips := make([]string, 0) - if network := c.ScalewayCluster.Status.Network; network != nil { - if network.LoadBalancerIP != nil { - ips = append(ips, *network.LoadBalancerIP) - } + if c.ScalewayCluster.Status.Network.LoadBalancerIP != "" { + ips = append(ips, string(c.ScalewayCluster.Status.Network.LoadBalancerIP)) + } - ips = append(ips, network.ExtraLoadBalancerIPs...) + for _, ip := range c.ScalewayCluster.Status.Network.ExtraLoadBalancerIPs { + ips = append(ips, string(ip)) } return slices.Sorted(slices.Values(ips)) @@ -248,69 +234,60 @@ func (c *Cluster) ControlPlaneLoadBalancerIPs() []string { // ControlPlaneLoadBalancerPrivate returns true if the control plane should only // be accessible through a private endpoint. func (c *Cluster) ControlPlaneLoadBalancerPrivate() bool { - return c.ScalewayCluster.Spec.Network != nil && - c.ScalewayCluster.Spec.Network.PrivateNetwork != nil && - c.ScalewayCluster.Spec.Network.PrivateNetwork.Enabled && // Private Network must be enabled. - c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil && - c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.Private != nil && - *c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.Private + return c.HasPrivateNetwork() && ptr.Deref(c.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.Private, false) } // IsVPCStatusSet if the VPC fields are set in the status. func (c *Cluster) IsVPCStatusSet() bool { - return c.ScalewayCluster.Status.Network != nil && - c.ScalewayCluster.Status.Network.PrivateNetworkID != nil && - c.ScalewayCluster.Status.Network.VPCID != nil + return c.ScalewayCluster.Status.Network.PrivateNetworkID != "" && + c.ScalewayCluster.Status.Network.VPCID != "" } // SetVPCStatus sets the VPC fields in the status. func (c *Cluster) SetVPCStatus(pnID, vpcID string) { - if c.ScalewayCluster.Status.Network == nil { - c.ScalewayCluster.Status.Network = &infrav1.NetworkStatus{} - } - - c.ScalewayCluster.Status.Network.PrivateNetworkID = &pnID - c.ScalewayCluster.Status.Network.VPCID = &vpcID + c.ScalewayCluster.Status.Network.PrivateNetworkID = infrav1.UUID(pnID) + c.ScalewayCluster.Status.Network.VPCID = infrav1.UUID(vpcID) } // SetStatusLoadBalancerIP sets the loadbalancer IP in the status. func (c *Cluster) SetStatusLoadBalancerIP(ip string) { - if c.ScalewayCluster.Status.Network == nil { - c.ScalewayCluster.Status.Network = &infrav1.NetworkStatus{ - LoadBalancerIP: &ip, - } - } else { - c.ScalewayCluster.Status.Network.LoadBalancerIP = &ip - } + c.ScalewayCluster.Status.Network.LoadBalancerIP = infrav1.IPv4(ip) } // SetStatusExtraLoadBalancerIPs sets the extra loadbalancer IPs in the status. func (c *Cluster) SetStatusExtraLoadBalancerIPs(ips []string) { - if c.ScalewayCluster.Status.Network == nil { - c.ScalewayCluster.Status.Network = &infrav1.NetworkStatus{ - ExtraLoadBalancerIPs: ips, - } - } else { - c.ScalewayCluster.Status.Network.ExtraLoadBalancerIPs = ips + extraIPs := make([]infrav1.IPv4, 0, len(ips)) + + for _, ip := range ips { + extraIPs = append(extraIPs, infrav1.IPv4(ip)) } + + c.ScalewayCluster.Status.Network.ExtraLoadBalancerIPs = extraIPs } // SetFailureDomains sets the failure domains of the cluster. func (c *Cluster) SetFailureDomains(zones []scw.Zone) { - c.ScalewayCluster.Status.FailureDomains = make(clusterv1.FailureDomains) + failureDomains := make([]clusterv1.FailureDomain, 0, len(zones)) for _, zone := range zones { - c.ScalewayCluster.Status.FailureDomains[string(zone)] = clusterv1.FailureDomainSpec{ - ControlPlane: true, - } + failureDomains = append(failureDomains, clusterv1.FailureDomain{ + Name: string(zone), + ControlPlane: ptr.To(true), + }) } + + c.ScalewayCluster.Status.FailureDomains = failureDomains } // PublicGateways returns the desired Public Gateways. -func (c *Cluster) PublicGateways() []infrav1.PublicGatewaySpec { - if c.ScalewayCluster.Spec.Network == nil { - return nil - } - +func (c *Cluster) PublicGateways() []infrav1.PublicGateway { return c.ScalewayCluster.Spec.Network.PublicGateways } + +func (c *Cluster) SetConditions(cond []metav1.Condition) { + c.ScalewayCluster.SetConditions(cond) +} + +func (c *Cluster) GetConditions() []metav1.Condition { + return c.ScalewayCluster.GetConditions() +} diff --git a/internal/scope/cluster_test.go b/internal/scope/cluster_test.go index 340dd96..8c3dcd9 100644 --- a/internal/scope/cluster_test.go +++ b/internal/scope/cluster_test.go @@ -5,12 +5,13 @@ import ( "reflect" "testing" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - "github.com/scaleway/scaleway-sdk-go/scw" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util/patch" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) const ( @@ -39,7 +40,7 @@ func TestCluster_ResourceName(t *testing.T) { name: "no suffix provided", fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster-name", }, }, @@ -51,7 +52,7 @@ func TestCluster_ResourceName(t *testing.T) { name: "suffix provided", fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster-name", }, }, @@ -93,7 +94,7 @@ func TestCluster_ResourceTags(t *testing.T) { name: "base tags", fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "my-cluster", Namespace: "default", }, @@ -106,7 +107,7 @@ func TestCluster_ResourceTags(t *testing.T) { name: "with additional tag", fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "my-cluster", Namespace: "default", }, @@ -153,9 +154,9 @@ func TestCluster_HasPrivateNetwork(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, @@ -199,9 +200,9 @@ func TestCluster_PrivateNetworkID(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, @@ -214,15 +215,15 @@ func TestCluster_PrivateNetworkID(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: infrav1.UUID(privateNetworkID), }, }, }, @@ -269,8 +270,8 @@ func TestCluster_ControlPlaneLoadBalancerPort(t *testing.T) { fields: fields{ Cluster: &clusterv1.Cluster{ Spec: clusterv1.ClusterSpec{ - ClusterNetwork: &clusterv1.ClusterNetwork{ - APIServerPort: scw.Int32Ptr(443), + ClusterNetwork: clusterv1.ClusterNetwork{ + APIServerPort: 443, }, }, }, @@ -305,15 +306,15 @@ func TestCluster_ControlPlaneLoadBalancerAllowedRanges(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{}, }, - want: nil, + want: []string{}, }, { name: "allowed ranges set", fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ AllowedRanges: []infrav1.CIDR{"127.0.0.1/32", "10.0.0.0/8"}, }, }, @@ -335,74 +336,6 @@ func TestCluster_ControlPlaneLoadBalancerAllowedRanges(t *testing.T) { } } -func TestCluster_HasControlPlaneDNS(t *testing.T) { - t.Parallel() - type fields struct { - ScalewayCluster *infrav1.ScalewayCluster - } - tests := []struct { - name string - fields fields - want bool - }{ - { - name: "empty spec", - fields: fields{ - ScalewayCluster: &infrav1.ScalewayCluster{}, - }, - want: false, - }, - { - name: "public dns", - fields: fields{ - ScalewayCluster: &infrav1.ScalewayCluster{ - Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - ControlPlaneDNS: &infrav1.ControlPlaneDNSSpec{ - Domain: "example.com", - Name: "domain", - }, - }, - }, - }, - }, - want: true, - }, - { - name: "private dns", - fields: fields{ - ScalewayCluster: &infrav1.ScalewayCluster{ - Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, - }, - ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), - }, - ControlPlanePrivateDNS: &infrav1.ControlPlanePrivateDNSSpec{ - Name: "domain", - }, - }, - }, - }, - }, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - c := &Cluster{ - ScalewayCluster: tt.fields.ScalewayCluster, - } - if got := c.HasControlPlaneDNS(); got != tt.want { - t.Errorf("Cluster.HasControlPlaneDNS() = %v, want %v", got, tt.want) - } - }) - } -} - func TestCluster_ControlPlaneDNSZoneAndName(t *testing.T) { t.Parallel() type fields struct { @@ -427,8 +360,8 @@ func TestCluster_ControlPlaneDNSZoneAndName(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - ControlPlaneDNS: &infrav1.ControlPlaneDNSSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: "example.com", Name: "domain", }, @@ -444,22 +377,22 @@ func TestCluster_ControlPlaneDNSZoneAndName(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ + Private: ptr.To(true), }, - ControlPlanePrivateDNS: &infrav1.ControlPlanePrivateDNSSpec{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Name: "domain", }, }, }, Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), }, }, }, @@ -512,8 +445,8 @@ func TestCluster_ControlPlaneHost(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - ControlPlaneDNS: &infrav1.ControlPlaneDNSSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: "example.com", Name: "domain", }, @@ -528,22 +461,22 @@ func TestCluster_ControlPlaneHost(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ + Private: ptr.To(true), }, - ControlPlanePrivateDNS: &infrav1.ControlPlanePrivateDNSSpec{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Name: "domain", }, }, }, Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), }, }, }, @@ -555,8 +488,8 @@ func TestCluster_ControlPlaneHost(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr(lbIP), + Network: infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4(lbIP), }, }, }, @@ -607,7 +540,7 @@ func TestCluster_IsVPCStatusSet(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{}, + Network: infrav1.ScalewayClusterNetworkStatus{}, }, }, }, @@ -618,9 +551,9 @@ func TestCluster_IsVPCStatusSet(t *testing.T) { fields: fields{ ScalewayCluster: &infrav1.ScalewayCluster{ Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), }, }, }, diff --git a/internal/scope/helpers.go b/internal/scope/helpers.go index df9d4ac..1a9c823 100644 --- a/internal/scope/helpers.go +++ b/internal/scope/helpers.go @@ -5,13 +5,14 @@ import ( "fmt" "strings" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/scaleway-sdk-go/scw" "golang.org/x/crypto/blake2b" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) const base36set = "0123456789abcdefghijklmnopqrstuvwxyz" @@ -40,14 +41,14 @@ func newScalewayClient(ctx context.Context, c client.Client, region, projectID s } func newScalewayClientForScalewayCluster(ctx context.Context, c client.Client, sc *infrav1.ScalewayCluster) (*scwClient.Client, error) { - return newScalewayClient(ctx, c, sc.Spec.Region, sc.Spec.ProjectID, types.NamespacedName{ + return newScalewayClient(ctx, c, string(sc.Spec.Region), string(sc.Spec.ProjectID), types.NamespacedName{ Namespace: sc.Namespace, Name: sc.Spec.ScalewaySecretName, }) } func newScalewayClientForScalewayManagedCluster(ctx context.Context, c client.Client, smc *infrav1.ScalewayManagedCluster) (*scwClient.Client, error) { - return newScalewayClient(ctx, c, smc.Spec.Region, smc.Spec.ProjectID, types.NamespacedName{ + return newScalewayClient(ctx, c, string(smc.Spec.Region), string(smc.Spec.ProjectID), types.NamespacedName{ Namespace: smc.Namespace, Name: smc.Spec.ScalewaySecretName, }) diff --git a/internal/scope/machine.go b/internal/scope/machine.go index 3aa8991..a6c8b94 100644 --- a/internal/scope/machine.go +++ b/internal/scope/machine.go @@ -5,16 +5,18 @@ import ( "errors" "fmt" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/scw" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) const ( @@ -63,7 +65,17 @@ func NewMachine(params *MachineParams) (*Machine, error) { // PatchObject patches the ScalewayMachine object. func (m *Machine) PatchObject(ctx context.Context) error { - return m.patchHelper.Patch(ctx, m.ScalewayMachine) + summaryConditions := []string{ + infrav1.ScalewayMachineInstanceReadyCondition, + } + + if err := conditions.SetSummaryCondition(m.ScalewayMachine, m.ScalewayMachine, infrav1.ScalewayMachineReadyCondition, conditions.ForConditionTypes(summaryConditions)); err != nil { + return err + } + + return m.patchHelper.Patch(ctx, m.ScalewayMachine, patch.WithOwnedConditions{ + Conditions: append(summaryConditions, infrav1.ScalewayMachineReadyCondition), + }) } // Close closes the Machine scope by patching the ScalewayMachine object. @@ -90,9 +102,8 @@ func (m *Machine) Zone() (scw.Zone, error) { func (m *Machine) RootVolumeSize() scw.Size { size := defaultRootVolumeSize - if m.ScalewayMachine.Spec.RootVolume != nil && - m.ScalewayMachine.Spec.RootVolume.Size != nil { - size = scw.Size(*m.ScalewayMachine.Spec.RootVolume.Size) * scw.GB + if m.ScalewayMachine.Spec.RootVolume.Size != 0 { + size = scw.Size(m.ScalewayMachine.Spec.RootVolume.Size) * scw.GB } return size @@ -102,11 +113,10 @@ func (m *Machine) RootVolumeSize() scw.Size { func (m *Machine) RootVolumeType() (instance.VolumeVolumeType, error) { volumeType := defaultRootVolumeType - if m.ScalewayMachine.Spec.RootVolume != nil && - m.ScalewayMachine.Spec.RootVolume.Type != nil { - volumeType = volumeTypeToInstanceVolumeType[*m.ScalewayMachine.Spec.RootVolume.Type] + if m.ScalewayMachine.Spec.RootVolume.Type != "" { + volumeType = volumeTypeToInstanceVolumeType[m.ScalewayMachine.Spec.RootVolume.Type] if volumeType == "" { - return "", scaleway.WithTerminalError(fmt.Errorf("unknown volume type %s", *m.ScalewayMachine.Spec.RootVolume.Type)) + return "", fmt.Errorf("unknown volume type %s", m.ScalewayMachine.Spec.RootVolume.Type) } } @@ -117,8 +127,8 @@ func (m *Machine) RootVolumeType() (instance.VolumeVolumeType, error) { // If not specified, it returns nil. // Note: IOPS is only applicable for block volumes. func (m *Machine) RootVolumeIOPS() *int64 { - if m.ScalewayMachine.Spec.RootVolume != nil { - return m.ScalewayMachine.Spec.RootVolume.IOPS + if m.ScalewayMachine.Spec.RootVolume.IOPS != 0 { + return &m.ScalewayMachine.Spec.RootVolume.IOPS } return nil @@ -132,28 +142,18 @@ func (m *Machine) HasPublicIPv4() bool { return true } - if m.ScalewayMachine.Spec.PublicNetwork != nil && - m.ScalewayMachine.Spec.PublicNetwork.EnableIPv4 != nil { - return *m.ScalewayMachine.Spec.PublicNetwork.EnableIPv4 - } - - return false + return ptr.Deref(m.ScalewayMachine.Spec.PublicNetwork.EnableIPv4, false) } // HasPublicIPv6 returns true if the machine should have a Public IPv6 address. func (m *Machine) HasPublicIPv6() bool { - if m.ScalewayMachine.Spec.PublicNetwork != nil && - m.ScalewayMachine.Spec.PublicNetwork.EnableIPv6 != nil { - return *m.ScalewayMachine.Spec.PublicNetwork.EnableIPv6 - } - - return false + return ptr.Deref(m.ScalewayMachine.Spec.PublicNetwork.EnableIPv6, false) } // SetProviderID sets the ProviderID of the ScalewayMachine if it is not already set. func (m *Machine) SetProviderID(providerID string) { - if m.ScalewayMachine.Spec.ProviderID == nil { - m.ScalewayMachine.Spec.ProviderID = scw.StringPtr(providerID) + if m.ScalewayMachine.Spec.ProviderID == "" { + m.ScalewayMachine.Spec.ProviderID = providerID } } @@ -187,7 +187,7 @@ func (m *Machine) GetBootstrapData(ctx context.Context) ([]byte, error) { // HasJoinedCluster returns true if the machine has joined the cluster. // A machine is considered to have joined the cluster if it has a NodeRef with a non-empty name. func (m *Machine) HasJoinedCluster() bool { - return m.Machine.Status.NodeRef != nil && m.Machine.Status.NodeRef.Name != "" + return m.Machine.Status.NodeRef.IsDefined() } // IsControlPlane returns true if the machine is a control plane machine. diff --git a/internal/scope/machine_test.go b/internal/scope/machine_test.go index 35024e2..2e33edb 100644 --- a/internal/scope/machine_test.go +++ b/internal/scope/machine_test.go @@ -4,12 +4,13 @@ import ( "reflect" "testing" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/scw" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) func TestMachine_ResourceTags(t *testing.T) { @@ -80,8 +81,8 @@ func TestMachine_RootVolumeSize(t *testing.T) { fields: fields{ ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - RootVolume: &infrav1.RootVolumeSpec{ - Size: scw.Int64Ptr(50), + RootVolume: infrav1.RootVolume{ + Size: 50, }, }, }, @@ -125,8 +126,8 @@ func TestMachine_RootVolumeType(t *testing.T) { fields: fields{ ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - RootVolume: &infrav1.RootVolumeSpec{ - Type: scw.StringPtr("local"), + RootVolume: infrav1.RootVolume{ + Type: "local", }, }, }, @@ -138,8 +139,8 @@ func TestMachine_RootVolumeType(t *testing.T) { fields: fields{ ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - RootVolume: &infrav1.RootVolumeSpec{ - Type: scw.StringPtr("block"), + RootVolume: infrav1.RootVolume{ + Type: "block", }, }, }, @@ -151,8 +152,8 @@ func TestMachine_RootVolumeType(t *testing.T) { fields: fields{ ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - RootVolume: &infrav1.RootVolumeSpec{ - Type: scw.StringPtr("unknown"), + RootVolume: infrav1.RootVolume{ + Type: "unknown", }, }, }, @@ -200,8 +201,8 @@ func TestMachine_RootVolumeIOPS(t *testing.T) { fields: fields{ ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - RootVolume: &infrav1.RootVolumeSpec{ - IOPS: scw.Int64Ptr(15000), + RootVolume: infrav1.RootVolume{ + IOPS: 15000, }, }, }, @@ -248,9 +249,9 @@ func TestMachine_HasPublicIPv4(t *testing.T) { Cluster: &Cluster{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, @@ -266,9 +267,9 @@ func TestMachine_HasPublicIPv4(t *testing.T) { Cluster: &Cluster{ ScalewayCluster: &infrav1.ScalewayCluster{ Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, @@ -276,8 +277,8 @@ func TestMachine_HasPublicIPv4(t *testing.T) { }, ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - PublicNetwork: &infrav1.PublicNetworkSpec{ - EnableIPv4: scw.BoolPtr(true), + PublicNetwork: infrav1.PublicNetwork{ + EnableIPv4: ptr.To(true), }, }, }, @@ -321,8 +322,8 @@ func TestMachine_HasPublicIPv6(t *testing.T) { fields: fields{ ScalewayMachine: &infrav1.ScalewayMachine{ Spec: infrav1.ScalewayMachineSpec{ - PublicNetwork: &infrav1.PublicNetworkSpec{ - EnableIPv6: scw.BoolPtr(true), + PublicNetwork: infrav1.PublicNetwork{ + EnableIPv6: ptr.To(true), }, }, }, @@ -365,7 +366,7 @@ func TestMachine_HasJoinedCluster(t *testing.T) { fields: fields{ Machine: &clusterv1.Machine{ Status: clusterv1.MachineStatus{ - NodeRef: &corev1.ObjectReference{ + NodeRef: clusterv1.MachineNodeReference{ Name: "node-name", }, }, diff --git a/internal/scope/managedcluster.go b/internal/scope/managedcluster.go index 3b3a15b..0d4a94f 100644 --- a/internal/scope/managedcluster.go +++ b/internal/scope/managedcluster.go @@ -6,18 +6,21 @@ import ( "fmt" "strings" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) type ManagedCluster struct { patchHelper *patch.Helper - ManagedCluster *infrav1.ScalewayManagedCluster - ManagedControlPlane *infrav1.ScalewayManagedControlPlane // ManagedControlPlane may be nil, on Cluster deletion. - ScalewayClient scwClient.Interface + ScalewayManagedCluster *infrav1.ScalewayManagedCluster + ScalewayManagedControlPlane *infrav1.ScalewayManagedControlPlane // ManagedControlPlane may be nil, on Cluster deletion. + ScalewayClient scwClient.Interface } // ClusterParams contains mandatory params for creating the Cluster scope. @@ -29,27 +32,39 @@ type ManagedClusterParams struct { // NewManagedCluster creates a new Cluster scope. func NewManagedCluster(ctx context.Context, params *ManagedClusterParams) (*ManagedCluster, error) { - c, err := newScalewayClientForScalewayManagedCluster(ctx, params.Client, params.ManagedCluster) + helper, err := patch.NewHelper(params.ManagedCluster, params.Client) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create patch helper for ScalewayCluster: %w", err) } - helper, err := patch.NewHelper(params.ManagedCluster, params.Client) + mc := &ManagedCluster{ + patchHelper: helper, + ScalewayManagedCluster: params.ManagedCluster, + ScalewayManagedControlPlane: params.ManagedControlPlane, + } + + mc.ScalewayClient, err = newScalewayClientForScalewayManagedCluster(ctx, params.Client, params.ManagedCluster) if err != nil { - return nil, fmt.Errorf("failed to create patch helper for ScalewayCluster: %w", err) + return nil, errors.Join(err, mc.Close(ctx)) } - return &ManagedCluster{ - patchHelper: helper, - ScalewayClient: c, - ManagedCluster: params.ManagedCluster, - ManagedControlPlane: params.ManagedControlPlane, - }, nil + return mc, nil } // PatchObject patches the ScalewayManagedCluster object. func (m *ManagedCluster) PatchObject(ctx context.Context) error { - return m.patchHelper.Patch(ctx, m.ManagedCluster) + summaryConditions := []string{ + infrav1.PrivateNetworkReadyCondition, + infrav1.PublicGatewaysReadyCondition, + } + + if err := conditions.SetSummaryCondition(m.ScalewayManagedCluster, m.ScalewayManagedCluster, infrav1.ScalewayManagedClusterReadyCondition, conditions.ForConditionTypes(summaryConditions)); err != nil { + return err + } + + return m.patchHelper.Patch(ctx, m.ScalewayManagedCluster, patch.WithOwnedConditions{ + Conditions: append(summaryConditions, infrav1.ScalewayManagedClusterReadyCondition), + }) } // Close closes the Machine scope by patching the ScalewayManagedCluster object. @@ -60,7 +75,7 @@ func (m *ManagedCluster) Close(ctx context.Context) error { // ResourceName returns the name/prefix that resources created for the cluster should have. // It is possible to provide additional suffixes that will be appended to the name with a leading "-". func (c *ManagedCluster) ResourceName(suffixes ...string) string { - return nameWithSuffixes(c.ManagedCluster.Name, suffixes...) + return nameWithSuffixes(c.ScalewayManagedCluster.Name, suffixes...) } // ResourceTags returns the tags that resources created for the cluster should have. @@ -68,8 +83,8 @@ func (c *ManagedCluster) ResourceName(suffixes ...string) string { func (c *ManagedCluster) ResourceTags(additional ...string) []string { return append( []string{ - fmt.Sprintf("caps-namespace=%s", c.ManagedCluster.Namespace), - fmt.Sprintf("caps-scalewaymanagedcluster=%s", c.ManagedCluster.Name), + fmt.Sprintf("caps-namespace=%s", c.ScalewayManagedCluster.Namespace), + fmt.Sprintf("caps-scalewaymanagedcluster=%s", c.ScalewayManagedCluster.Name), }, additional...) } @@ -88,35 +103,26 @@ func (c *ManagedCluster) Cloud() scwClient.Interface { func (c *ManagedCluster) HasPrivateNetwork() bool { // On Cluster deletion, we no longer have the info, we have to return true // to force private network cleanup. - if c.ManagedControlPlane == nil { + if c.ScalewayManagedControlPlane == nil { return true } - return !strings.HasPrefix(c.ManagedControlPlane.Spec.Type, "multicloud") + return !strings.HasPrefix(c.ScalewayManagedControlPlane.Spec.Type, "multicloud") } -// IsVPCStatusSet if the VPC fields are set in the status. +// IsVPCStatusSet returns true if the VPC fields are set in the status. func (c *ManagedCluster) IsVPCStatusSet() bool { - return c.ManagedCluster.Status.Network != nil && - c.ManagedCluster.Status.Network.PrivateNetworkID != nil + return c.ScalewayManagedCluster.Status.Network.PrivateNetworkID != "" } -// PrivateNetworkParams returns the private network parameters. -func (c *ManagedCluster) PrivateNetworkParams() infrav1.PrivateNetworkParams { - if c.ManagedCluster.Spec.Network == nil || c.ManagedCluster.Spec.Network.PrivateNetwork == nil { - return infrav1.PrivateNetworkParams{} - } - - return *c.ManagedCluster.Spec.Network.PrivateNetwork +// PrivateNetwork returns the private network parameters. +func (c *ManagedCluster) PrivateNetwork() infrav1.PrivateNetwork { + return c.ScalewayManagedCluster.Spec.Network.PrivateNetwork } // SetVPCStatus sets the VPC fields in the status. func (c *ManagedCluster) SetVPCStatus(pnID, _ string) { - if c.ManagedCluster.Status.Network == nil { - c.ManagedCluster.Status.Network = &infrav1.ManagedNetworkStatus{} - } - - c.ManagedCluster.Status.Network.PrivateNetworkID = &pnID + c.ScalewayManagedCluster.Status.Network.PrivateNetworkID = infrav1.UUID(pnID) } // PrivateNetworkID returns the PrivateNetwork ID of the managed cluster, obtained from @@ -126,18 +132,22 @@ func (c *ManagedCluster) PrivateNetworkID() (string, error) { return "", errors.New("cluster has no Private Network") } - if c.ManagedCluster.Status.Network == nil || c.ManagedCluster.Status.Network.PrivateNetworkID == nil { + if c.ScalewayManagedCluster.Status.Network.PrivateNetworkID == "" { return "", errors.New("PrivateNetworkID not found in ScalewayManagedCluster status") } - return *c.ManagedCluster.Status.Network.PrivateNetworkID, nil + return string(c.ScalewayManagedCluster.Status.Network.PrivateNetworkID), nil } // PublicGateways returns the desired Public Gateways. -func (c *ManagedCluster) PublicGateways() []infrav1.PublicGatewaySpec { - if c.ManagedCluster.Spec.Network == nil { - return nil - } +func (c *ManagedCluster) PublicGateways() []infrav1.PublicGateway { + return c.ScalewayManagedCluster.Spec.Network.PublicGateways +} + +func (c *ManagedCluster) SetConditions(cond []metav1.Condition) { + c.ScalewayManagedCluster.SetConditions(cond) +} - return c.ManagedCluster.Spec.Network.PublicGateways +func (c *ManagedCluster) GetConditions() []metav1.Condition { + return c.ScalewayManagedCluster.GetConditions() } diff --git a/internal/scope/managedcluster_test.go b/internal/scope/managedcluster_test.go index d19ecf7..f03ca1c 100644 --- a/internal/scope/managedcluster_test.go +++ b/internal/scope/managedcluster_test.go @@ -3,9 +3,10 @@ package scope import ( "testing" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "sigs.k8s.io/cluster-api/util/patch" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) func TestManagedCluster_HasPrivateNetwork(t *testing.T) { @@ -64,10 +65,10 @@ func TestManagedCluster_HasPrivateNetwork(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &ManagedCluster{ - patchHelper: tt.fields.patchHelper, - ManagedCluster: tt.fields.ManagedCluster, - ManagedControlPlane: tt.fields.ManagedControlPlane, - ScalewayClient: tt.fields.ScalewayClient, + patchHelper: tt.fields.patchHelper, + ScalewayManagedCluster: tt.fields.ManagedCluster, + ScalewayManagedControlPlane: tt.fields.ManagedControlPlane, + ScalewayClient: tt.fields.ScalewayClient, } if got := c.HasPrivateNetwork(); got != tt.want { t.Errorf("ManagedCluster.HasPrivateNetwork() = %v, want %v", got, tt.want) diff --git a/internal/scope/managedcontrolplane.go b/internal/scope/managedcontrolplane.go index 7e01e36..4202c3f 100644 --- a/internal/scope/managedcontrolplane.go +++ b/internal/scope/managedcontrolplane.go @@ -2,17 +2,20 @@ package scope import ( "context" + "errors" "fmt" "strconv" "strings" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" - "github.com/scaleway/scaleway-sdk-go/scw" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) const ( @@ -23,11 +26,11 @@ const ( type ManagedControlPlane struct { patchHelper *patch.Helper - Client client.Client - Cluster *clusterv1.Cluster - ManagedCluster *infrav1.ScalewayManagedCluster - ManagedControlPlane *infrav1.ScalewayManagedControlPlane - ScalewayClient scwClient.Interface + Client client.Client + Cluster *clusterv1.Cluster + ScalewayManagedCluster *infrav1.ScalewayManagedCluster + ScalewayManagedControlPlane *infrav1.ScalewayManagedControlPlane + ScalewayClient scwClient.Interface } // ClusterParams contains mandatory params for creating the Cluster scope. @@ -40,29 +43,45 @@ type ManagedControlPlaneParams struct { // NewCluster creates a new Cluster scope. func NewManagedControlPlane(ctx context.Context, params *ManagedControlPlaneParams) (*ManagedControlPlane, error) { - c, err := newScalewayClientForScalewayManagedCluster(ctx, params.Client, params.ManagedCluster) - if err != nil { - return nil, err - } helper, err := patch.NewHelper(params.ManagedControlPlane, params.Client) if err != nil { return nil, fmt.Errorf("failed to create patch helper for ScalewayCluster: %w", err) } - return &ManagedControlPlane{ - patchHelper: helper, - Client: params.Client, - ScalewayClient: c, - Cluster: params.Cluster, - ManagedCluster: params.ManagedCluster, - ManagedControlPlane: params.ManagedControlPlane, - }, nil + mcp := &ManagedControlPlane{ + patchHelper: helper, + Client: params.Client, + Cluster: params.Cluster, + ScalewayManagedCluster: params.ManagedCluster, + ScalewayManagedControlPlane: params.ManagedControlPlane, + } + + mcp.ScalewayClient, err = newScalewayClientForScalewayManagedCluster(ctx, params.Client, params.ManagedCluster) + if err != nil { + return nil, errors.Join(err, mcp.Close(ctx)) + } + + return mcp, nil } // PatchObject patches the ScalewayManagedControlPlane object. func (m *ManagedControlPlane) PatchObject(ctx context.Context) error { - return m.patchHelper.Patch(ctx, m.ManagedControlPlane) + summaryConditions := []string{ + infrav1.ScalewayManagedControlPlaneClusterReadyCondition, + } + + if err := conditions.SetSummaryCondition( + m.ScalewayManagedControlPlane, m.ScalewayManagedControlPlane, + infrav1.ScalewayManagedControlPlaneReadyCondition, + conditions.ForConditionTypes(summaryConditions), + ); err != nil { + return err + } + + return m.patchHelper.Patch(ctx, m.ScalewayManagedControlPlane, patch.WithOwnedConditions{ + Conditions: append(summaryConditions, infrav1.ScalewayManagedControlPlaneReadyCondition), + }) } // Close closes the Machine scope by patching the ScalewayManagedControlPlane object. @@ -75,140 +94,116 @@ func (m *ManagedControlPlane) Close(ctx context.Context) error { func (c *ManagedControlPlane) ResourceTags(additional ...string) []string { return append( []string{ - fmt.Sprintf("caps-namespace=%s", c.ManagedControlPlane.Namespace), - fmt.Sprintf("caps-scalewaymanagedcontrolplane=%s", c.ManagedControlPlane.Name), + fmt.Sprintf("caps-namespace=%s", c.ScalewayManagedControlPlane.Namespace), + fmt.Sprintf("caps-scalewaymanagedcontrolplane=%s", c.ScalewayManagedControlPlane.Name), }, additional...) } // PrivateNetworkID returns the Private Network ID that should be used when creating // the managed cluster. It's nil if no Private Network ID is needed. func (m *ManagedControlPlane) PrivateNetworkID() *string { - if m.ManagedCluster.Status.Network == nil { + if m.ScalewayManagedCluster.Status.Network.PrivateNetworkID == "" { return nil } - return m.ManagedCluster.Status.Network.PrivateNetworkID + return ptr.To(string(m.ScalewayManagedCluster.Status.Network.PrivateNetworkID)) } // DeleteWithAdditionalResources returns true if we should tell Scaleway k8s API // to delete additional resources when cluster is deleted. func (m *ManagedControlPlane) DeleteWithAdditionalResources() bool { - if m.ManagedControlPlane.Spec.OnDelete == nil || m.ManagedControlPlane.Spec.OnDelete.WithAdditionalResources == nil { - return false - } - - return *m.ManagedControlPlane.Spec.OnDelete.WithAdditionalResources + return ptr.Deref(m.ScalewayManagedControlPlane.Spec.OnDelete.WithAdditionalResources, false) } // SetControlPlaneEndpoint sets the control plane endpoint host and port. func (m *ManagedControlPlane) SetControlPlaneEndpoint(host string, port int32) { - m.ManagedControlPlane.Spec.ControlPlaneEndpoint.Host = host - m.ManagedControlPlane.Spec.ControlPlaneEndpoint.Port = port + m.ScalewayManagedControlPlane.Spec.ControlPlaneEndpoint.Host = host + m.ScalewayManagedControlPlane.Spec.ControlPlaneEndpoint.Port = port } // SetStatusVersion sets the current cluster Kubernetes version in the status. func (m *ManagedControlPlane) SetStatusVersion(version string) { - m.ManagedControlPlane.Status.Version = scw.StringPtr("v" + version) + m.ScalewayManagedControlPlane.Status.Version = "v" + version } // DesiredVersion returns the desired Kubernetes version, without leading "v". func (m *ManagedControlPlane) DesiredVersion() string { - version, _ := strings.CutPrefix(m.ManagedControlPlane.Spec.Version, "v") + version, _ := strings.CutPrefix(m.ScalewayManagedControlPlane.Spec.Version, "v") return version } // FixedVersion returns the desired Kubernetes version, with a leading "v" if it's missing. func (m *ManagedControlPlane) FixedVersion() string { - if !strings.HasPrefix(m.ManagedControlPlane.Spec.Version, "v") { - return "v" + m.ManagedControlPlane.Spec.Version + if !strings.HasPrefix(m.ScalewayManagedControlPlane.Spec.Version, "v") { + return "v" + m.ScalewayManagedControlPlane.Spec.Version } - return m.ManagedControlPlane.Spec.Version + return m.ScalewayManagedControlPlane.Spec.Version } func (m *ManagedControlPlane) DesiredTags() []string { - return m.ResourceTags(m.ManagedControlPlane.Spec.AdditionalTags...) + return m.ResourceTags(m.ScalewayManagedControlPlane.Spec.AdditionalTags...) } func (m *ManagedControlPlane) ClusterName() string { - if m.ManagedControlPlane.Spec.ClusterName == nil { - name, err := generateScalewayK8sName(m.ManagedControlPlane.Name, m.ManagedControlPlane.Namespace, maxClusterNameLength) + if m.ScalewayManagedControlPlane.Spec.ClusterName == "" { + name, err := GenerateClusterName(m.ScalewayManagedControlPlane) if err != nil { panic(err) } - m.ManagedControlPlane.Spec.ClusterName = &name + m.ScalewayManagedControlPlane.Spec.ClusterName = name } - return *m.ManagedControlPlane.Spec.ClusterName + return m.ScalewayManagedControlPlane.Spec.ClusterName } -func (m *ManagedControlPlane) DesiredCNI() k8s.CNI { - var cni k8s.CNI - if m.ManagedControlPlane.Spec.CNI != nil { - cni = k8s.CNI(*m.ManagedControlPlane.Spec.CNI) +func (m *ManagedControlPlane) DesiredCNI() (cni k8s.CNI) { + if m.ScalewayManagedControlPlane.Spec.CNI != "" { + cni = k8s.CNI(m.ScalewayManagedControlPlane.Spec.CNI) } - return cni + return } func (m *ManagedControlPlane) DesiredType() string { - return m.ManagedControlPlane.Spec.Type + return m.ScalewayManagedControlPlane.Spec.Type } func (m *ManagedControlPlane) DesiredClusterAutoscalerConfig() (*k8s.ClusterAutoscalerConfig, error) { + autoscaler := m.ScalewayManagedControlPlane.Spec.Autoscaler + config := &k8s.ClusterAutoscalerConfig{ - ScaleDownDisabled: false, + ScaleDownDisabled: ptr.Deref(autoscaler.ScaleDownDisabled, false), + IgnoreDaemonsetsUtilization: ptr.Deref(autoscaler.IgnoreDaemonsetsUtilization, false), + BalanceSimilarNodeGroups: ptr.Deref(autoscaler.BalanceSimilarNodeGroups, false), ScaleDownDelayAfterAdd: "10m", Estimator: k8s.AutoscalerEstimatorBinpacking, Expander: k8s.AutoscalerExpanderRandom, - IgnoreDaemonsetsUtilization: false, - BalanceSimilarNodeGroups: false, - ExpendablePodsPriorityCutoff: -10, + ExpendablePodsPriorityCutoff: ptr.Deref(autoscaler.ExpendablePodsPriorityCutoff, -10), ScaleDownUnneededTime: "10m", ScaleDownUtilizationThreshold: 0.5, MaxGracefulTerminationSec: 600, } - autoscaler := m.ManagedControlPlane.Spec.Autoscaler - if autoscaler == nil { - return config, nil - } - - if autoscaler.ScaleDownDisabled != nil { - config.ScaleDownDisabled = *autoscaler.ScaleDownDisabled - } - - if autoscaler.ScaleDownDelayAfterAdd != nil { - config.ScaleDownDelayAfterAdd = *autoscaler.ScaleDownDelayAfterAdd - } - - if autoscaler.Estimator != nil && *autoscaler.Estimator != k8s.AutoscalerEstimatorUnknownEstimator.String() { - config.Estimator = k8s.AutoscalerEstimator(*autoscaler.Estimator) - } - - if autoscaler.Expander != nil && *autoscaler.Expander != k8s.AutoscalerEstimatorUnknownEstimator.String() { - config.Expander = k8s.AutoscalerExpander(*autoscaler.Expander) - } - - if autoscaler.IgnoreDaemonsetsUtilization != nil { - config.IgnoreDaemonsetsUtilization = *autoscaler.IgnoreDaemonsetsUtilization + if autoscaler.ScaleDownDelayAfterAdd != "" { + config.ScaleDownDelayAfterAdd = autoscaler.ScaleDownDelayAfterAdd } - if autoscaler.BalanceSimilarNodeGroups != nil { - config.BalanceSimilarNodeGroups = *autoscaler.BalanceSimilarNodeGroups + if autoscaler.Estimator != "" && autoscaler.Estimator != k8s.AutoscalerEstimatorUnknownEstimator.String() { + config.Estimator = k8s.AutoscalerEstimator(autoscaler.Estimator) } - if autoscaler.ExpendablePodsPriorityCutoff != nil { - config.ExpendablePodsPriorityCutoff = *autoscaler.ExpendablePodsPriorityCutoff + if autoscaler.Expander != "" && autoscaler.Expander != k8s.AutoscalerEstimatorUnknownEstimator.String() { + config.Expander = k8s.AutoscalerExpander(autoscaler.Expander) } - if autoscaler.ScaleDownUnneededTime != nil { - config.ScaleDownUnneededTime = *autoscaler.ScaleDownUnneededTime + if autoscaler.ScaleDownUnneededTime != "" { + config.ScaleDownUnneededTime = autoscaler.ScaleDownUnneededTime } - if autoscaler.ScaleDownUtilizationThreshold != nil { - value, err := strconv.ParseFloat(*autoscaler.ScaleDownUtilizationThreshold, 32) + if autoscaler.ScaleDownUtilizationThreshold != "" { + value, err := strconv.ParseFloat(autoscaler.ScaleDownUtilizationThreshold, 32) if err != nil { return nil, fmt.Errorf("failed to parse scaleDownUtilizationThreshold as float32: %w", err) } @@ -216,94 +211,53 @@ func (m *ManagedControlPlane) DesiredClusterAutoscalerConfig() (*k8s.ClusterAuto config.ScaleDownUtilizationThreshold = float32(value) } - if autoscaler.MaxGracefulTerminationSec != nil { - config.MaxGracefulTerminationSec = uint32(*autoscaler.MaxGracefulTerminationSec) + if autoscaler.MaxGracefulTerminationSec > 0 { + config.MaxGracefulTerminationSec = uint32(autoscaler.MaxGracefulTerminationSec) } return config, nil } func (m *ManagedControlPlane) DesiredAutoUpgrade() *k8s.ClusterAutoUpgrade { + autoUpgrade := m.ScalewayManagedControlPlane.Spec.AutoUpgrade + config := &k8s.ClusterAutoUpgrade{ - Enabled: false, + Enabled: ptr.Deref(autoUpgrade.Enabled, false), MaintenanceWindow: &k8s.MaintenanceWindow{ - StartHour: 0, + StartHour: uint32(ptr.Deref(autoUpgrade.MaintenanceWindow.StartHour, 0)), Day: k8s.MaintenanceWindowDayOfTheWeekAny, }, } - autoUpgrade := m.ManagedControlPlane.Spec.AutoUpgrade - if autoUpgrade == nil { - return config - } - - config.Enabled = autoUpgrade.Enabled - - if autoUpgrade.MaintenanceWindow != nil { - config.MaintenanceWindow = &k8s.MaintenanceWindow{} - - if autoUpgrade.MaintenanceWindow.StartHour != nil { - config.MaintenanceWindow.StartHour = *scw.Uint32Ptr(uint32(*autoUpgrade.MaintenanceWindow.StartHour)) - } - - if autoUpgrade.MaintenanceWindow.Day != nil { - config.MaintenanceWindow.Day = k8s.MaintenanceWindowDayOfTheWeek(*autoUpgrade.MaintenanceWindow.Day) - } + if autoUpgrade.MaintenanceWindow.Day != "" { + config.MaintenanceWindow.Day = k8s.MaintenanceWindowDayOfTheWeek(autoUpgrade.MaintenanceWindow.Day) } return config } func (m *ManagedControlPlane) DesiredClusterOpenIDConnectConfig() *k8s.ClusterOpenIDConnectConfig { - config := &k8s.ClusterOpenIDConnectConfig{ - GroupsClaim: []string{}, - RequiredClaim: []string{}, - } - - oidc := m.ManagedControlPlane.Spec.OpenIDConnect - if oidc == nil { - return config - } - - config.IssuerURL = oidc.IssuerURL - config.ClientID = oidc.ClientID - - if config.GroupsClaim != nil { - config.GroupsClaim = oidc.GroupsClaim - } - - if config.RequiredClaim != nil { - config.RequiredClaim = oidc.RequiredClaim - } - - if oidc.UsernameClaim != nil { - config.UsernameClaim = *oidc.UsernameClaim - } - - if oidc.UsernamePrefix != nil { - config.UsernamePrefix = *oidc.UsernamePrefix - } - - if oidc.UsernameClaim != nil { - config.UsernameClaim = *oidc.UsernameClaim + oidc := m.ScalewayManagedControlPlane.Spec.OpenIDConnect + return &k8s.ClusterOpenIDConnectConfig{ + IssuerURL: oidc.IssuerURL, + ClientID: oidc.ClientID, + GroupsClaim: oidc.GroupsClaim, + RequiredClaim: oidc.RequiredClaim, + UsernamePrefix: oidc.UsernamePrefix, + UsernameClaim: oidc.UsernameClaim, + GroupsPrefix: oidc.GroupsPrefix, } - - if oidc.GroupsPrefix != nil { - config.GroupsPrefix = *oidc.GroupsPrefix - } - - return config } func (m *ManagedControlPlane) DesiredAllowedRanges() []string { // If ACL is not configured, we want all ranges to be allowed. - if m.ManagedControlPlane.Spec.ACL == nil { + if m.ScalewayManagedControlPlane.Spec.ACL == nil { return []string{"0.0.0.0/0"} } - ranges := make([]string, 0, len(m.ManagedControlPlane.Spec.ACL.AllowedRanges)) + ranges := make([]string, 0, len(m.ScalewayManagedControlPlane.Spec.ACL.AllowedRanges)) - for _, r := range m.ManagedControlPlane.Spec.ACL.AllowedRanges { + for _, r := range m.ScalewayManagedControlPlane.Spec.ACL.AllowedRanges { ranges = append(ranges, string(r)) } @@ -311,9 +265,7 @@ func (m *ManagedControlPlane) DesiredAllowedRanges() []string { } func (m *ManagedControlPlane) ClusterEndpoint(cluster *k8s.Cluster) string { - if m.ManagedControlPlane.Spec.EnablePrivateEndpoint != nil && - *m.ManagedControlPlane.Spec.EnablePrivateEndpoint && - cluster.PrivateNetworkID != nil { + if ptr.Deref(m.ScalewayManagedControlPlane.Spec.EnablePrivateEndpoint, false) && cluster.PrivateNetworkID != nil { return fmt.Sprintf("https://%s.%s.internal:6443", cluster.ID, *cluster.PrivateNetworkID) } @@ -336,3 +288,11 @@ func generateScalewayK8sName(resourceName, namespace string, maxLength int) (str return fmt.Sprintf("%s%s", resourcePrefix, hashedName), nil } + +func GenerateClusterName(smcp *infrav1.ScalewayManagedControlPlane) (string, error) { + if smcp.Name == "" || smcp.Namespace == "" { + return "", errors.New("can't generate clusterName if name or namespace is not set") + } + + return generateScalewayK8sName(smcp.Name, smcp.Namespace, maxClusterNameLength) +} diff --git a/internal/scope/managedcontrolplane_test.go b/internal/scope/managedcontrolplane_test.go index f008d9e..a9ca0c1 100644 --- a/internal/scope/managedcontrolplane_test.go +++ b/internal/scope/managedcontrolplane_test.go @@ -3,9 +3,9 @@ package scope import ( "testing" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/scaleway-sdk-go/scw" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) func Test_generateScalewayK8sName(t *testing.T) { @@ -67,18 +67,18 @@ func TestManagedControlPlane_ClusterName(t *testing.T) { name: "name already present", fields: fields{ ManagedCluster: &infrav1.ScalewayManagedCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, }, ManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, Spec: infrav1.ScalewayManagedControlPlaneSpec{ - ClusterName: scw.StringPtr("mycluster"), + ClusterName: "mycluster", }, }, }, @@ -88,13 +88,13 @@ func TestManagedControlPlane_ClusterName(t *testing.T) { name: "generate name", fields: fields{ ManagedCluster: &infrav1.ScalewayManagedCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, }, ManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, @@ -106,14 +106,14 @@ func TestManagedControlPlane_ClusterName(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &ManagedControlPlane{ - ManagedCluster: tt.fields.ManagedCluster, - ManagedControlPlane: tt.fields.ManagedControlPlane, + ScalewayManagedCluster: tt.fields.ManagedCluster, + ScalewayManagedControlPlane: tt.fields.ManagedControlPlane, } if got := m.ClusterName(); got != tt.want { t.Errorf("ManagedControlPlane.ClusterName() = %v, want %v", got, tt.want) } - if tt.want != *m.ManagedControlPlane.Spec.ClusterName { - t.Errorf("expected ManagedControlPlane.Spec.ClusterName to equal %v, got %v", tt.want, *m.ManagedControlPlane.Spec.ClusterName) + if tt.want != m.ScalewayManagedControlPlane.Spec.ClusterName { + t.Errorf("expected ManagedControlPlane.Spec.ClusterName to equal %v, got %v", tt.want, m.ScalewayManagedControlPlane.Spec.ClusterName) } }) } diff --git a/internal/scope/managedmachinepool.go b/internal/scope/managedmachinepool.go index 42ecb17..4292fee 100644 --- a/internal/scope/managedmachinepool.go +++ b/internal/scope/managedmachinepool.go @@ -3,35 +3,35 @@ package scope import ( "context" "fmt" - "math" "strings" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" - "github.com/scaleway/scaleway-sdk-go/scw" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/util/conditions" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + scwClient "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) type ManagedMachinePool struct { - patchHelper *patch.Helper - Client client.Client - Cluster *clusterv1.Cluster - MachinePool *expclusterv1.MachinePool - ManagedCluster *infrav1.ScalewayManagedCluster - ManagedControlPlane *infrav1.ScalewayManagedControlPlane - ManagedMachinePool *infrav1.ScalewayManagedMachinePool - ScalewayClient scwClient.Interface + patchHelper *patch.Helper + Client client.Client + Cluster *clusterv1.Cluster + MachinePool *clusterv1.MachinePool + ScalewayManagedCluster *infrav1.ScalewayManagedCluster + ScalewayManagedControlPlane *infrav1.ScalewayManagedControlPlane + ScalewayManagedMachinePool *infrav1.ScalewayManagedMachinePool + ScalewayClient scwClient.Interface } // ClusterParams contains mandatory params for creating the Cluster scope. type ManagedMachinePoolParams struct { Client client.Client Cluster *clusterv1.Cluster - MachinePool *expclusterv1.MachinePool + MachinePool *clusterv1.MachinePool ManagedCluster *infrav1.ScalewayManagedCluster ManagedControlPlane *infrav1.ScalewayManagedControlPlane ManagedMachinePool *infrav1.ScalewayManagedMachinePool @@ -39,31 +39,46 @@ type ManagedMachinePoolParams struct { // NewCluster creates a new Cluster scope. func NewManagedMachinePool(ctx context.Context, params *ManagedMachinePoolParams) (*ManagedMachinePool, error) { - c, err := newScalewayClientForScalewayManagedCluster(ctx, params.Client, params.ManagedCluster) + helper, err := patch.NewHelper(params.ManagedMachinePool, params.Client) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create patch helper for ScalewayManagedMachinePool: %w", err) } - helper, err := patch.NewHelper(params.ManagedMachinePool, params.Client) + mmp := &ManagedMachinePool{ + patchHelper: helper, + Client: params.Client, + Cluster: params.Cluster, + MachinePool: params.MachinePool, + ScalewayManagedCluster: params.ManagedCluster, + ScalewayManagedControlPlane: params.ManagedControlPlane, + ScalewayManagedMachinePool: params.ManagedMachinePool, + } + + mmp.ScalewayClient, err = newScalewayClientForScalewayManagedCluster(ctx, params.Client, params.ManagedCluster) if err != nil { - return nil, fmt.Errorf("failed to create patch helper for ScalewayManagedMachinePool: %w", err) + return nil, err } - return &ManagedMachinePool{ - patchHelper: helper, - Client: params.Client, - ScalewayClient: c, - Cluster: params.Cluster, - MachinePool: params.MachinePool, - ManagedCluster: params.ManagedCluster, - ManagedControlPlane: params.ManagedControlPlane, - ManagedMachinePool: params.ManagedMachinePool, - }, nil + return mmp, nil } // PatchObject patches the ScalewayManagedControlPlane object. func (m *ManagedMachinePool) PatchObject(ctx context.Context) error { - return m.patchHelper.Patch(ctx, m.ManagedMachinePool) + summaryConditions := []string{ + infrav1.ScalewayManagedMachinePoolPoolReadyCondition, + } + + if err := conditions.SetSummaryCondition( + m.ScalewayManagedMachinePool, m.ScalewayManagedMachinePool, + infrav1.ScalewayManagedMachinePoolReadyCondition, + conditions.ForConditionTypes(summaryConditions), + ); err != nil { + return err + } + + return m.patchHelper.Patch(ctx, m.ScalewayManagedMachinePool, patch.WithOwnedConditions{ + Conditions: append(summaryConditions, infrav1.ScalewayManagedMachinePoolReadyCondition), + }) } // Close closes the Machine scope by patching the ScalewayManagedControlPlane object. @@ -74,7 +89,7 @@ func (m *ManagedMachinePool) Close(ctx context.Context) error { // ResourceName returns the name/prefix that resources created for the cluster should have. // It is possible to provide additional suffixes that will be appended to the name with a leading "-". func (m *ManagedMachinePool) ResourceName(suffixes ...string) string { - return strings.Join(append([]string{m.ManagedMachinePool.Name}, suffixes...), "-") + return strings.Join(append([]string{m.ScalewayManagedMachinePool.Name}, suffixes...), "-") } // ResourceTags returns the tags that resources created for the cluster should have. @@ -82,77 +97,57 @@ func (m *ManagedMachinePool) ResourceName(suffixes ...string) string { func (c *ManagedMachinePool) ResourceTags(additional ...string) []string { return append( []string{ - fmt.Sprintf("caps-namespace=%s", c.ManagedMachinePool.Namespace), - fmt.Sprintf("caps-scalewaymanagedmachinepool=%s", c.ManagedMachinePool.Name), + fmt.Sprintf("caps-namespace=%s", c.ScalewayManagedMachinePool.Namespace), + fmt.Sprintf("caps-scalewaymanagedmachinepool=%s", c.ScalewayManagedMachinePool.Name), }, additional...) } -func (c *ManagedMachinePool) ClusterName() (string, bool) { - if c.ManagedControlPlane.Spec.ClusterName == nil { - return "", false - } - - return *c.ManagedControlPlane.Spec.ClusterName, true +func (c *ManagedMachinePool) ClusterName() string { + return c.ScalewayManagedControlPlane.Spec.ClusterName } -func (c *ManagedMachinePool) Scaling() (autoscaling bool, size, min, max uint32) { +func (c *ManagedMachinePool) Scaling() (autoscaling bool, size, minSize, maxSize uint32) { // Completely ignore scaling parameters for external node pools. - if c.ManagedMachinePool.Spec.NodeType == "external" { + if c.ScalewayManagedMachinePool.Spec.NodeType == "external" { return } - size = c.replicas() - - if c.ManagedMachinePool.Spec.Scaling != nil { - if c.ManagedMachinePool.Spec.Scaling.Autoscaling != nil { - autoscaling = *c.ManagedMachinePool.Spec.Scaling.Autoscaling - } - - if c.ManagedMachinePool.Spec.Scaling.MinSize != nil { - min = uint32(*c.ManagedMachinePool.Spec.Scaling.MinSize) - } - - if c.ManagedMachinePool.Spec.Scaling.MaxSize != nil { - max = uint32(*c.ManagedMachinePool.Spec.Scaling.MaxSize) - } - } + scaling := c.ScalewayManagedMachinePool.Spec.Scaling - min = uint32(math.Min(float64(min), float64(size))) - max = uint32(math.Max(float64(max), float64(size))) + size = c.replicas() + autoscaling = ptr.Deref(scaling.Autoscaling, false) + minSize = min(uint32(ptr.Deref(scaling.MinSize, 0)), size) + maxSize = max(uint32(ptr.Deref(scaling.MaxSize, 0)), size) return } func (c *ManagedMachinePool) Autohealing() bool { - if c.ManagedMachinePool.Spec.Autohealing == nil { + if c.ScalewayManagedMachinePool.Spec.Autohealing == nil { return false } - return *c.ManagedMachinePool.Spec.Autohealing + return *c.ScalewayManagedMachinePool.Spec.Autohealing } func (c *ManagedMachinePool) PublicIPDisabled() bool { - if c.ManagedMachinePool.Spec.PublicIPDisabled == nil { + if c.ScalewayManagedMachinePool.Spec.PublicIPDisabled == nil { return false } - return *c.ManagedMachinePool.Spec.PublicIPDisabled + return *c.ScalewayManagedMachinePool.Spec.PublicIPDisabled } func (c *ManagedMachinePool) replicas() uint32 { - if c.MachinePool.Spec.Replicas == nil { - return 3 - } - - return uint32(*c.MachinePool.Spec.Replicas) + return uint32(ptr.Deref(c.MachinePool.Spec.Replicas, 3)) } func (c *ManagedMachinePool) RootVolumeSizeGB() *uint64 { - if c.ManagedMachinePool.Spec.RootVolumeSizeGB == nil { + if c.ScalewayManagedMachinePool.Spec.RootVolumeSizeGB == 0 { return nil } - return scw.Uint64Ptr(uint64(*c.ManagedMachinePool.Spec.RootVolumeSizeGB)) + return ptr.To(uint64(c.ScalewayManagedMachinePool.Spec.RootVolumeSizeGB)) } func (c *ManagedMachinePool) SetProviderIDs(nodes []*k8s.Node) { @@ -166,52 +161,54 @@ func (c *ManagedMachinePool) SetProviderIDs(nodes []*k8s.Node) { providerIDs = append(providerIDs, node.ProviderID) } - c.ManagedMachinePool.Spec.ProviderIDList = providerIDs + c.ScalewayManagedMachinePool.Spec.ProviderIDList = providerIDs } func (c *ManagedMachinePool) SetStatusReplicas(replicas uint32) { - c.ManagedMachinePool.Status.Replicas = int32(replicas) + c.ScalewayManagedMachinePool.Status.Replicas = ptr.To(int32(replicas)) } func (c *ManagedMachinePool) RootVolumeType() k8s.PoolVolumeType { - if c.ManagedMachinePool.Spec.RootVolumeType == nil { + if c.ScalewayManagedMachinePool.Spec.RootVolumeType == "" { return k8s.PoolVolumeTypeDefaultVolumeType } - return k8s.PoolVolumeType(*c.ManagedMachinePool.Spec.RootVolumeType) + return k8s.PoolVolumeType(c.ScalewayManagedMachinePool.Spec.RootVolumeType) } func (c *ManagedMachinePool) DesiredPoolUpgradePolicy() *k8s.PoolUpgradePolicy { - policy := &k8s.PoolUpgradePolicy{ - MaxSurge: 0, - MaxUnavailable: 1, - } - - if c.ManagedMachinePool.Spec.UpgradePolicy == nil { - return policy + return &k8s.PoolUpgradePolicy{ + MaxSurge: uint32(ptr.Deref(c.ScalewayManagedMachinePool.Spec.UpgradePolicy.MaxSurge, 0)), + MaxUnavailable: uint32(ptr.Deref(c.ScalewayManagedMachinePool.Spec.UpgradePolicy.MaxUnavailable, 1)), } +} - if c.ManagedMachinePool.Spec.UpgradePolicy.MaxSurge != nil { - policy.MaxSurge = uint32(*c.ManagedMachinePool.Spec.UpgradePolicy.MaxSurge) - } +func (m *ManagedMachinePool) DesiredTags() []string { + return m.ResourceTags(m.ScalewayManagedMachinePool.Spec.AdditionalTags...) +} - if c.ManagedMachinePool.Spec.UpgradePolicy.MaxUnavailable != nil { - policy.MaxUnavailable = uint32(*c.ManagedMachinePool.Spec.UpgradePolicy.MaxUnavailable) +func (m *ManagedMachinePool) DesiredVersion() *string { + version := m.MachinePool.Spec.Template.Spec.Version + if version == "" { + return nil } - return policy + version, _ = strings.CutPrefix(version, "v") + return &version } -func (m *ManagedMachinePool) DesiredTags() []string { - return m.ResourceTags(m.ManagedMachinePool.Spec.AdditionalTags...) +func (m *ManagedMachinePool) PlacementGroupID() *string { + if m.ScalewayManagedMachinePool.Spec.PlacementGroupID == "" { + return nil + } + + return ptr.To(string(m.ScalewayManagedMachinePool.Spec.PlacementGroupID)) } -func (m *ManagedMachinePool) DesiredVersion() *string { - version := m.MachinePool.Spec.Template.Spec.Version - if version == nil { +func (m *ManagedMachinePool) SecurityGroupID() *string { + if m.ScalewayManagedMachinePool.Spec.SecurityGroupID == "" { return nil } - *version, _ = strings.CutPrefix(*version, "v") - return version + return ptr.To(string(m.ScalewayManagedMachinePool.Spec.SecurityGroupID)) } diff --git a/internal/service/scaleway/client/block_test.go b/internal/service/scaleway/client/block_test.go index 9f321ee..d2dec4d 100644 --- a/internal/service/scaleway/client/block_test.go +++ b/internal/service/scaleway/client/block_test.go @@ -5,10 +5,11 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/block/v1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const volumeID = "22222222-2222-2222-2222-222222222222" diff --git a/internal/service/scaleway/client/client.go b/internal/service/scaleway/client/client.go index 1fa04f1..c61970d 100644 --- a/internal/service/scaleway/client/client.go +++ b/internal/service/scaleway/client/client.go @@ -4,7 +4,6 @@ import ( "fmt" "slices" - "github.com/scaleway/cluster-api-provider-scaleway/internal/version" "github.com/scaleway/scaleway-sdk-go/api/block/v1" domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" @@ -15,6 +14,8 @@ import ( "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" "github.com/scaleway/scaleway-sdk-go/scw" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/version" ) const ( diff --git a/internal/service/scaleway/client/client_test.go b/internal/service/scaleway/client/client_test.go index a8af65a..dfde4ac 100644 --- a/internal/service/scaleway/client/client_test.go +++ b/internal/service/scaleway/client/client_test.go @@ -6,6 +6,7 @@ import ( "testing" . "github.com/onsi/gomega" + "github.com/scaleway/scaleway-sdk-go/scw" ) diff --git a/internal/service/scaleway/client/domain.go b/internal/service/scaleway/client/domain.go index c08737e..221b24d 100644 --- a/internal/service/scaleway/client/domain.go +++ b/internal/service/scaleway/client/domain.go @@ -5,6 +5,7 @@ import ( domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/utils/ptr" ) type DomainAPI interface { @@ -62,7 +63,7 @@ func (c *Client) SetDNSZoneRecords(ctx context.Context, zone, name string, ips [ Priority: 0, TTL: 60, Type: domain.RecordTypeA, - Comment: scw.StringPtr(createdByDescription), + Comment: ptr.To(createdByDescription), }) } diff --git a/internal/service/scaleway/client/domain_test.go b/internal/service/scaleway/client/domain_test.go index 7a9eee5..1645692 100644 --- a/internal/service/scaleway/client/domain_test.go +++ b/internal/service/scaleway/client/domain_test.go @@ -5,10 +5,12 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -243,7 +245,7 @@ func TestClient_SetDNSZoneRecords(t *testing.T) { Priority: 0, TTL: 60, Type: domain.RecordTypeA, - Comment: scw.StringPtr(createdByDescription), + Comment: ptr.To(createdByDescription), }, { Data: "127.0.0.2", @@ -251,7 +253,7 @@ func TestClient_SetDNSZoneRecords(t *testing.T) { Priority: 0, TTL: 60, Type: domain.RecordTypeA, - Comment: scw.StringPtr(createdByDescription), + Comment: ptr.To(createdByDescription), }, { Data: "127.0.0.3", @@ -259,7 +261,7 @@ func TestClient_SetDNSZoneRecords(t *testing.T) { Priority: 0, TTL: 60, Type: domain.RecordTypeA, - Comment: scw.StringPtr(createdByDescription), + Comment: ptr.To(createdByDescription), }, }, }, diff --git a/internal/service/scaleway/client/instance.go b/internal/service/scaleway/client/instance.go index 18be045..91c5bfc 100644 --- a/internal/service/scaleway/client/instance.go +++ b/internal/service/scaleway/client/instance.go @@ -9,6 +9,7 @@ import ( "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/utils/ptr" ) type InstanceAPI interface { @@ -126,7 +127,7 @@ func (c *Client) CreateServer( Zone: zone, Name: name, CommercialType: commercialType, - DynamicIPRequired: scw.BoolPtr(false), + DynamicIPRequired: ptr.To(false), Image: &imageID, PlacementGroup: placementGroupID, SecurityGroup: securityGroupID, @@ -134,7 +135,7 @@ func (c *Client) CreateServer( "0": { Size: &rootVolumeSize, VolumeType: rootVolumeType, - Boot: scw.BoolPtr(true), + Boot: ptr.To(true), }, }, Tags: append(tags, createdByTag), @@ -143,7 +144,7 @@ func (c *Client) CreateServer( // Automatically attach scratch volume if server supports it. if serverType.ScratchStorageMaxSize != nil && *serverType.ScratchStorageMaxSize > 0 { req.Volumes["1"] = &instance.VolumeServerTemplate{ - Name: scw.StringPtr(fmt.Sprintf("%s-scratch", name)), + Name: ptr.To(fmt.Sprintf("%s-scratch", name)), Size: serverType.ScratchStorageMaxSize, VolumeType: instance.VolumeVolumeTypeScratch, } @@ -167,8 +168,8 @@ func (c *Client) FindImage(ctx context.Context, zone scw.Zone, name string) (*in resp, err := c.instance.ListImages(&instance.ListImagesRequest{ Zone: zone, Project: &c.projectID, - Name: scw.StringPtr(name), - Public: scw.BoolPtr(false), + Name: ptr.To(name), + Public: ptr.To(false), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { return nil, newCallError("ListImages", err) diff --git a/internal/service/scaleway/client/instance_test.go b/internal/service/scaleway/client/instance_test.go index c8ae1c7..1f05aa3 100644 --- a/internal/service/scaleway/client/instance_test.go +++ b/internal/service/scaleway/client/instance_test.go @@ -8,10 +8,12 @@ import ( "strings" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -61,7 +63,7 @@ func TestClient_FindServer(t *testing.T) { d.ListServers(&instance.ListServersRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - Project: scw.StringPtr(projectID), + Project: ptr.To(projectID), }, gomock.Any()).Return(&instance.ListServersResponse{}, nil) }, }, @@ -80,7 +82,7 @@ func TestClient_FindServer(t *testing.T) { d.ListServers(&instance.ListServersRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - Project: scw.StringPtr(projectID), + Project: ptr.To(projectID), }, gomock.Any()).Return(&instance.ListServersResponse{ TotalCount: 1, Servers: []*instance.Server{ @@ -111,7 +113,7 @@ func TestClient_FindServer(t *testing.T) { d.ListServers(&instance.ListServersRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - Project: scw.StringPtr(projectID), + Project: ptr.To(projectID), }, gomock.Any()).Return(&instance.ListServersResponse{ TotalCount: 2, Servers: []*instance.Server{ @@ -198,8 +200,8 @@ func TestClient_CreateServer(t *testing.T) { name: "server", commercialType: "DEV1-S", imageID: imageID, - placementGroupID: scw.StringPtr(placementGroupID), - securityGroupID: scw.StringPtr(securityGroupID), + placementGroupID: ptr.To(placementGroupID), + securityGroupID: ptr.To(securityGroupID), rootVolumeSize: rootVolumeSize, rootVolumeType: instance.VolumeVolumeTypeBSSD, tags: []string{"tag1", "tag2", "tag3"}, @@ -214,15 +216,15 @@ func TestClient_CreateServer(t *testing.T) { Zone: scw.ZoneFrPar1, Name: "server", CommercialType: "DEV1-S", - DynamicIPRequired: scw.BoolPtr(false), - Image: scw.StringPtr(imageID), - PlacementGroup: scw.StringPtr(placementGroupID), - SecurityGroup: scw.StringPtr(securityGroupID), + DynamicIPRequired: ptr.To(false), + Image: ptr.To(imageID), + PlacementGroup: ptr.To(placementGroupID), + SecurityGroup: ptr.To(securityGroupID), Volumes: map[string]*instance.VolumeServerTemplate{ "0": { - Size: scw.SizePtr(rootVolumeSize), + Size: ptr.To(rootVolumeSize), VolumeType: instance.VolumeVolumeTypeBSSD, - Boot: scw.BoolPtr(true), + Boot: ptr.To(true), }, }, Tags: []string{"tag1", "tag2", "tag3", createdByTag}, @@ -248,8 +250,8 @@ func TestClient_CreateServer(t *testing.T) { name: "server", commercialType: "H100-1-80G", imageID: imageID, - placementGroupID: scw.StringPtr(placementGroupID), - securityGroupID: scw.StringPtr(securityGroupID), + placementGroupID: ptr.To(placementGroupID), + securityGroupID: ptr.To(securityGroupID), rootVolumeSize: rootVolumeSize, rootVolumeType: instance.VolumeVolumeTypeBSSD, tags: []string{"tag1", "tag2", "tag3"}, @@ -259,26 +261,26 @@ func TestClient_CreateServer(t *testing.T) { Zone: scw.ZoneFrPar2, Name: "H100-1-80G", }).Return(&instance.ServerType{ - ScratchStorageMaxSize: scw.SizePtr(scratchVolumeSize), + ScratchStorageMaxSize: ptr.To(scratchVolumeSize), }, nil) d.CreateServer(&instance.CreateServerRequest{ Zone: scw.ZoneFrPar2, Name: "server", CommercialType: "H100-1-80G", - DynamicIPRequired: scw.BoolPtr(false), - Image: scw.StringPtr(imageID), - PlacementGroup: scw.StringPtr(placementGroupID), - SecurityGroup: scw.StringPtr(securityGroupID), + DynamicIPRequired: ptr.To(false), + Image: ptr.To(imageID), + PlacementGroup: ptr.To(placementGroupID), + SecurityGroup: ptr.To(securityGroupID), Volumes: map[string]*instance.VolumeServerTemplate{ "0": { - Size: scw.SizePtr(rootVolumeSize), + Size: ptr.To(rootVolumeSize), VolumeType: instance.VolumeVolumeTypeBSSD, - Boot: scw.BoolPtr(true), + Boot: ptr.To(true), }, "1": { - Name: scw.StringPtr("server-scratch"), - Size: scw.SizePtr(scratchVolumeSize), + Name: ptr.To("server-scratch"), + Size: ptr.To(scratchVolumeSize), VolumeType: instance.VolumeVolumeTypeScratch, }, }, @@ -359,9 +361,9 @@ func TestClient_FindImage(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListImages(&instance.ListImagesRequest{ Zone: scw.ZoneFrPar1, - Project: scw.StringPtr(projectID), - Name: scw.StringPtr("my-image"), - Public: scw.BoolPtr(false), + Project: ptr.To(projectID), + Name: ptr.To("my-image"), + Public: ptr.To(false), }, gomock.Any()).Return(&instance.ListImagesResponse{}, nil) }, }, @@ -379,9 +381,9 @@ func TestClient_FindImage(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListImages(&instance.ListImagesRequest{ Zone: scw.ZoneFrPar1, - Project: scw.StringPtr(projectID), - Name: scw.StringPtr("my-image"), - Public: scw.BoolPtr(false), + Project: ptr.To(projectID), + Name: ptr.To("my-image"), + Public: ptr.To(false), }, gomock.Any()).Return(&instance.ListImagesResponse{ TotalCount: 1, Images: []*instance.Image{ @@ -409,9 +411,9 @@ func TestClient_FindImage(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListImages(&instance.ListImagesRequest{ Zone: scw.ZoneFrPar1, - Project: scw.StringPtr(projectID), - Name: scw.StringPtr("my-image"), - Public: scw.BoolPtr(false), + Project: ptr.To(projectID), + Name: ptr.To("my-image"), + Public: ptr.To(false), }, gomock.Any()).Return(&instance.ListImagesResponse{ TotalCount: 2, Images: []*instance.Image{ @@ -491,7 +493,7 @@ func TestClient_FindIPs(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListIPs(&instance.ListIPsRequest{ Zone: scw.ZoneFrPar1, - Project: scw.StringPtr(projectID), + Project: ptr.To(projectID), Tags: []string{"tag1", "tag2", "tag3"}, }, gomock.Any(), gomock.Any()).Return(&instance.ListIPsResponse{}, nil) }, @@ -510,7 +512,7 @@ func TestClient_FindIPs(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListIPs(&instance.ListIPsRequest{ Zone: scw.ZoneFrPar1, - Project: scw.StringPtr(projectID), + Project: ptr.To(projectID), Tags: []string{"tag1", "tag2", "tag3"}, }, gomock.Any(), gomock.Any()).Return(&instance.ListIPsResponse{ TotalCount: 2, @@ -1499,8 +1501,8 @@ func TestClient_FindPlacementGroup(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListPlacementGroups(&instance.ListPlacementGroupsRequest{ Zone: scw.ZoneFrPar1, - Name: scw.StringPtr("test-placement-group"), - Project: scw.StringPtr(projectID), + Name: ptr.To("test-placement-group"), + Project: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&instance.ListPlacementGroupsResponse{}, nil) }, }, @@ -1522,8 +1524,8 @@ func TestClient_FindPlacementGroup(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListPlacementGroups(&instance.ListPlacementGroupsRequest{ Zone: scw.ZoneFrPar1, - Name: scw.StringPtr("test-placement-group"), - Project: scw.StringPtr(projectID), + Name: ptr.To("test-placement-group"), + Project: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&instance.ListPlacementGroupsResponse{ TotalCount: 1, PlacementGroups: []*instance.PlacementGroup{ @@ -1550,8 +1552,8 @@ func TestClient_FindPlacementGroup(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListPlacementGroups(&instance.ListPlacementGroupsRequest{ Zone: scw.ZoneFrPar1, - Name: scw.StringPtr("test-placement-group"), - Project: scw.StringPtr(projectID), + Name: ptr.To("test-placement-group"), + Project: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&instance.ListPlacementGroupsResponse{ TotalCount: 2, PlacementGroups: []*instance.PlacementGroup{ @@ -1631,8 +1633,8 @@ func TestClient_FindSecurityGroup(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListSecurityGroups(&instance.ListSecurityGroupsRequest{ Zone: scw.ZoneFrPar1, - Name: scw.StringPtr("test-security-group"), - Project: scw.StringPtr(projectID), + Name: ptr.To("test-security-group"), + Project: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&instance.ListSecurityGroupsResponse{}, nil) }, }, @@ -1654,8 +1656,8 @@ func TestClient_FindSecurityGroup(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListSecurityGroups(&instance.ListSecurityGroupsRequest{ Zone: scw.ZoneFrPar1, - Name: scw.StringPtr("test-security-group"), - Project: scw.StringPtr(projectID), + Name: ptr.To("test-security-group"), + Project: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&instance.ListSecurityGroupsResponse{ TotalCount: 1, SecurityGroups: []*instance.SecurityGroup{ @@ -1682,8 +1684,8 @@ func TestClient_FindSecurityGroup(t *testing.T) { expect: func(d *mock_client.MockInstanceAPIMockRecorder) { d.ListSecurityGroups(&instance.ListSecurityGroupsRequest{ Zone: scw.ZoneFrPar1, - Name: scw.StringPtr("test-security-group"), - Project: scw.StringPtr(projectID), + Name: ptr.To("test-security-group"), + Project: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&instance.ListSecurityGroupsResponse{ TotalCount: 2, SecurityGroups: []*instance.SecurityGroup{ diff --git a/internal/service/scaleway/client/ipam.go b/internal/service/scaleway/client/ipam.go index 0c1181f..e787d32 100644 --- a/internal/service/scaleway/client/ipam.go +++ b/internal/service/scaleway/client/ipam.go @@ -5,6 +5,7 @@ import ( "github.com/scaleway/scaleway-sdk-go/api/ipam/v1" "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/utils/ptr" ) type IPAMAPI interface { @@ -24,7 +25,7 @@ func (c *Client) FindPrivateNICIPs(ctx context.Context, privateNICID string) ([] ProjectID: &c.projectID, ResourceType: ipam.ResourceTypeInstancePrivateNic, ResourceID: &privateNICID, - IsIPv6: scw.BoolPtr(false), + IsIPv6: ptr.To(false), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { return nil, newCallError("ListIPs", err) @@ -39,7 +40,7 @@ func (c *Client) FindLBServersIPs(ctx context.Context, privateNetworkID string, ResourceType: ipam.ResourceTypeLBServer, ResourceIDs: lbIDs, PrivateNetworkID: &privateNetworkID, - IsIPv6: scw.BoolPtr(false), + IsIPv6: ptr.To(false), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { return nil, newCallError("ListIPs", err) @@ -52,8 +53,8 @@ func (c *Client) FindAvailableIPs(ctx context.Context, privateNetworkID string) ips, err := c.ipam.ListIPs(&ipam.ListIPsRequest{ ProjectID: &c.projectID, PrivateNetworkID: &privateNetworkID, - IsIPv6: scw.BoolPtr(false), - Attached: scw.BoolPtr(false), + IsIPv6: ptr.To(false), + Attached: ptr.To(false), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { return nil, newCallError("ListIPs", err) @@ -66,7 +67,7 @@ func (c *Client) CleanAvailableIPs(ctx context.Context, privateNetworkID string) resp, err := c.ipam.ListIPs(&ipam.ListIPsRequest{ ProjectID: &c.projectID, PrivateNetworkID: &privateNetworkID, - Attached: scw.BoolPtr(false), + Attached: ptr.To(false), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { return newCallError("ListIPs", err) diff --git a/internal/service/scaleway/client/ipam_test.go b/internal/service/scaleway/client/ipam_test.go index 76e7acd..d85ba05 100644 --- a/internal/service/scaleway/client/ipam_test.go +++ b/internal/service/scaleway/client/ipam_test.go @@ -6,10 +6,12 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/ipam/v1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -52,10 +54,10 @@ func TestClient_FindPrivateNICIPs(t *testing.T) { }, expect: func(d *mock_client.MockIPAMAPIMockRecorder) { d.ListIPs(&ipam.ListIPsRequest{ - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), ResourceType: ipam.ResourceTypeInstancePrivateNic, - ResourceID: scw.StringPtr(privateNICID), - IsIPv6: scw.BoolPtr(false), + ResourceID: ptr.To(privateNICID), + IsIPv6: ptr.To(false), }, gomock.Any(), gomock.Any()).Return(&ipam.ListIPsResponse{ TotalCount: 2, IPs: []*ipam.IP{ @@ -129,11 +131,11 @@ func TestClient_FindLBServersIPs(t *testing.T) { }, expect: func(d *mock_client.MockIPAMAPIMockRecorder) { d.ListIPs(&ipam.ListIPsRequest{ - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), ResourceType: ipam.ResourceTypeLBServer, ResourceIDs: []string{lbID}, - PrivateNetworkID: scw.StringPtr(privateNetworkID), - IsIPv6: scw.BoolPtr(false), + PrivateNetworkID: ptr.To(privateNetworkID), + IsIPv6: ptr.To(false), }, gomock.Any(), gomock.Any()).Return(&ipam.ListIPsResponse{ TotalCount: 1, IPs: []*ipam.IP{ @@ -205,10 +207,10 @@ func TestClient_FindAvailableIPs(t *testing.T) { }, expect: func(d *mock_client.MockIPAMAPIMockRecorder) { d.ListIPs(&ipam.ListIPsRequest{ - ProjectID: scw.StringPtr(projectID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), - IsIPv6: scw.BoolPtr(false), - Attached: scw.BoolPtr(false), + ProjectID: ptr.To(projectID), + PrivateNetworkID: ptr.To(privateNetworkID), + IsIPv6: ptr.To(false), + Attached: ptr.To(false), }, gomock.Any(), gomock.Any()).Return(&ipam.ListIPsResponse{ TotalCount: 2, IPs: []*ipam.IP{ @@ -276,9 +278,9 @@ func TestClient_CleanAvailableIPs(t *testing.T) { }, expect: func(d *mock_client.MockIPAMAPIMockRecorder) { d.ListIPs(&ipam.ListIPsRequest{ - ProjectID: scw.StringPtr(projectID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), - Attached: scw.BoolPtr(false), + ProjectID: ptr.To(projectID), + PrivateNetworkID: ptr.To(privateNetworkID), + Attached: ptr.To(false), }, gomock.Any(), gomock.Any()).Return(&ipam.ListIPsResponse{ TotalCount: 2, IPs: []*ipam.IP{ diff --git a/internal/service/scaleway/client/k8s.go b/internal/service/scaleway/client/k8s.go index bee3ce3..05c3f39 100644 --- a/internal/service/scaleway/client/k8s.go +++ b/internal/service/scaleway/client/k8s.go @@ -7,6 +7,7 @@ import ( "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/utils/ptr" ) type K8sAPI interface { @@ -220,7 +221,7 @@ func (c *Client) SetClusterType(ctx context.Context, id, clusterType string) err func (c *Client) FindPool(ctx context.Context, clusterID, name string) (*k8s.Pool, error) { resp, err := c.k8s.ListPools(&k8s.ListPoolsRequest{ ClusterID: clusterID, - Name: scw.StringPtr(name), + Name: ptr.To(name), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { return nil, newCallError("ListPools", err) @@ -259,7 +260,7 @@ func (c *Client) CreatePool( ) (*k8s.Pool, error) { var rootVolumeSize *scw.Size if rootVolumeSizeGB != nil { - rootVolumeSize = scw.SizePtr(scw.Size(*rootVolumeSizeGB) * scw.GB) + rootVolumeSize = ptr.To(scw.Size(*rootVolumeSizeGB) * scw.GB) } pool, err := c.k8s.CreatePool(&k8s.CreatePoolRequest{ diff --git a/internal/service/scaleway/client/k8s_test.go b/internal/service/scaleway/client/k8s_test.go index 04f33c7..0964e18 100644 --- a/internal/service/scaleway/client/k8s_test.go +++ b/internal/service/scaleway/client/k8s_test.go @@ -6,10 +6,12 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -51,8 +53,8 @@ func TestClient_FindCluster(t *testing.T) { }, expect: func(d *mock_client.MockK8sAPIMockRecorder) { d.ListClusters(&k8s.ListClustersRequest{ - ProjectID: scw.StringPtr(projectID), - Name: scw.StringPtr("mycluster"), + ProjectID: ptr.To(projectID), + Name: ptr.To("mycluster"), }, gomock.Any()).Return(&k8s.ListClustersResponse{ TotalCount: 1, Clusters: []*k8s.Cluster{ @@ -76,8 +78,8 @@ func TestClient_FindCluster(t *testing.T) { wantErr: true, expect: func(d *mock_client.MockK8sAPIMockRecorder) { d.ListClusters(&k8s.ListClustersRequest{ - ProjectID: scw.StringPtr(projectID), - Name: scw.StringPtr("mycluster"), + ProjectID: ptr.To(projectID), + Name: ptr.To("mycluster"), }, gomock.Any(), gomock.Any()).Return(&k8s.ListClustersResponse{}, nil) }, }, @@ -142,21 +144,21 @@ func TestClient_CreateCluster(t *testing.T) { name: "test", clusterType: "kapsule", version: "1.30.4", - pnID: scw.StringPtr(privateNetworkID), + pnID: ptr.To(privateNetworkID), tags: []string{"tag1", "tag2"}, featureGates: []string{"HPAScaleToZero"}, admissionPlugins: []string{"AlwaysPullImages"}, apiServerCertSANs: []string{"my-cluster.test"}, cni: k8s.CNICilium, autoscalerConfig: &k8s.CreateClusterRequestAutoscalerConfig{ - ScaleDownDisabled: scw.BoolPtr(false), - ScaleDownDelayAfterAdd: scw.StringPtr("1m"), + ScaleDownDisabled: ptr.To(false), + ScaleDownDelayAfterAdd: ptr.To("1m"), Estimator: k8s.AutoscalerEstimatorBinpacking, Expander: k8s.AutoscalerExpanderMostPods, - IgnoreDaemonsetsUtilization: scw.BoolPtr(true), - BalanceSimilarNodeGroups: scw.BoolPtr(true), + IgnoreDaemonsetsUtilization: ptr.To(true), + BalanceSimilarNodeGroups: ptr.To(true), ExpendablePodsPriorityCutoff: scw.Int32Ptr(1), - ScaleDownUnneededTime: scw.StringPtr("1m"), + ScaleDownUnneededTime: ptr.To("1m"), ScaleDownUtilizationThreshold: scw.Float32Ptr(1), MaxGracefulTerminationSec: scw.Uint32Ptr(30), }, @@ -170,10 +172,10 @@ func TestClient_CreateCluster(t *testing.T) { openIDConnectConfig: &k8s.CreateClusterRequestOpenIDConnectConfig{ IssuerURL: "http://oidcprovider.test", ClientID: "abcd", - UsernameClaim: scw.StringPtr("username"), - UsernamePrefix: scw.StringPtr("usernameprefix"), + UsernameClaim: ptr.To("username"), + UsernamePrefix: ptr.To("usernameprefix"), GroupsClaim: &[]string{"groups"}, - GroupsPrefix: scw.StringPtr("groupsprefix"), + GroupsPrefix: ptr.To("groupsprefix"), RequiredClaim: &[]string{"verified"}, }, podCIDR: scw.IPNet{IPNet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 0, 0)}}, @@ -192,14 +194,14 @@ func TestClient_CreateCluster(t *testing.T) { Version: "1.30.4", Cni: k8s.CNICilium, AutoscalerConfig: &k8s.CreateClusterRequestAutoscalerConfig{ - ScaleDownDisabled: scw.BoolPtr(false), - ScaleDownDelayAfterAdd: scw.StringPtr("1m"), + ScaleDownDisabled: ptr.To(false), + ScaleDownDelayAfterAdd: ptr.To("1m"), Estimator: k8s.AutoscalerEstimatorBinpacking, Expander: k8s.AutoscalerExpanderMostPods, - IgnoreDaemonsetsUtilization: scw.BoolPtr(true), - BalanceSimilarNodeGroups: scw.BoolPtr(true), + IgnoreDaemonsetsUtilization: ptr.To(true), + BalanceSimilarNodeGroups: ptr.To(true), ExpendablePodsPriorityCutoff: scw.Int32Ptr(1), - ScaleDownUnneededTime: scw.StringPtr("1m"), + ScaleDownUnneededTime: ptr.To("1m"), ScaleDownUtilizationThreshold: scw.Float32Ptr(1), MaxGracefulTerminationSec: scw.Uint32Ptr(30), }, @@ -215,14 +217,14 @@ func TestClient_CreateCluster(t *testing.T) { OpenIDConnectConfig: &k8s.CreateClusterRequestOpenIDConnectConfig{ IssuerURL: "http://oidcprovider.test", ClientID: "abcd", - UsernameClaim: scw.StringPtr("username"), - UsernamePrefix: scw.StringPtr("usernameprefix"), + UsernameClaim: ptr.To("username"), + UsernamePrefix: ptr.To("usernameprefix"), GroupsClaim: &[]string{"groups"}, - GroupsPrefix: scw.StringPtr("groupsprefix"), + GroupsPrefix: ptr.To("groupsprefix"), RequiredClaim: &[]string{"verified"}, }, ApiserverCertSans: []string{"my-cluster.test"}, - PrivateNetworkID: scw.StringPtr(privateNetworkID), + PrivateNetworkID: ptr.To(privateNetworkID), PodCidr: &scw.IPNet{IPNet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 0, 0)}}, ServiceCidr: &scw.IPNet{IPNet: net.IPNet{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 0, 0, 0)}}, }, gomock.Any()).Return(&k8s.Cluster{ @@ -391,31 +393,31 @@ func TestClient_UpdateCluster(t *testing.T) { admissionPlugins: &[]string{"AlwaysPullImages"}, apiServerCertSANs: &[]string{"mycluster.test"}, autoscalerConfig: &k8s.UpdateClusterRequestAutoscalerConfig{ - ScaleDownDisabled: scw.BoolPtr(false), - ScaleDownDelayAfterAdd: scw.StringPtr("1m"), + ScaleDownDisabled: ptr.To(false), + ScaleDownDelayAfterAdd: ptr.To("1m"), Estimator: k8s.AutoscalerEstimatorBinpacking, Expander: k8s.AutoscalerExpanderMostPods, - IgnoreDaemonsetsUtilization: scw.BoolPtr(true), - BalanceSimilarNodeGroups: scw.BoolPtr(true), + IgnoreDaemonsetsUtilization: ptr.To(true), + BalanceSimilarNodeGroups: ptr.To(true), ExpendablePodsPriorityCutoff: scw.Int32Ptr(1), - ScaleDownUnneededTime: scw.StringPtr("1m"), + ScaleDownUnneededTime: ptr.To("1m"), ScaleDownUtilizationThreshold: scw.Float32Ptr(1), MaxGracefulTerminationSec: scw.Uint32Ptr(30), }, autoUpgrade: &k8s.UpdateClusterRequestAutoUpgrade{ - Enable: scw.BoolPtr(true), + Enable: ptr.To(true), MaintenanceWindow: &k8s.MaintenanceWindow{ StartHour: 1, Day: k8s.MaintenanceWindowDayOfTheWeekFriday, }, }, openIDConnectConfig: &k8s.UpdateClusterRequestOpenIDConnectConfig{ - IssuerURL: scw.StringPtr("http://oidcprovider.test"), - ClientID: scw.StringPtr("abcd"), - UsernameClaim: scw.StringPtr("username"), - UsernamePrefix: scw.StringPtr("usernameprefix"), + IssuerURL: ptr.To("http://oidcprovider.test"), + ClientID: ptr.To("abcd"), + UsernameClaim: ptr.To("username"), + UsernamePrefix: ptr.To("usernameprefix"), GroupsClaim: &[]string{"groups"}, - GroupsPrefix: scw.StringPtr("groupsprefix"), + GroupsPrefix: ptr.To("groupsprefix"), RequiredClaim: &[]string{"verified"}, }, }, @@ -424,19 +426,19 @@ func TestClient_UpdateCluster(t *testing.T) { ClusterID: clusterID, Tags: &[]string{"tag1", "tag2", createdByTag}, AutoscalerConfig: &k8s.UpdateClusterRequestAutoscalerConfig{ - ScaleDownDisabled: scw.BoolPtr(false), - ScaleDownDelayAfterAdd: scw.StringPtr("1m"), + ScaleDownDisabled: ptr.To(false), + ScaleDownDelayAfterAdd: ptr.To("1m"), Estimator: k8s.AutoscalerEstimatorBinpacking, Expander: k8s.AutoscalerExpanderMostPods, - IgnoreDaemonsetsUtilization: scw.BoolPtr(true), - BalanceSimilarNodeGroups: scw.BoolPtr(true), + IgnoreDaemonsetsUtilization: ptr.To(true), + BalanceSimilarNodeGroups: ptr.To(true), ExpendablePodsPriorityCutoff: scw.Int32Ptr(1), - ScaleDownUnneededTime: scw.StringPtr("1m"), + ScaleDownUnneededTime: ptr.To("1m"), ScaleDownUtilizationThreshold: scw.Float32Ptr(1), MaxGracefulTerminationSec: scw.Uint32Ptr(30), }, AutoUpgrade: &k8s.UpdateClusterRequestAutoUpgrade{ - Enable: scw.BoolPtr(true), + Enable: ptr.To(true), MaintenanceWindow: &k8s.MaintenanceWindow{ StartHour: 1, Day: k8s.MaintenanceWindowDayOfTheWeekFriday, @@ -446,12 +448,12 @@ func TestClient_UpdateCluster(t *testing.T) { AdmissionPlugins: &[]string{"AlwaysPullImages"}, ApiserverCertSans: &[]string{"mycluster.test"}, OpenIDConnectConfig: &k8s.UpdateClusterRequestOpenIDConnectConfig{ - IssuerURL: scw.StringPtr("http://oidcprovider.test"), - ClientID: scw.StringPtr("abcd"), - UsernameClaim: scw.StringPtr("username"), - UsernamePrefix: scw.StringPtr("usernameprefix"), + IssuerURL: ptr.To("http://oidcprovider.test"), + ClientID: ptr.To("abcd"), + UsernameClaim: ptr.To("username"), + UsernamePrefix: ptr.To("usernameprefix"), GroupsClaim: &[]string{"groups"}, - GroupsPrefix: scw.StringPtr("groupsprefix"), + GroupsPrefix: ptr.To("groupsprefix"), RequiredClaim: &[]string{"verified"}, }, }, gomock.Any()).Return(&k8s.Cluster{ @@ -608,7 +610,7 @@ func TestClient_FindPool(t *testing.T) { expect: func(d *mock_client.MockK8sAPIMockRecorder) { d.ListPools(&k8s.ListPoolsRequest{ ClusterID: clusterID, - Name: scw.StringPtr("mypool"), + Name: ptr.To("mypool"), }, gomock.Any(), gomock.Any()).Return(&k8s.ListPoolsResponse{ TotalCount: 1, Pools: []*k8s.Pool{ @@ -631,7 +633,7 @@ func TestClient_FindPool(t *testing.T) { expect: func(d *mock_client.MockK8sAPIMockRecorder) { d.ListPools(&k8s.ListPoolsRequest{ ClusterID: clusterID, - Name: scw.StringPtr("mypool"), + Name: ptr.To("mypool"), }, gomock.Any(), gomock.Any()).Return(&k8s.ListPoolsResponse{}, nil) }, }, @@ -699,8 +701,8 @@ func TestClient_CreatePool(t *testing.T) { clusterID: clusterID, name: "mypool", nodeType: "DEV1-S", - placementGroupID: scw.StringPtr(placementGroupID), - securityGroupID: scw.StringPtr(securityGroupID), + placementGroupID: ptr.To(placementGroupID), + securityGroupID: ptr.To(securityGroupID), autoscaling: true, autohealing: true, publicIPDisabled: true, @@ -728,7 +730,7 @@ func TestClient_CreatePool(t *testing.T) { ClusterID: clusterID, Name: "mypool", NodeType: "DEV1-S", - PlacementGroupID: scw.StringPtr(placementGroupID), + PlacementGroupID: ptr.To(placementGroupID), Autoscaling: true, Size: 1, MinSize: scw.Uint32Ptr(1), @@ -745,9 +747,9 @@ func TestClient_CreatePool(t *testing.T) { }, Zone: scw.ZoneFrPar1, RootVolumeType: k8s.PoolVolumeTypeBSSD, - RootVolumeSize: scw.SizePtr(30 * scw.GB), + RootVolumeSize: ptr.To(30 * scw.GB), PublicIPDisabled: true, - SecurityGroupID: scw.StringPtr(securityGroupID), + SecurityGroupID: ptr.To(securityGroupID), }, gomock.Any()).Return(&k8s.Pool{ ID: poolID, Name: "mypool", @@ -806,8 +808,8 @@ func TestClient_UpdatePool(t *testing.T) { args: args{ ctx: context.TODO(), id: poolID, - autoscaling: scw.BoolPtr(true), - autohealing: scw.BoolPtr(true), + autoscaling: ptr.To(true), + autohealing: ptr.To(true), size: scw.Uint32Ptr(1), minSize: scw.Uint32Ptr(1), maxSize: scw.Uint32Ptr(5), @@ -824,8 +826,8 @@ func TestClient_UpdatePool(t *testing.T) { expect: func(d *mock_client.MockK8sAPIMockRecorder) { d.UpdatePool(&k8s.UpdatePoolRequest{ PoolID: poolID, - Autoscaling: scw.BoolPtr(true), - Autohealing: scw.BoolPtr(true), + Autoscaling: ptr.To(true), + Autohealing: ptr.To(true), Size: scw.Uint32Ptr(1), MinSize: scw.Uint32Ptr(1), MaxSize: scw.Uint32Ptr(5), @@ -990,7 +992,7 @@ func TestClient_ListNodes(t *testing.T) { expect: func(d *mock_client.MockK8sAPIMockRecorder) { d.ListNodes(&k8s.ListNodesRequest{ ClusterID: clusterID, - PoolID: scw.StringPtr(poolID), + PoolID: ptr.To(poolID), }, gomock.Any(), gomock.Any()).Return(&k8s.ListNodesResponse{ TotalCount: 2, Nodes: []*k8s.Node{ @@ -1121,7 +1123,7 @@ func TestClient_SetClusterACLRules(t *testing.T) { clusterID: clusterID, rules: []*k8s.ACLRuleRequest{ { - ScalewayRanges: scw.BoolPtr(true), + ScalewayRanges: ptr.To(true), }, { IP: &scw.IPNet{IPNet: net.IPNet{IP: net.IPv4(0, 0, 0, 0), Mask: net.IPv4Mask(0, 0, 0, 0)}}, @@ -1133,7 +1135,7 @@ func TestClient_SetClusterACLRules(t *testing.T) { ClusterID: clusterID, ACLs: []*k8s.ACLRuleRequest{ { - ScalewayRanges: scw.BoolPtr(true), + ScalewayRanges: ptr.To(true), Description: createdByDescription, }, { diff --git a/internal/service/scaleway/client/lb.go b/internal/service/scaleway/client/lb.go index 9db61ba..d5b1965 100644 --- a/internal/service/scaleway/client/lb.go +++ b/internal/service/scaleway/client/lb.go @@ -8,6 +8,7 @@ import ( "github.com/scaleway/scaleway-sdk-go/api/lb/v1" "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/utils/ptr" ) type LBAPI interface { @@ -191,16 +192,16 @@ func (c *Client) CreateLB( Type: strings.ToLower(lbType), Tags: append(tags, createdByTag), Description: createdByDescription, - AssignFlexibleIPv6: scw.BoolPtr(false), + AssignFlexibleIPv6: ptr.To(false), } if private { - params.AssignFlexibleIP = scw.BoolPtr(false) + params.AssignFlexibleIP = ptr.To(false) } else { if ipID != nil { params.IPIDs = []string{*ipID} } - params.AssignFlexibleIP = scw.BoolPtr(ipID == nil) + params.AssignFlexibleIP = ptr.To(ipID == nil) } loadbalancer, err := c.lb.CreateLB(params, scw.WithContext(ctx)) diff --git a/internal/service/scaleway/client/lb_test.go b/internal/service/scaleway/client/lb_test.go index 5600882..65f9ba1 100644 --- a/internal/service/scaleway/client/lb_test.go +++ b/internal/service/scaleway/client/lb_test.go @@ -5,10 +5,12 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/lb/v1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -67,7 +69,7 @@ func TestClient_FindLB(t *testing.T) { l.ListLBs(&lb.ZonedAPIListLBsRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&lb.ListLBsResponse{ LBs: []*lb.LB{}, }, nil) @@ -93,7 +95,7 @@ func TestClient_FindLB(t *testing.T) { l.ListLBs(&lb.ZonedAPIListLBsRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&lb.ListLBsResponse{ LBs: []*lb.LB{ { @@ -121,7 +123,7 @@ func TestClient_FindLB(t *testing.T) { l.ListLBs(&lb.ZonedAPIListLBsRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&lb.ListLBsResponse{ LBs: []*lb.LB{ { @@ -283,8 +285,8 @@ func TestClient_FindLBIP(t *testing.T) { expect: func(l *mock_client.MockLBAPIMockRecorder) { l.ListIPs(&lb.ZonedAPIListIPsRequest{ Zone: scw.ZoneFrPar1, - ProjectID: scw.StringPtr(projectID), - IPAddress: scw.StringPtr("42.42.42.42"), + ProjectID: ptr.To(projectID), + IPAddress: ptr.To("42.42.42.42"), }, gomock.Any()).Return(&lb.ListIPsResponse{ IPs: []*lb.IP{}, }, nil) @@ -307,8 +309,8 @@ func TestClient_FindLBIP(t *testing.T) { expect: func(l *mock_client.MockLBAPIMockRecorder) { l.ListIPs(&lb.ZonedAPIListIPsRequest{ Zone: scw.ZoneFrPar1, - ProjectID: scw.StringPtr(projectID), - IPAddress: scw.StringPtr("42.42.42.42"), + ProjectID: ptr.To(projectID), + IPAddress: ptr.To("42.42.42.42"), }, gomock.Any()).Return(&lb.ListIPsResponse{ IPs: []*lb.IP{{IPAddress: "42.42.42.42"}}, TotalCount: 1, @@ -381,7 +383,7 @@ func TestClient_CreateLB(t *testing.T) { zone: scw.ZoneFrPar1, name: "my-lb", lbType: "LB-GP-M", - ipID: scw.StringPtr(lbIPID), + ipID: ptr.To(lbIPID), tags: []string{"tag1", "tag2"}, }, want: &lb.LB{ @@ -399,8 +401,8 @@ func TestClient_CreateLB(t *testing.T) { IPIDs: []string{lbIPID}, Tags: []string{"tag1", "tag2", createdByTag}, Description: createdByDescription, - AssignFlexibleIP: scw.BoolPtr(false), - AssignFlexibleIPv6: scw.BoolPtr(false), + AssignFlexibleIP: ptr.To(false), + AssignFlexibleIPv6: ptr.To(false), }, gomock.Any()).Return(&lb.LB{ ID: lbID, Name: "my-lb", @@ -437,8 +439,8 @@ func TestClient_CreateLB(t *testing.T) { Type: "lb-gp-m", Tags: []string{"tag1", "tag2", createdByTag}, Description: createdByDescription, - AssignFlexibleIP: scw.BoolPtr(true), - AssignFlexibleIPv6: scw.BoolPtr(false), + AssignFlexibleIP: ptr.To(true), + AssignFlexibleIPv6: ptr.To(false), }, gomock.Any()).Return(&lb.LB{ ID: lbID, Name: "my-lb", @@ -579,7 +581,7 @@ func TestClient_FindLBs(t *testing.T) { l.ListLBs(&lb.ZonedAPIListLBsRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), }, gomock.Any(), gomock.Any(), gomock.Any()).Return(&lb.ListLBsResponse{ LBs: []*lb.LB{}, }, nil) @@ -602,7 +604,7 @@ func TestClient_FindLBs(t *testing.T) { l.ListLBs(&lb.ZonedAPIListLBsRequest{ Zone: scw.ZoneFrPar1, Tags: []string{"tag1", "tag2"}, - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), }, gomock.Any(), gomock.Any(), gomock.Any()).Return(&lb.ListLBsResponse{ TotalCount: 2, LBs: []*lb.LB{&lb1, &lb2}, @@ -675,7 +677,7 @@ func TestClient_FindBackend(t *testing.T) { l.ListBackends(&lb.ZonedAPIListBackendsRequest{ Zone: scw.ZoneFrPar1, LBID: lbID, - Name: scw.StringPtr("backend-name"), + Name: ptr.To("backend-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListBackendsResponse{ Backends: []*lb.Backend{}, }, nil) @@ -697,7 +699,7 @@ func TestClient_FindBackend(t *testing.T) { l.ListBackends(&lb.ZonedAPIListBackendsRequest{ Zone: scw.ZoneFrPar1, LBID: lbID, - Name: scw.StringPtr("backend-name"), + Name: ptr.To("backend-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListBackendsResponse{ Backends: []*lb.Backend{ { @@ -728,7 +730,7 @@ func TestClient_FindBackend(t *testing.T) { l.ListBackends(&lb.ZonedAPIListBackendsRequest{ Zone: scw.ZoneFrPar1, LBID: lbID, - Name: scw.StringPtr("backend-name"), + Name: ptr.To("backend-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListBackendsResponse{ Backends: []*lb.Backend{ {Name: "backend-name"}, @@ -981,7 +983,7 @@ func TestClient_FindFrontend(t *testing.T) { l.ListFrontends(&lb.ZonedAPIListFrontendsRequest{ Zone: scw.ZoneFrPar1, LBID: lbID, - Name: scw.StringPtr("frontend-name"), + Name: ptr.To("frontend-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListFrontendsResponse{ Frontends: []*lb.Frontend{}, }, nil) @@ -1007,7 +1009,7 @@ func TestClient_FindFrontend(t *testing.T) { l.ListFrontends(&lb.ZonedAPIListFrontendsRequest{ Zone: scw.ZoneFrPar1, LBID: lbID, - Name: scw.StringPtr("frontend-name"), + Name: ptr.To("frontend-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListFrontendsResponse{ Frontends: []*lb.Frontend{ { @@ -1035,7 +1037,7 @@ func TestClient_FindFrontend(t *testing.T) { l.ListFrontends(&lb.ZonedAPIListFrontendsRequest{ Zone: scw.ZoneFrPar1, LBID: lbID, - Name: scw.StringPtr("frontend-name"), + Name: ptr.To("frontend-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListFrontendsResponse{ Frontends: []*lb.Frontend{ {Name: "frontend-name"}, @@ -1531,7 +1533,7 @@ func TestClient_FindLBACLByName(t *testing.T) { l.ListACLs(&lb.ZonedAPIListACLsRequest{ Zone: scw.ZoneFrPar1, FrontendID: frontendID, - Name: scw.StringPtr("acl-name"), + Name: ptr.To("acl-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListACLResponse{ ACLs: []*lb.ACL{}, }, nil) @@ -1557,7 +1559,7 @@ func TestClient_FindLBACLByName(t *testing.T) { l.ListACLs(&lb.ZonedAPIListACLsRequest{ Zone: scw.ZoneFrPar1, FrontendID: frontendID, - Name: scw.StringPtr("acl-name"), + Name: ptr.To("acl-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListACLResponse{ TotalCount: 1, ACLs: []*lb.ACL{{ID: aclID, Name: "acl-name"}}, @@ -1581,7 +1583,7 @@ func TestClient_FindLBACLByName(t *testing.T) { l.ListACLs(&lb.ZonedAPIListACLsRequest{ Zone: scw.ZoneFrPar1, FrontendID: frontendID, - Name: scw.StringPtr("acl-name"), + Name: ptr.To("acl-name"), }, gomock.Any(), gomock.Any()).Return(&lb.ListACLResponse{ TotalCount: 1, ACLs: []*lb.ACL{ diff --git a/internal/service/scaleway/client/marketplace_test.go b/internal/service/scaleway/client/marketplace_test.go index c31c2bd..651a2ad 100644 --- a/internal/service/scaleway/client/marketplace_test.go +++ b/internal/service/scaleway/client/marketplace_test.go @@ -5,10 +5,11 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/marketplace/v2" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const marketplaceImageID = "11111111-1111-1111-1111-111111111111" diff --git a/internal/service/scaleway/client/mock_client/client_mock.go b/internal/service/scaleway/client/mock_client/client_mock.go index aa217d5..9f5f9d3 100644 --- a/internal/service/scaleway/client/mock_client/client_mock.go +++ b/internal/service/scaleway/client/mock_client/client_mock.go @@ -2254,7 +2254,7 @@ func (c *MockInterfaceGetSecretKeyCall) DoAndReturn(f func() string) *MockInterf } // GetZoneOrDefault mocks base method. -func (m *MockInterface) GetZoneOrDefault(zone *string) (scw.Zone, error) { +func (m *MockInterface) GetZoneOrDefault(zone string) (scw.Zone, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetZoneOrDefault", zone) ret0, _ := ret[0].(scw.Zone) @@ -2281,13 +2281,13 @@ func (c *MockInterfaceGetZoneOrDefaultCall) Return(arg0 scw.Zone, arg1 error) *M } // Do rewrite *gomock.Call.Do -func (c *MockInterfaceGetZoneOrDefaultCall) Do(f func(*string) (scw.Zone, error)) *MockInterfaceGetZoneOrDefaultCall { +func (c *MockInterfaceGetZoneOrDefaultCall) Do(f func(string) (scw.Zone, error)) *MockInterfaceGetZoneOrDefaultCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockInterfaceGetZoneOrDefaultCall) DoAndReturn(f func(*string) (scw.Zone, error)) *MockInterfaceGetZoneOrDefaultCall { +func (c *MockInterfaceGetZoneOrDefaultCall) DoAndReturn(f func(string) (scw.Zone, error)) *MockInterfaceGetZoneOrDefaultCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/internal/service/scaleway/client/mock_client/zones_mock.go b/internal/service/scaleway/client/mock_client/zones_mock.go index 51a2af3..b4629f5 100644 --- a/internal/service/scaleway/client/mock_client/zones_mock.go +++ b/internal/service/scaleway/client/mock_client/zones_mock.go @@ -117,7 +117,7 @@ func (c *MockZonesGetControlPlaneZonesCall) DoAndReturn(f func() []scw.Zone) *Mo } // GetZoneOrDefault mocks base method. -func (m *MockZones) GetZoneOrDefault(zone *string) (scw.Zone, error) { +func (m *MockZones) GetZoneOrDefault(zone string) (scw.Zone, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetZoneOrDefault", zone) ret0, _ := ret[0].(scw.Zone) @@ -144,13 +144,13 @@ func (c *MockZonesGetZoneOrDefaultCall) Return(arg0 scw.Zone, arg1 error) *MockZ } // Do rewrite *gomock.Call.Do -func (c *MockZonesGetZoneOrDefaultCall) Do(f func(*string) (scw.Zone, error)) *MockZonesGetZoneOrDefaultCall { +func (c *MockZonesGetZoneOrDefaultCall) Do(f func(string) (scw.Zone, error)) *MockZonesGetZoneOrDefaultCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockZonesGetZoneOrDefaultCall) DoAndReturn(f func(*string) (scw.Zone, error)) *MockZonesGetZoneOrDefaultCall { +func (c *MockZonesGetZoneOrDefaultCall) DoAndReturn(f func(string) (scw.Zone, error)) *MockZonesGetZoneOrDefaultCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/internal/service/scaleway/client/vpc_test.go b/internal/service/scaleway/client/vpc_test.go index 59065b4..9b72f5a 100644 --- a/internal/service/scaleway/client/vpc_test.go +++ b/internal/service/scaleway/client/vpc_test.go @@ -6,10 +6,12 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -45,14 +47,14 @@ func TestClient_FindPrivateNetwork(t *testing.T) { args: args{ ctx: context.TODO(), tags: []string{"tag1", "tag2", "tag3"}, - vpcID: scw.StringPtr(vpcID), + vpcID: ptr.To(vpcID), }, wantErr: true, expect: func(v *mock_client.MockVPCAPIMockRecorder) { v.ListPrivateNetworks(&vpc.ListPrivateNetworksRequest{ Tags: []string{"tag1", "tag2", "tag3"}, - ProjectID: scw.StringPtr(projectID), - VpcID: scw.StringPtr(vpcID), + ProjectID: ptr.To(projectID), + VpcID: ptr.To(vpcID), }, gomock.Any(), gomock.Any()).Return(&vpc.ListPrivateNetworksResponse{ PrivateNetworks: []*vpc.PrivateNetwork{}, }, nil) @@ -67,7 +69,7 @@ func TestClient_FindPrivateNetwork(t *testing.T) { args: args{ ctx: context.TODO(), tags: []string{"tag1", "tag2", "tag3"}, - vpcID: scw.StringPtr(vpcID), + vpcID: ptr.To(vpcID), }, want: &vpc.PrivateNetwork{ ID: privateNetworkID, @@ -76,8 +78,8 @@ func TestClient_FindPrivateNetwork(t *testing.T) { expect: func(v *mock_client.MockVPCAPIMockRecorder) { v.ListPrivateNetworks(&vpc.ListPrivateNetworksRequest{ Tags: []string{"tag1", "tag2", "tag3"}, - ProjectID: scw.StringPtr(projectID), - VpcID: scw.StringPtr(vpcID), + ProjectID: ptr.To(projectID), + VpcID: ptr.To(vpcID), }, gomock.Any(), gomock.Any()).Return(&vpc.ListPrivateNetworksResponse{ PrivateNetworks: []*vpc.PrivateNetwork{ { @@ -97,14 +99,14 @@ func TestClient_FindPrivateNetwork(t *testing.T) { args: args{ ctx: context.TODO(), tags: []string{"tag1", "tag2", "tag3"}, - vpcID: scw.StringPtr(vpcID), + vpcID: ptr.To(vpcID), }, wantErr: true, expect: func(v *mock_client.MockVPCAPIMockRecorder) { v.ListPrivateNetworks(&vpc.ListPrivateNetworksRequest{ Tags: []string{"tag1", "tag2", "tag3"}, - ProjectID: scw.StringPtr(projectID), - VpcID: scw.StringPtr(vpcID), + ProjectID: ptr.To(projectID), + VpcID: ptr.To(vpcID), }, gomock.Any(), gomock.Any()).Return(&vpc.ListPrivateNetworksResponse{ PrivateNetworks: []*vpc.PrivateNetwork{ { @@ -233,8 +235,8 @@ func TestClient_CreatePrivateNetwork(t *testing.T) { args: args{ ctx: context.TODO(), name: "privatenetwork", - vpcID: scw.StringPtr(vpcID), - subnet: scw.StringPtr("192.168.1.0/24"), + vpcID: ptr.To(vpcID), + subnet: ptr.To("192.168.1.0/24"), tags: []string{"tag1", "tag2"}, }, want: &vpc.PrivateNetwork{ @@ -249,7 +251,7 @@ func TestClient_CreatePrivateNetwork(t *testing.T) { v.CreatePrivateNetwork(&vpc.CreatePrivateNetworkRequest{ Name: "privatenetwork", - VpcID: scw.StringPtr(vpcID), + VpcID: ptr.To(vpcID), Tags: []string{"tag1", "tag2", createdByTag}, Subnets: []scw.IPNet{{IPNet: *ipNet}}, }, gomock.Any()).Return(&vpc.PrivateNetwork{ diff --git a/internal/service/scaleway/client/vpcgw.go b/internal/service/scaleway/client/vpcgw.go index 7ec436a..735e2e8 100644 --- a/internal/service/scaleway/client/vpcgw.go +++ b/internal/service/scaleway/client/vpcgw.go @@ -6,6 +6,7 @@ import ( "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/utils/ptr" ) type VPCGWAPI interface { @@ -81,7 +82,7 @@ func (c *Client) FindGatewayIP(ctx context.Context, zone scw.Zone, ip string) (* ips, err := c.vpcgw.ListIPs(&vpcgw.ListIPsRequest{ Zone: zone, - IsFree: scw.BoolPtr(true), + IsFree: ptr.To(true), ProjectID: &c.projectID, }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { diff --git a/internal/service/scaleway/client/vpcgw_test.go b/internal/service/scaleway/client/vpcgw_test.go index c52ee8e..bfdd4e1 100644 --- a/internal/service/scaleway/client/vpcgw_test.go +++ b/internal/service/scaleway/client/vpcgw_test.go @@ -6,10 +6,12 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const vpcgwID = "11111111-1111-1111-1111-111111111111" @@ -52,7 +54,7 @@ func TestClient_FindGateways(t *testing.T) { v.Zones() v.ListGateways(&vpcgw.ListGatewaysRequest{ Zone: scw.ZoneFrPar1, - ProjectID: scw.StringPtr(projectID), + ProjectID: ptr.To(projectID), Tags: []string{"tag1", "tag2"}, }, gomock.Any(), gomock.Any(), gomock.Any()).Return(&vpcgw.ListGatewaysResponse{ TotalCount: 1, @@ -196,8 +198,8 @@ func TestClient_FindGatewayIP(t *testing.T) { expect: func(v *mock_client.MockVPCGWAPIMockRecorder) { v.ListIPs(&vpcgw.ListIPsRequest{ Zone: scw.ZoneFrPar1, - IsFree: scw.BoolPtr(true), - ProjectID: scw.StringPtr(projectID), + IsFree: ptr.To(true), + ProjectID: ptr.To(projectID), }, gomock.Any(), gomock.Any()).Return(&vpcgw.ListIPsResponse{ TotalCount: 1, IPs: []*vpcgw.IP{ @@ -272,7 +274,7 @@ func TestClient_CreateGateway(t *testing.T) { name: "gateway", gwType: "VPC-GW-S", tags: []string{"tag1", "tag2"}, - ipID: scw.StringPtr(ipID), + ipID: ptr.To(ipID), }, want: &vpcgw.Gateway{ ID: vpcgwID, @@ -283,7 +285,7 @@ func TestClient_CreateGateway(t *testing.T) { Name: "gateway", Tags: []string{"tag1", "tag2", createdByTag}, Type: "VPC-GW-S", - IPID: scw.StringPtr(ipID), + IPID: ptr.To(ipID), }, gomock.Any()).Return(&vpcgw.Gateway{ ID: vpcgwID, }, nil) @@ -502,7 +504,7 @@ func TestClient_UpgradeGateway(t *testing.T) { v.UpgradeGateway(&vpcgw.UpgradeGatewayRequest{ Zone: scw.ZoneFrPar1, GatewayID: vpcgwID, - Type: scw.StringPtr("VPC-GW-M"), + Type: ptr.To("VPC-GW-M"), }, gomock.Any()).Return(&vpcgw.Gateway{ ID: vpcgwID, }, nil) diff --git a/internal/service/scaleway/client/zones.go b/internal/service/scaleway/client/zones.go index e212825..4327d84 100644 --- a/internal/service/scaleway/client/zones.go +++ b/internal/service/scaleway/client/zones.go @@ -4,25 +4,24 @@ import ( "fmt" "slices" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/scaleway-sdk-go/scw" ) type Zones interface { - GetZoneOrDefault(zone *string) (scw.Zone, error) + GetZoneOrDefault(zone string) (scw.Zone, error) DefaultZone() scw.Zone GetControlPlaneZones() []scw.Zone } -// GetZoneOrDefault dereferences and parses the provided zone, or returns the default zone. -func (c *Client) GetZoneOrDefault(zone *string) (scw.Zone, error) { - if zone == nil { +// GetZoneOrDefault parses the provided zone, or returns the default zone if empty. +func (c *Client) GetZoneOrDefault(zone string) (scw.Zone, error) { + if zone == "" { return c.DefaultZone(), nil } - providedZone, err := scw.ParseZone(*zone) + providedZone, err := scw.ParseZone(zone) if err != nil { - return "", scaleway.WithTerminalError(fmt.Errorf("zone %s is not valid: %w", *zone, err)) + return "", fmt.Errorf("zone %s is not valid: %w", zone, err) } return providedZone, nil @@ -61,7 +60,7 @@ func (c *Client) productZones(productAPI zonesGetter) []scw.Zone { func (c *Client) validateZone(productAPI zonesGetter, zone scw.Zone) error { zones := c.productZones(productAPI) if !slices.Contains(zones, zone) { - return scaleway.WithTerminalError(fmt.Errorf("zone %s must be one of the following zones (%s)", zone, zones)) + return fmt.Errorf("zone %s must be one of the following zones (%s)", zone, zones) } return nil diff --git a/internal/service/scaleway/client/zones_test.go b/internal/service/scaleway/client/zones_test.go index ffdd41a..607159d 100644 --- a/internal/service/scaleway/client/zones_test.go +++ b/internal/service/scaleway/client/zones_test.go @@ -4,9 +4,10 @@ import ( "reflect" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) func TestClient_GetZoneOrDefault(t *testing.T) { @@ -15,7 +16,7 @@ func TestClient_GetZoneOrDefault(t *testing.T) { region scw.Region } type args struct { - zone *string + zone string } tests := []struct { name string @@ -38,7 +39,7 @@ func TestClient_GetZoneOrDefault(t *testing.T) { region: scw.RegionFrPar, }, args: args{ - zone: scw.StringPtr("invalid-zone"), + zone: "invalid-zone", }, wantErr: true, }, @@ -48,7 +49,7 @@ func TestClient_GetZoneOrDefault(t *testing.T) { region: scw.RegionFrPar, }, args: args{ - zone: scw.StringPtr("fr-par-3"), + zone: "fr-par-3", }, want: scw.ZoneFrPar3, }, @@ -60,7 +61,7 @@ func TestClient_GetZoneOrDefault(t *testing.T) { region: scw.RegionFrPar, }, args: args{ - zone: scw.StringPtr("nl-ams-1"), + zone: "nl-ams-1", }, want: scw.ZoneNlAms1, }, diff --git a/internal/service/scaleway/common/resource_ensurer_test.go b/internal/service/scaleway/common/resource_ensurer_test.go index 8c5509c..40d6c2b 100644 --- a/internal/service/scaleway/common/resource_ensurer_test.go +++ b/internal/service/scaleway/common/resource_ensurer_test.go @@ -6,9 +6,10 @@ import ( "slices" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common/mock_common" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common/mock_common" ) type resource int diff --git a/internal/service/scaleway/domain/domain.go b/internal/service/scaleway/domain/domain.go index 51090b7..11f0c31 100644 --- a/internal/service/scaleway/domain/domain.go +++ b/internal/service/scaleway/domain/domain.go @@ -6,9 +6,13 @@ import ( "fmt" "slices" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/cluster-api/util/conditions" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" ) type Service struct { @@ -24,7 +28,7 @@ func (s *Service) Name() string { } func (s *Service) Delete(ctx context.Context) error { - if !s.HasControlPlaneDNS() { + if !s.ScalewayCluster.Spec.Network.ControlPlaneDNS.IsDefined() { return nil } @@ -56,11 +60,27 @@ func (s *Service) Delete(ctx context.Context) error { return nil } -func (s *Service) Reconcile(ctx context.Context) error { - if !s.HasControlPlaneDNS() { +func (s *Service) Reconcile(ctx context.Context) (retErr error) { + if !s.ScalewayCluster.Spec.Network.ControlPlaneDNS.IsDefined() { + conditions.Set(s.ScalewayCluster, metav1.Condition{ + Type: infrav1.ScalewayClusterDomainReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.ScalewayClusterNoDomainReason, + }) return nil } + defer func() { + if retErr != nil { + conditions.Set(s.ScalewayCluster, metav1.Condition{ + Type: infrav1.ScalewayClusterDomainReadyCondition, + Status: metav1.ConditionFalse, + Reason: infrav1.ScalewayClusterDomainReconciliationFailedReason, + Message: retErr.Error(), + }) + } + }() + zone, name, err := s.ControlPlaneDNSZoneAndName() if err != nil { return err @@ -82,15 +102,19 @@ func (s *Service) Reconcile(ctx context.Context) error { return errors.New("no control plane ips found") } - if slices.Equal(recordIPs, controlPlaneIPs) { - return nil - } - - logf.FromContext(ctx).Info("Updating zone records", "zone", zone, "name", name, "controlPlaneIPs", controlPlaneIPs) + if !slices.Equal(recordIPs, controlPlaneIPs) { + logf.FromContext(ctx).Info("Updating zone records", "zone", zone, "name", name, "controlPlaneIPs", controlPlaneIPs) - if err := s.ScalewayClient.SetDNSZoneRecords(ctx, zone, name, controlPlaneIPs); err != nil { - return fmt.Errorf("failed to set dns records: %w", err) + if err := s.ScalewayClient.SetDNSZoneRecords(ctx, zone, name, controlPlaneIPs); err != nil { + return fmt.Errorf("failed to set dns records: %w", err) + } } + conditions.Set(s.ScalewayCluster, metav1.Condition{ + Type: infrav1.ScalewayClusterDomainReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.ScalewayClusterDomainZoneConfiguredReason, + }) + return nil } diff --git a/internal/service/scaleway/domain/domain_test.go b/internal/service/scaleway/domain/domain_test.go index 37c8505..729e5cd 100644 --- a/internal/service/scaleway/domain/domain_test.go +++ b/internal/service/scaleway/domain/domain_test.go @@ -6,12 +6,14 @@ import ( "net/http" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -26,6 +28,16 @@ var ( extraLBIPs = []string{"11.11.11.11", "22.22.22.22"} ) +func sliceToInfraIPv4(slice []string) []infrav1.IPv4 { + r := make([]infrav1.IPv4, 0, len(slice)) + + for _, s := range slice { + r = append(r, infrav1.IPv4(s)) + } + + return r +} + func TestService_Reconcile(t *testing.T) { t.Parallel() type fields struct { @@ -45,7 +57,7 @@ func TestService_Reconcile(t *testing.T) { name: "no dns", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{}, + ScalewayCluster: &infrav1.ScalewayCluster{}, }, }, args: args{ @@ -58,19 +70,19 @@ func TestService_Reconcile(t *testing.T) { name: "public dns: set zone records", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - ControlPlaneDNS: &v1alpha1.ControlPlaneDNSSpec{ + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: zone, Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -89,19 +101,19 @@ func TestService_Reconcile(t *testing.T) { name: "public dns: up-to-date", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - ControlPlaneDNS: &v1alpha1.ControlPlaneDNSSpec{ + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: zone, Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -123,26 +135,26 @@ func TestService_Reconcile(t *testing.T) { name: "private dns: set zone records", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &v1alpha1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ + Private: ptr.To(true), }, - ControlPlanePrivateDNS: &v1alpha1.ControlPlanePrivateDNSSpec{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -162,26 +174,26 @@ func TestService_Reconcile(t *testing.T) { name: "private dns: up-to-date", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &v1alpha1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ + Private: ptr.To(true), }, - ControlPlanePrivateDNS: &v1alpha1.ControlPlanePrivateDNSSpec{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -242,7 +254,7 @@ func TestService_Delete(t *testing.T) { name: "no dns", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{}, + ScalewayCluster: &infrav1.ScalewayCluster{}, }, }, args: args{ @@ -255,19 +267,19 @@ func TestService_Delete(t *testing.T) { name: "public dns: ignore missing zone", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - ControlPlaneDNS: &v1alpha1.ControlPlaneDNSSpec{ + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: zone, Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -287,19 +299,19 @@ func TestService_Delete(t *testing.T) { name: "public dns: already deleted", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - ControlPlaneDNS: &v1alpha1.ControlPlaneDNSSpec{ + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: zone, Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -317,19 +329,19 @@ func TestService_Delete(t *testing.T) { name: "public dns: delete records", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - ControlPlaneDNS: &v1alpha1.ControlPlaneDNSSpec{ + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Domain: zone, Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -352,26 +364,26 @@ func TestService_Delete(t *testing.T) { name: "private dns: already deleted", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &v1alpha1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ + Private: ptr.To(true), }, - ControlPlanePrivateDNS: &v1alpha1.ControlPlanePrivateDNSSpec{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, @@ -390,26 +402,26 @@ func TestService_Delete(t *testing.T) { name: "private dns: delete records", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &v1alpha1.ControlPlaneLoadBalancerSpec{ - Private: scw.BoolPtr(true), + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ + Private: ptr.To(true), }, - ControlPlanePrivateDNS: &v1alpha1.ControlPlanePrivateDNSSpec{ + ControlPlaneDNS: infrav1.ControlPlaneDNS{ Name: name, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), - LoadBalancerIP: scw.StringPtr(lbIP), - ExtraLoadBalancerIPs: *scw.StringsPtr(extraLBIPs), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), + LoadBalancerIP: infrav1.IPv4(lbIP), + ExtraLoadBalancerIPs: sliceToInfraIPv4(extraLBIPs), }, }, }, diff --git a/internal/service/scaleway/errors.go b/internal/service/scaleway/errors.go index 73ff1f3..16df32e 100644 --- a/internal/service/scaleway/errors.go +++ b/internal/service/scaleway/errors.go @@ -1,27 +1,14 @@ package scaleway import ( + "errors" "fmt" "time" ) -// ReconcileErrorType represents the type of a ReconcileError. -type ReconcileErrorType string - -const ( - // TransientErrorType can be recovered, will be requeued after a configured time interval. - TransientErrorType ReconcileErrorType = "Transient" - // TerminalErrorType cannot be recovered, will not be requeued. - TerminalErrorType ReconcileErrorType = "Terminal" -) - -// ReconcileError represents an error that is not automatically recoverable -// errorType indicates what type of action is required to recover. It can take two values: -// 1. `Transient` - Can be recovered through manual intervention or by waiting, will be requeued after. -// 2. `Terminal` - Cannot be recovered, will not be requeued. +// ReconcileError represents an error that may be fixed by waiting. type ReconcileError struct { error - errorType ReconcileErrorType requestAfter time.Duration } @@ -31,37 +18,30 @@ func (t *ReconcileError) Error() string { if t.error != nil { errStr = t.error.Error() } - switch t.errorType { - case TransientErrorType: + + if t.requestAfter != 0 { return fmt.Sprintf("%s. Object will be requeued after %s", errStr, t.requestAfter.String()) - case TerminalErrorType: - return fmt.Sprintf("reconcile error that cannot be recovered occurred: %s. Object will not be requeued", errStr) - default: - return fmt.Sprintf("reconcile error occurred with unknown recovery type. The actual error is: %s", errStr) } -} -// IsTransient returns true if the ReconcileError is recoverable. -func (t *ReconcileError) IsTransient() bool { - return t.errorType == TransientErrorType -} - -// IsTerminal returns true if the ReconcileError is not recoverable. -func (t *ReconcileError) IsTerminal() bool { - return t.errorType == TerminalErrorType + return fmt.Sprintf("reconcile error: %s", errStr) } // RequeueAfter returns requestAfter value. func (t *ReconcileError) RequeueAfter() time.Duration { + if t == nil { + return 0 + } + return t.requestAfter } -// WithTransientError wraps the error in a ReconcileError with errorType as `Transient`. +// WithTransientError wraps the error in a ReconcileError with requeueAfter duration. func WithTransientError(err error, requeueAfter time.Duration) *ReconcileError { - return &ReconcileError{error: err, errorType: TransientErrorType, requestAfter: requeueAfter} + return &ReconcileError{error: err, requestAfter: requeueAfter} } -// WithTerminalError wraps the error in a ReconcileError with errorType as `Terminal`. -func WithTerminalError(err error) *ReconcileError { - return &ReconcileError{error: err, errorType: TerminalErrorType} +// IsTransientReconcileError returns true if the provided error is a transient ReconcileError. +func IsTransientReconcileError(err error) bool { + var reconcileErr *ReconcileError + return errors.As(err, &reconcileErr) && reconcileErr.RequeueAfter() != 0 } diff --git a/internal/service/scaleway/instance/instance.go b/internal/service/scaleway/instance/instance.go index f9b12a8..e90d987 100644 --- a/internal/service/scaleway/instance/instance.go +++ b/internal/service/scaleway/instance/instance.go @@ -9,21 +9,25 @@ import ( "text/template" "time" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - servicelb "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb" - lbutil "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb/util" "github.com/scaleway/scaleway-sdk-go/api/block/v1" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/api/ipam/v1" "github.com/scaleway/scaleway-sdk-go/api/lb/v1" "github.com/scaleway/scaleway-sdk-go/api/marketplace/v2" "github.com/scaleway/scaleway-sdk-go/scw" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + "sigs.k8s.io/cluster-api/util/conditions" logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" + servicelb "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb" + lbutil "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb/util" ) const ( @@ -55,7 +59,22 @@ func (s *Service) Name() string { return "instance" } -func (s *Service) Reconcile(ctx context.Context) error { +func (s *Service) Reconcile(ctx context.Context) (retErr error) { + defer func() { + condition := metav1.Condition{ + Type: infrav1.ScalewayMachineInstanceReadyCondition, + } + if retErr != nil { + condition.Status = metav1.ConditionFalse + condition.Reason = infrav1.ScalewayMachineInstanceReconciliationFailedReason + condition.Message = retErr.Error() + } else { + condition.Status = metav1.ConditionTrue + condition.Reason = infrav1.ScalewayMachineInstanceReadyReason + } + conditions.Set(s.ScalewayMachine, condition) + }() + server, err := s.ensureServer(ctx) if err != nil { return fmt.Errorf("failed to ensure server: %w", err) @@ -185,8 +204,8 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) { } // Provider ID is already set, it's not normal that we didn't find the server. - if s.ScalewayMachine.Spec.ProviderID != nil { - return nil, scaleway.WithTerminalError(fmt.Errorf("providerID is already set on ScalewayMachine, but no existing server was found")) + if s.ScalewayMachine.Spec.ProviderID != "" { + return nil, errors.New("providerID is already set on ScalewayMachine, but no existing server was found") } // Server does not exist, let's create it. @@ -200,22 +219,22 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) { var imageID string switch image := s.ScalewayMachine.Spec.Image; { - case image.ID != nil: - imageID = *image.ID - case image.Label != nil: + case image.ID != "": + imageID = string(image.ID) + case image.Label != "": marketplaceType, ok := instanceVolumeTypeToMarketplaceType[volumeType] if !ok { - return nil, scaleway.WithTerminalError(fmt.Errorf("did not find marketplace type for volume type %s", volumeType)) + return nil, fmt.Errorf("did not find marketplace type for volume type %s", volumeType) } - image, err := s.ScalewayClient.GetLocalImageByLabel(ctx, zone, s.ScalewayMachine.Spec.CommercialType, *image.Label, marketplaceType) + image, err := s.ScalewayClient.GetLocalImageByLabel(ctx, zone, s.ScalewayMachine.Spec.CommercialType, image.Label, marketplaceType) if err != nil { return nil, err } imageID = image.ID - case image.Name != nil: - image, err := s.ScalewayClient.FindImage(ctx, zone, *image.Name) + case image.Name != "": + image, err := s.ScalewayClient.FindImage(ctx, zone, image.Name) if err != nil { return nil, fmt.Errorf("failed to find image by name, make sure it exists in zone %s: %w", zone, err) } @@ -271,18 +290,16 @@ func (s *Service) ensureServer(ctx context.Context) (*instance.Server, error) { func (s *Service) placementGroupID(ctx context.Context, zone scw.Zone) (*string, error) { // If user has specified a placement group, get its ID. - if s.ScalewayMachine.Spec.PlacementGroup != nil { - switch pgref := s.ScalewayMachine.Spec.PlacementGroup; { - case pgref.ID != nil: - return pgref.ID, nil - case pgref.Name != nil: - placementGroup, err := s.ScalewayClient.FindPlacementGroup(ctx, zone, *pgref.Name) - if err != nil { - return nil, fmt.Errorf("failed to find placement group: %w", err) - } - - return &placementGroup.ID, nil + switch pgref := s.ScalewayMachine.Spec.PlacementGroup; { + case pgref.ID != "": + return ptr.To(string(pgref.ID)), nil + case pgref.Name != "": + placementGroup, err := s.ScalewayClient.FindPlacementGroup(ctx, zone, pgref.Name) + if err != nil { + return nil, fmt.Errorf("failed to find placement group: %w", err) } + + return &placementGroup.ID, nil } return nil, nil @@ -290,18 +307,16 @@ func (s *Service) placementGroupID(ctx context.Context, zone scw.Zone) (*string, func (s *Service) securityGroupID(ctx context.Context, zone scw.Zone) (*string, error) { // If user has specified a security group, get its ID. - if s.ScalewayMachine.Spec.SecurityGroup != nil { - switch sgref := s.ScalewayMachine.Spec.SecurityGroup; { - case sgref.ID != nil: - return sgref.ID, nil - case sgref.Name != nil: - securityGroup, err := s.ScalewayClient.FindSecurityGroup(ctx, zone, *sgref.Name) - if err != nil { - return nil, fmt.Errorf("failed to find security group: %w", err) - } - - return &securityGroup.ID, nil + switch sgref := s.ScalewayMachine.Spec.SecurityGroup; { + case sgref.ID != "": + return ptr.To(string(sgref.ID)), nil + case sgref.Name != "": + securityGroup, err := s.ScalewayClient.FindSecurityGroup(ctx, zone, sgref.Name) + if err != nil { + return nil, fmt.Errorf("failed to find security group: %w", err) } + + return &securityGroup.ID, nil } return nil, nil @@ -457,12 +472,7 @@ func nodeIP(server *instance.Server, privateIPs []*ipam.IP) (string, error) { } func (s *Service) findControlPlaneLBs(ctx context.Context) ([]*lb.LB, error) { - var spec infrav1.LoadBalancerSpec - if s.ScalewayCluster.Spec.Network != nil && s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil { - spec = s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancerSpec - } - - zone, err := s.ScalewayClient.GetZoneOrDefault(spec.Zone) + zone, err := s.ScalewayClient.GetZoneOrDefault(string(s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancer.Zone)) if err != nil { return nil, err } @@ -696,7 +706,7 @@ func (s *Service) ensureBootVolumeDeleted(ctx context.Context, server *instance. return err } default: - return scaleway.WithTerminalError(fmt.Errorf("cannot detach unsupported boot volume with type %s", vol.VolumeType)) + return fmt.Errorf("cannot detach unsupported boot volume with type %s", vol.VolumeType) } if err := s.ScalewayClient.DetachVolume(ctx, server.Zone, vol.ID); err != nil { diff --git a/internal/service/scaleway/instance/instance_test.go b/internal/service/scaleway/instance/instance_test.go index 95fc040..5d862ae 100644 --- a/internal/service/scaleway/instance/instance_test.go +++ b/internal/service/scaleway/instance/instance_test.go @@ -9,11 +9,7 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" - servicelb "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb" + "github.com/scaleway/scaleway-sdk-go/api/block/v1" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/api/ipam/v1" @@ -21,11 +17,17 @@ import ( "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/cluster-api/api/v1beta1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" + servicelb "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb" ) const ( @@ -75,54 +77,56 @@ func TestService_Reconcile(t *testing.T) { name: "create control-plane machine", fields: fields{ Machine: &scope.Machine{ - Machine: &v1beta1.Machine{ - ObjectMeta: v1.ObjectMeta{ + Machine: &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ Name: "machine", Namespace: "default", Labels: map[string]string{clusterv1.MachineControlPlaneLabel: ""}, }, - Spec: v1beta1.MachineSpec{ - FailureDomain: scw.StringPtr("fr-par-1"), + Spec: clusterv1.MachineSpec{ + FailureDomain: "fr-par-1", Bootstrap: clusterv1.Bootstrap{ - DataSecretName: scw.StringPtr("bootstrap"), + DataSecretName: ptr.To("bootstrap"), }, }, }, - ScalewayMachine: &v1alpha1.ScalewayMachine{ - ObjectMeta: v1.ObjectMeta{ + ScalewayMachine: &infrav1.ScalewayMachine{ + ObjectMeta: metav1.ObjectMeta{ Name: "machine", Namespace: "default", }, - Spec: v1alpha1.ScalewayMachineSpec{ + Spec: infrav1.ScalewayMachineSpec{ CommercialType: "DEV1-S", - Image: v1alpha1.ImageSpec{ - ID: scw.StringPtr(imageID), + Image: infrav1.Image{ + IDOrName: infrav1.IDOrName{ + ID: imageID, + }, }, - PublicNetwork: &v1alpha1.PublicNetworkSpec{ - EnableIPv4: scw.BoolPtr(true), - EnableIPv6: scw.BoolPtr(true), + PublicNetwork: infrav1.PublicNetwork{ + EnableIPv4: ptr.To(true), + EnableIPv6: ptr.To(true), }, - RootVolume: &v1alpha1.RootVolumeSpec{ - Size: scw.Int64Ptr(42), + RootVolume: infrav1.RootVolume{ + Size: 42, }, }, }, Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -134,7 +138,7 @@ func TestService_Reconcile(t *testing.T) { }, objects: []runtime.Object{ &corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "bootstrap", Namespace: "default", }, @@ -147,7 +151,7 @@ func TestService_Reconcile(t *testing.T) { clusterTags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} tags := append(clusterTags, "caps-scalewaymachine=machine") - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil) i.FindServer(gomock.Any(), scw.ZoneFrPar1, tags).Return(nil, client.ErrNoItemFound) i.CreateServer( gomock.Any(), @@ -195,7 +199,7 @@ func TestService_Reconcile(t *testing.T) { }, nil) // LB configuration - i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil) // Get main LB zone. + i.GetZoneOrDefault("").Return(scw.ZoneFrPar1, nil) // Get main LB zone. i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(clusterTags, servicelb.CAPSMainLBTag)).Return(&lb.LB{ ID: lbID, Zone: scw.ZoneFrPar1, @@ -226,67 +230,69 @@ func TestService_Reconcile(t *testing.T) { {Type: clusterv1.MachineExternalDNS, Address: "11111111-1111-1111-1111-111111111111.pub.instances.scw.cloud"}, {Type: clusterv1.MachineInternalIP, Address: "10.0.0.1"}, })) - g.Expect(m.ScalewayMachine.Spec.ProviderID).To(Equal(scw.StringPtr("scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111"))) + g.Expect(m.ScalewayMachine.Spec.ProviderID).To(Equal("scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111")) }, }, { name: "node has joined cluster: need to clean userdata", fields: fields{ Machine: &scope.Machine{ - Machine: &v1beta1.Machine{ - ObjectMeta: v1.ObjectMeta{ + Machine: &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ Name: "machine", Namespace: "default", Labels: map[string]string{clusterv1.MachineControlPlaneLabel: ""}, }, - Spec: v1beta1.MachineSpec{ - FailureDomain: scw.StringPtr("fr-par-1"), + Spec: clusterv1.MachineSpec{ + FailureDomain: "fr-par-1", Bootstrap: clusterv1.Bootstrap{ - DataSecretName: scw.StringPtr("bootstrap"), + DataSecretName: ptr.To("bootstrap"), }, }, Status: clusterv1.MachineStatus{ - NodeRef: &corev1.ObjectReference{ + NodeRef: clusterv1.MachineNodeReference{ Name: "cluster", }, }, }, - ScalewayMachine: &v1alpha1.ScalewayMachine{ - ObjectMeta: v1.ObjectMeta{ + ScalewayMachine: &infrav1.ScalewayMachine{ + ObjectMeta: metav1.ObjectMeta{ Name: "machine", Namespace: "default", }, - Spec: v1alpha1.ScalewayMachineSpec{ + Spec: infrav1.ScalewayMachineSpec{ CommercialType: "DEV1-S", - Image: v1alpha1.ImageSpec{ - ID: scw.StringPtr(imageID), + Image: infrav1.Image{ + IDOrName: infrav1.IDOrName{ + ID: imageID, + }, }, - PublicNetwork: &v1alpha1.PublicNetworkSpec{ - EnableIPv4: scw.BoolPtr(true), - EnableIPv6: scw.BoolPtr(true), + PublicNetwork: infrav1.PublicNetwork{ + EnableIPv4: ptr.To(true), + EnableIPv6: ptr.To(true), }, - RootVolume: &v1alpha1.RootVolumeSpec{ - Size: scw.Int64Ptr(42), + RootVolume: infrav1.RootVolume{ + Size: 42, }, - ProviderID: scw.StringPtr("scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111"), + ProviderID: "scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111", }, }, Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -301,7 +307,7 @@ func TestService_Reconcile(t *testing.T) { clusterTags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} tags := append(clusterTags, "caps-scalewaymachine=machine") - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil) i.FindServer(gomock.Any(), scw.ZoneFrPar1, tags).Return(&instance.Server{ Name: "machine", Hostname: "machine", @@ -367,7 +373,7 @@ func TestService_Delete(t *testing.T) { Machine: &scope.Machine{ Machine: &clusterv1.Machine{ Spec: clusterv1.MachineSpec{ - FailureDomain: scw.StringPtr("invalidvalue"), + FailureDomain: "invalidvalue", }, }, Cluster: &scope.Cluster{}, @@ -377,67 +383,69 @@ func TestService_Delete(t *testing.T) { ctx: context.TODO(), }, expect: func(i *mock_client.MockInterfaceMockRecorder) { - i.GetZoneOrDefault(scw.StringPtr("invalidvalue")).Return(scw.Zone(""), errors.New("invalid zone")) + i.GetZoneOrDefault("invalidvalue").Return(scw.Zone(""), errors.New("invalid zone")) }, }, { name: "delete control-plane machine", fields: fields{ Machine: &scope.Machine{ - Machine: &v1beta1.Machine{ - ObjectMeta: v1.ObjectMeta{ + Machine: &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ Name: "machine", Namespace: "default", Labels: map[string]string{clusterv1.MachineControlPlaneLabel: ""}, }, - Spec: v1beta1.MachineSpec{ - FailureDomain: scw.StringPtr("fr-par-1"), + Spec: clusterv1.MachineSpec{ + FailureDomain: "fr-par-1", Bootstrap: clusterv1.Bootstrap{ - DataSecretName: scw.StringPtr("bootstrap"), + DataSecretName: ptr.To("bootstrap"), }, }, Status: clusterv1.MachineStatus{ - NodeRef: &corev1.ObjectReference{ + NodeRef: clusterv1.MachineNodeReference{ Name: "cluster", }, }, }, - ScalewayMachine: &v1alpha1.ScalewayMachine{ - ObjectMeta: v1.ObjectMeta{ + ScalewayMachine: &infrav1.ScalewayMachine{ + ObjectMeta: metav1.ObjectMeta{ Name: "machine", Namespace: "default", }, - Spec: v1alpha1.ScalewayMachineSpec{ + Spec: infrav1.ScalewayMachineSpec{ CommercialType: "DEV1-S", - Image: v1alpha1.ImageSpec{ - ID: scw.StringPtr(imageID), + Image: infrav1.Image{ + IDOrName: infrav1.IDOrName{ + ID: imageID, + }, }, - PublicNetwork: &v1alpha1.PublicNetworkSpec{ - EnableIPv4: scw.BoolPtr(true), - EnableIPv6: scw.BoolPtr(true), + PublicNetwork: infrav1.PublicNetwork{ + EnableIPv4: ptr.To(true), + EnableIPv6: ptr.To(true), }, - RootVolume: &v1alpha1.RootVolumeSpec{ - Size: scw.Int64Ptr(42), + RootVolume: infrav1.RootVolume{ + Size: 42, }, - ProviderID: scw.StringPtr("scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111"), + ProviderID: "scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111", }, }, Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -451,7 +459,7 @@ func TestService_Delete(t *testing.T) { clusterTags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} tags := append(clusterTags, "caps-scalewaymachine=machine") - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil) i.FindServer(gomock.Any(), scw.ZoneFrPar1, tags).Return(&instance.Server{ Name: "machine", Hostname: "machine", @@ -479,7 +487,7 @@ func TestService_Delete(t *testing.T) { }, nil) // LB config - i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("").Return(scw.ZoneFrPar1, nil) i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(clusterTags, servicelb.CAPSMainLBTag)).Return(&lb.LB{ ID: lbID, Zone: scw.ZoneFrPar1, diff --git a/internal/service/scaleway/k8s/cluster/cluster.go b/internal/service/scaleway/k8s/cluster/cluster.go index 722c518..bc97696 100644 --- a/internal/service/scaleway/k8s/cluster/cluster.go +++ b/internal/service/scaleway/k8s/cluster/cluster.go @@ -9,13 +9,18 @@ import ( "sync" "time" + "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/utils/ptr" + "sigs.k8s.io/cluster-api/util/conditions" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common" - "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" - "github.com/scaleway/scaleway-sdk-go/scw" - utilerrors "k8s.io/apimachinery/pkg/util/errors" ) const clusterRetryTime = 30 * time.Second @@ -34,7 +39,29 @@ func (s *Service) Name() string { return "k8s_cluster" } -func (s *Service) Reconcile(ctx context.Context) error { +func (s *Service) Reconcile(ctx context.Context) (retErr error) { + defer func() { + condition := metav1.Condition{ + Type: infrav1.ScalewayManagedControlPlaneClusterReadyCondition, + Reason: infrav1.ScalewayManagedControlPlaneClusterReconciliationFailedReason, + } + + if retErr != nil { + condition.Status = metav1.ConditionFalse + condition.Message = retErr.Error() + + // Override reason if this is a transient error (e.g. cluster is being upgraded). + if scaleway.IsTransientReconcileError(retErr) { + condition.Reason = infrav1.ScalewayManagedControlPlaneClusterTransientStatusReason + } + } else { + condition.Status = metav1.ConditionTrue + condition.Reason = infrav1.ScalewayManagedControlPlaneClusterReadyReason + } + + conditions.Set(s.ScalewayManagedControlPlane, condition) + }() + cluster, err := s.getOrCreateCluster(ctx) if err != nil { return err @@ -108,12 +135,12 @@ func (s *Service) Reconcile(ctx context.Context) error { } func (s *Service) Delete(ctx context.Context) error { - clusterName := s.ManagedControlPlane.ManagedControlPlane.Spec.ClusterName - if clusterName == nil { + clusterName := s.ScalewayManagedControlPlane.Spec.ClusterName + if clusterName == "" { return nil } - cluster, err := s.ScalewayClient.FindCluster(ctx, *clusterName) + cluster, err := s.ScalewayClient.FindCluster(ctx, clusterName) if err != nil { if client.IsNotFoundError(err) { return nil @@ -136,7 +163,7 @@ func (s *Service) getOrCreateCluster(ctx context.Context) (*k8s.Cluster, error) } if cluster == nil { - smcp := s.ManagedControlPlane.ManagedControlPlane + smcp := s.ScalewayManagedControlPlane autoscalerConfig, err := s.DesiredClusterAutoscalerConfig() if err != nil { return nil, err @@ -146,24 +173,22 @@ func (s *Service) getOrCreateCluster(ctx context.Context) (*k8s.Cluster, error) oidcConfig := s.DesiredClusterOpenIDConnectConfig() var podCIDR, serviceCIDR scw.IPNet - if clusterNetwork := s.Cluster.Spec.ClusterNetwork; clusterNetwork != nil { - if clusterNetwork.Pods != nil && len(clusterNetwork.Pods.CIDRBlocks) > 0 { - _, podCIDRIPNet, err := net.ParseCIDR(clusterNetwork.Pods.CIDRBlocks[0]) - if err != nil { - return nil, err - } - - podCIDR.IPNet = *podCIDRIPNet + if len(s.Cluster.Spec.ClusterNetwork.Pods.CIDRBlocks) > 0 { + _, podCIDRIPNet, err := net.ParseCIDR(s.Cluster.Spec.ClusterNetwork.Pods.CIDRBlocks[0]) + if err != nil { + return nil, err } - if clusterNetwork.Services != nil && len(clusterNetwork.Services.CIDRBlocks) > 0 { - _, podCIDRIPNet, err := net.ParseCIDR(clusterNetwork.Services.CIDRBlocks[0]) - if err != nil { - return nil, err - } + podCIDR.IPNet = *podCIDRIPNet + } - serviceCIDR.IPNet = *podCIDRIPNet + if len(s.Cluster.Spec.ClusterNetwork.Services.CIDRBlocks) > 0 { + _, podCIDRIPNet, err := net.ParseCIDR(s.Cluster.Spec.ClusterNetwork.Services.CIDRBlocks[0]) + if err != nil { + return nil, err } + + serviceCIDR.IPNet = *podCIDRIPNet } cluster, err = s.ScalewayClient.CreateCluster( @@ -201,9 +226,9 @@ func (s *Service) getOrCreateCluster(ctx context.Context) (*k8s.Cluster, error) ClientID: oidcConfig.ClientID, UsernameClaim: &oidcConfig.UsernameClaim, UsernamePrefix: &oidcConfig.UsernamePrefix, - GroupsClaim: &oidcConfig.GroupsClaim, GroupsPrefix: &oidcConfig.GroupsPrefix, - RequiredClaim: &oidcConfig.RequiredClaim, + GroupsClaim: ptr.To(makeSliceIfNeeded(oidcConfig.GroupsClaim)), + RequiredClaim: ptr.To(makeSliceIfNeeded(oidcConfig.RequiredClaim)), }, podCIDR, serviceCIDR, @@ -218,30 +243,30 @@ func (s *Service) getOrCreateCluster(ctx context.Context) (*k8s.Cluster, error) func (s *Service) updateCluster(ctx context.Context, cluster *k8s.Cluster) (bool, error) { updateNeeded := false - smmp := s.ManagedControlPlane.ManagedControlPlane + smmp := s.ScalewayManagedControlPlane var tags *[]string if !common.SlicesEqualIgnoreOrder(client.TagsWithoutCreatedBy(cluster.Tags), s.DesiredTags()) { updateNeeded = true - tags = scw.StringsPtr(s.DesiredTags()) + tags = ptr.To(s.DesiredTags()) } var featureGates *[]string if !common.SlicesEqualIgnoreOrder(cluster.FeatureGates, smmp.Spec.FeatureGates) { updateNeeded = true - featureGates = scw.StringsPtr(makeSliceIfNeeded(smmp.Spec.FeatureGates)) + featureGates = ptr.To(makeSliceIfNeeded(smmp.Spec.FeatureGates)) } var admissionPlugins *[]string if !common.SlicesEqualIgnoreOrder(cluster.AdmissionPlugins, smmp.Spec.AdmissionPlugins) { updateNeeded = true - admissionPlugins = scw.StringsPtr(makeSliceIfNeeded(smmp.Spec.AdmissionPlugins)) + admissionPlugins = ptr.To(makeSliceIfNeeded(smmp.Spec.AdmissionPlugins)) } var apiServerCertSANs *[]string if !common.SlicesEqualIgnoreOrder(cluster.ApiserverCertSans, smmp.Spec.APIServerCertSANs) { updateNeeded = true - apiServerCertSANs = scw.StringsPtr(makeSliceIfNeeded(smmp.Spec.APIServerCertSANs)) + apiServerCertSANs = ptr.To(makeSliceIfNeeded(smmp.Spec.APIServerCertSANs)) } var autoscalerConfig *k8s.UpdateClusterRequestAutoscalerConfig @@ -284,9 +309,9 @@ func (s *Service) updateCluster(ctx context.Context, cluster *k8s.Cluster) (bool ClientID: &desiredOIDCConfig.ClientID, UsernameClaim: &desiredOIDCConfig.UsernameClaim, UsernamePrefix: &desiredOIDCConfig.UsernamePrefix, - GroupsClaim: &desiredOIDCConfig.GroupsClaim, GroupsPrefix: &desiredOIDCConfig.GroupsPrefix, - RequiredClaim: &desiredOIDCConfig.RequiredClaim, + GroupsClaim: ptr.To(makeSliceIfNeeded(desiredOIDCConfig.GroupsClaim)), + RequiredClaim: ptr.To(makeSliceIfNeeded(desiredOIDCConfig.RequiredClaim)), } } @@ -336,7 +361,7 @@ func (s *Service) updateClusterACLs(ctx context.Context, cluster *k8s.Cluster) ( if currentScalewayRanges { request = append(request, &k8s.ACLRuleRequest{ - ScalewayRanges: scw.BoolPtr(true), + ScalewayRanges: ptr.To(true), }) } diff --git a/internal/service/scaleway/k8s/cluster/cluster_test.go b/internal/service/scaleway/k8s/cluster/cluster_test.go index 6a73925..5db99dd 100644 --- a/internal/service/scaleway/k8s/cluster/cluster_test.go +++ b/internal/service/scaleway/k8s/cluster/cluster_test.go @@ -7,19 +7,22 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" + "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/cluster-api/api/v1beta1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -56,37 +59,39 @@ func TestService_Reconcile(t *testing.T) { name: "create control-plane", fields: fields{ ManagedControlPlane: &scope.ManagedControlPlane{ - Cluster: &v1beta1.Cluster{ - ObjectMeta: v1.ObjectMeta{ + Cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1beta1.ClusterSpec{}, + Spec: clusterv1.ClusterSpec{}, }, - ManagedCluster: &v1alpha1.ScalewayManagedCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedCluster: &infrav1.ScalewayManagedCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "managedcluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedClusterSpec{ + Spec: infrav1.ScalewayManagedClusterSpec{ ProjectID: projectID, }, - Status: v1alpha1.ScalewayManagedClusterStatus{ - Ready: true, - Network: &v1alpha1.ManagedNetworkStatus{ - PrivateNetworkID: scw.StringPtr("11111111-1111-1111-1111-111111111111"), + Status: infrav1.ScalewayManagedClusterStatus{ + Initialization: infrav1.ScalewayManagedClusterInitializationStatus{ + Provisioned: ptr.To(true), + }, + Network: infrav1.ScalewayManagedClusterNetworkStatus{ + PrivateNetworkID: infrav1.UUID("11111111-1111-1111-1111-111111111111"), }, }, }, - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ Name: "controlplane", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ Type: "kapsule", Version: "v1.31.1", - CNI: scw.StringPtr("cilium"), + CNI: "cilium", }, }, }, @@ -102,21 +107,21 @@ func TestService_Reconcile(t *testing.T) { "default-controlplane", "kapsule", "1.31.1", - scw.StringPtr(privateNetworkID), + ptr.To(privateNetworkID), []string{"caps-namespace=default", "caps-scalewaymanagedcontrolplane=controlplane"}, nil, nil, nil, k8s.CNICilium, &k8s.CreateClusterRequestAutoscalerConfig{ - ScaleDownDisabled: scw.BoolPtr(false), - ScaleDownDelayAfterAdd: scw.StringPtr("10m"), + ScaleDownDisabled: ptr.To(false), + ScaleDownDelayAfterAdd: ptr.To("10m"), Estimator: k8s.AutoscalerEstimatorBinpacking, Expander: k8s.AutoscalerExpanderRandom, - IgnoreDaemonsetsUtilization: scw.BoolPtr(false), - BalanceSimilarNodeGroups: scw.BoolPtr(false), + IgnoreDaemonsetsUtilization: ptr.To(false), + BalanceSimilarNodeGroups: ptr.To(false), ExpendablePodsPriorityCutoff: scw.Int32Ptr(-10), - ScaleDownUnneededTime: scw.StringPtr("10m"), + ScaleDownUnneededTime: ptr.To("10m"), ScaleDownUtilizationThreshold: scw.Float32Ptr(0.5), MaxGracefulTerminationSec: scw.Uint32Ptr(600), }, @@ -128,9 +133,9 @@ func TestService_Reconcile(t *testing.T) { }, }, &k8s.CreateClusterRequestOpenIDConnectConfig{ - UsernameClaim: scw.StringPtr(""), - UsernamePrefix: scw.StringPtr(""), - GroupsPrefix: scw.StringPtr(""), + UsernameClaim: ptr.To(""), + UsernamePrefix: ptr.To(""), + GroupsPrefix: ptr.To(""), GroupsClaim: &[]string{}, RequiredClaim: &[]string{}, }, @@ -181,10 +186,10 @@ func TestService_Reconcile(t *testing.T) { i.GetSecretKey().Return("secret-key") }, asserts: func(g *WithT, s *scope.ManagedControlPlane) { - g.Expect(s.ManagedControlPlane.Spec.ClusterName).To(HaveValue(Equal("default-controlplane"))) - g.Expect(s.ManagedControlPlane.Status.Version).To(HaveValue(Equal("v1.31.1"))) - g.Expect(s.ManagedControlPlane.Spec.ControlPlaneEndpoint.Host).To(Equal(fmt.Sprintf("%s.api.k8s.fr-par.scw.cloud", clusterID))) - g.Expect(s.ManagedControlPlane.Spec.ControlPlaneEndpoint.Port).To(BeEquivalentTo(6443)) + g.Expect(s.ScalewayManagedControlPlane.Spec.ClusterName).To(HaveValue(Equal("default-controlplane"))) + g.Expect(s.ScalewayManagedControlPlane.Status.Version).To(HaveValue(Equal("v1.31.1"))) + g.Expect(s.ScalewayManagedControlPlane.Spec.ControlPlaneEndpoint.Host).To(Equal(fmt.Sprintf("%s.api.k8s.fr-par.scw.cloud", clusterID))) + g.Expect(s.ScalewayManagedControlPlane.Spec.ControlPlaneEndpoint.Port).To(BeEquivalentTo(6443)) kubeconfig := &corev1.Secret{} g.Expect(s.Client.Get(context.TODO(), types.NamespacedName{ @@ -205,41 +210,44 @@ func TestService_Reconcile(t *testing.T) { name: "control-plane is already created and up-to-date", fields: fields{ ManagedControlPlane: &scope.ManagedControlPlane{ - Cluster: &v1beta1.Cluster{ - Spec: v1beta1.ClusterSpec{}, + Cluster: &clusterv1.Cluster{ + Spec: clusterv1.ClusterSpec{}, }, - ManagedCluster: &v1alpha1.ScalewayManagedCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedCluster: &infrav1.ScalewayManagedCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "managedcluster", Namespace: "default", }, - Status: v1alpha1.ScalewayManagedClusterStatus{ - Ready: true, - Network: &v1alpha1.ManagedNetworkStatus{ - PrivateNetworkID: scw.StringPtr("11111111-1111-1111-1111-111111111111"), + Status: infrav1.ScalewayManagedClusterStatus{ + Initialization: infrav1.ScalewayManagedClusterInitializationStatus{ + Provisioned: ptr.To(true), + }, + Network: infrav1.ScalewayManagedClusterNetworkStatus{ + PrivateNetworkID: infrav1.UUID("11111111-1111-1111-1111-111111111111"), }, }, }, - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ Name: "controlplane", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ Type: "kapsule", Version: "v1.31.1", - CNI: scw.StringPtr("cilium"), - ClusterName: scw.StringPtr("default-controlplane"), - ControlPlaneEndpoint: v1beta1.APIEndpoint{ + CNI: "cilium", + ClusterName: "default-controlplane", + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: fmt.Sprintf("%s.api.k8s.fr-par.scw.cloud", clusterID), Port: 6443, }, }, - Status: v1alpha1.ScalewayManagedControlPlaneStatus{ - Ready: true, - Initialized: true, - ExternalManagedControlPlane: true, - Version: scw.StringPtr("v1.31.1"), + Status: infrav1.ScalewayManagedControlPlaneStatus{ + Version: "v1.31.1", + Initialization: infrav1.ScalewayManagedControlPlaneInitializationStatus{ + ControlPlaneInitialized: ptr.To(true), + }, + ExternalManagedControlPlane: ptr.To(true), }, }, }, @@ -341,26 +349,27 @@ func TestService_Delete(t *testing.T) { name: "delete cluster", fields: fields{ ManagedControlPlane: &scope.ManagedControlPlane{ - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ Name: "controlplane", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ Type: "kapsule", Version: "v1.31.1", - CNI: scw.StringPtr("cilium"), - ClusterName: scw.StringPtr("default-controlplane"), - ControlPlaneEndpoint: v1beta1.APIEndpoint{ + CNI: "cilium", + ClusterName: "default-controlplane", + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: fmt.Sprintf("%s.api.k8s.fr-par.scw.cloud", clusterID), Port: 6443, }, }, - Status: v1alpha1.ScalewayManagedControlPlaneStatus{ - Ready: true, - Initialized: true, - ExternalManagedControlPlane: true, - Version: scw.StringPtr("v1.31.1"), + Status: infrav1.ScalewayManagedControlPlaneStatus{ + Version: "v1.31.1", + Initialization: infrav1.ScalewayManagedControlPlaneInitializationStatus{ + ControlPlaneInitialized: ptr.To(true), + }, + ExternalManagedControlPlane: ptr.To(true), }, }, }, @@ -379,29 +388,30 @@ func TestService_Delete(t *testing.T) { name: "delete cluster with additional resources", fields: fields{ ManagedControlPlane: &scope.ManagedControlPlane{ - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ Name: "controlplane", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ Type: "kapsule", Version: "v1.31.1", - CNI: scw.StringPtr("cilium"), - ClusterName: scw.StringPtr("default-controlplane"), - ControlPlaneEndpoint: v1beta1.APIEndpoint{ + CNI: "cilium", + ClusterName: "default-controlplane", + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: fmt.Sprintf("%s.api.k8s.fr-par.scw.cloud", clusterID), Port: 6443, }, - OnDelete: &v1alpha1.OnDeleteSpec{ - WithAdditionalResources: scw.BoolPtr(true), + OnDelete: infrav1.OnDelete{ + WithAdditionalResources: ptr.To(true), }, }, - Status: v1alpha1.ScalewayManagedControlPlaneStatus{ - Ready: true, - Initialized: true, - ExternalManagedControlPlane: true, - Version: scw.StringPtr("v1.31.1"), + Status: infrav1.ScalewayManagedControlPlaneStatus{ + Version: "v1.31.1", + Initialization: infrav1.ScalewayManagedControlPlaneInitializationStatus{ + ControlPlaneInitialized: ptr.To(true), + }, + ExternalManagedControlPlane: ptr.To(true), }, }, }, diff --git a/internal/service/scaleway/k8s/cluster/kubeconfig.go b/internal/service/scaleway/k8s/cluster/kubeconfig.go index 28beecd..1f22bfc 100644 --- a/internal/service/scaleway/k8s/cluster/kubeconfig.go +++ b/internal/service/scaleway/k8s/cluster/kubeconfig.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "fmt" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -13,8 +12,11 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/util/kubeconfig" "sigs.k8s.io/cluster-api/util/secret" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) func (s *Service) reconcileKubeconfig(ctx context.Context, cluster *k8s.Cluster, getKubeconfig kubeconfigGetter) error { @@ -51,22 +53,23 @@ func (s *Service) reconcileAdditionalKubeconfigs(ctx context.Context, cluster *k return fmt.Errorf("getting kubeconfig (user) secret %s: %w", clusterRef, err) } - createErr := s.createUserKubeconfigSecret( - ctx, - cluster, - getKubeconfig, - &clusterRef, - ) + createErr := s.createUserKubeconfigSecret(ctx, cluster, getKubeconfig, &clusterRef, s.Cluster.Name) if createErr != nil { - return fmt.Errorf("creating additional kubeconfig secret: %w", err) + return fmt.Errorf("creating additional kubeconfig secret: %w", createErr) } } return nil } -func (s *Service) createUserKubeconfigSecret(ctx context.Context, cluster *k8s.Cluster, getKubeconfig kubeconfigGetter, clusterRef *types.NamespacedName) error { - controllerOwnerRef := *metav1.NewControllerRef(s.ManagedControlPlane.ManagedControlPlane, infrav1.GroupVersion.WithKind("ScalewayManagedControlPlane")) +func (s *Service) createUserKubeconfigSecret( + ctx context.Context, + cluster *k8s.Cluster, + getKubeconfig kubeconfigGetter, + clusterRef *types.NamespacedName, + clusterName string, +) error { + controllerOwnerRef := *metav1.NewControllerRef(s.ScalewayManagedControlPlane, infrav1.GroupVersion.WithKind("ScalewayManagedControlPlane")) contextName := s.getKubeConfigContextName(false) @@ -99,6 +102,8 @@ func (s *Service) createUserKubeconfigSecret(ctx context.Context, cluster *k8s.C } kubeconfigSecret := kubeconfig.GenerateSecretWithOwner(*clusterRef, out, controllerOwnerRef) + kubeconfigSecret.Labels[clusterv1.ClusterNameLabel] = clusterName + if err := s.Client.Create(ctx, kubeconfigSecret); err != nil { return fmt.Errorf("creating secret: %w", err) } @@ -107,7 +112,7 @@ func (s *Service) createUserKubeconfigSecret(ctx context.Context, cluster *k8s.C } func (s *Service) createCAPIKubeconfigSecret(ctx context.Context, cluster *k8s.Cluster, getKubeconfig kubeconfigGetter, clusterRef *types.NamespacedName) error { - controllerOwnerRef := *metav1.NewControllerRef(s.ManagedControlPlane.ManagedControlPlane, infrav1.GroupVersion.WithKind("ScalewayManagedControlPlane")) + controllerOwnerRef := *metav1.NewControllerRef(s.ScalewayManagedControlPlane, infrav1.GroupVersion.WithKind("ScalewayManagedControlPlane")) contextName := s.getKubeConfigContextName(false) @@ -208,7 +213,7 @@ func (s *Service) createBaseKubeConfig(contextName string, cluster *k8s.Cluster, } func (s *Service) getKubeConfigContextName(isUser bool) string { - contextName := fmt.Sprintf("scw_%s_%s_%s", s.ManagedCluster.Spec.ProjectID, s.ManagedCluster.Spec.Region, s.ClusterName()) + contextName := fmt.Sprintf("scw_%s_%s_%s", s.ScalewayManagedCluster.Spec.ProjectID, s.ScalewayManagedCluster.Spec.Region, s.ClusterName()) if isUser { contextName += "-user" } diff --git a/internal/service/scaleway/k8s/pool/pool.go b/internal/service/scaleway/k8s/pool/pool.go index 6ab5112..e6aae9f 100644 --- a/internal/service/scaleway/k8s/pool/pool.go +++ b/internal/service/scaleway/k8s/pool/pool.go @@ -8,13 +8,18 @@ import ( "slices" "time" + "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/utils/ptr" + "sigs.k8s.io/cluster-api/util/conditions" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common" - "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" - "github.com/scaleway/scaleway-sdk-go/scw" - utilerrors "k8s.io/apimachinery/pkg/util/errors" ) const poolRetryTime = 30 * time.Second @@ -32,8 +37,8 @@ func (s Service) Name() string { } func (s *Service) Delete(ctx context.Context) error { - clusterName, ok := s.ClusterName() - if !ok { + clusterName := s.ClusterName() + if clusterName == "" { return nil } @@ -64,9 +69,31 @@ func (s *Service) Delete(ctx context.Context) error { return scaleway.WithTransientError(errors.New("pool is being deleted"), poolRetryTime) } -func (s *Service) Reconcile(ctx context.Context) error { - clusterName, ok := s.ClusterName() - if !ok { +func (s *Service) Reconcile(ctx context.Context) (retErr error) { + defer func() { + condition := metav1.Condition{ + Type: infrav1.ScalewayManagedMachinePoolPoolReadyCondition, + Reason: infrav1.ScalewayManagedMachinePoolPoolReconciliationFailedReason, + } + + if retErr != nil { + condition.Status = metav1.ConditionFalse + condition.Message = retErr.Error() + + // Override reason if this is a transient error (e.g. cluster is being upgraded). + if scaleway.IsTransientReconcileError(retErr) { + condition.Reason = infrav1.ScalewayManagedMachinePoolPoolTransientStatusReason + } + } else { + condition.Status = metav1.ConditionTrue + condition.Reason = infrav1.ScalewayManagedMachinePoolPoolReadyReason + } + + conditions.Set(s.ScalewayManagedMachinePool, condition) + }() + + clusterName := s.ClusterName() + if clusterName == "" { return scaleway.WithTransientError(errors.New("cluster name not set"), poolRetryTime) } @@ -136,7 +163,7 @@ func (s *Service) getOrCreatePool(ctx context.Context, cluster *k8s.Cluster) (*k } if pool == nil { - mmp := s.ManagedMachinePool.ManagedMachinePool + mmp := s.ScalewayManagedMachinePool autoscaling, size, min, max := s.Scaling() pup := s.DesiredPoolUpgradePolicy() @@ -147,8 +174,8 @@ func (s *Service) getOrCreatePool(ctx context.Context, cluster *k8s.Cluster) (*k cluster.ID, s.ResourceName(), mmp.Spec.NodeType, - mmp.Spec.PlacementGroupID, - mmp.Spec.SecurityGroupID, + s.PlacementGroupID(), + s.SecurityGroupID(), autoscaling, s.Autohealing(), s.PublicIPDisabled(), @@ -178,7 +205,7 @@ func (s *Service) updatePool(ctx context.Context, pool *k8s.Pool) (bool, error) var autohealing *bool if pool.Autohealing != s.Autohealing() { updateNeeded = true - autohealing = scw.BoolPtr(s.Autohealing()) + autohealing = ptr.To(s.Autohealing()) } var autoscaling *bool @@ -215,13 +242,13 @@ func (s *Service) updatePool(ctx context.Context, pool *k8s.Pool) (bool, error) var tags *[]string if !common.SlicesEqualIgnoreOrder(client.TagsWithoutCreatedBy(pool.Tags), s.DesiredTags()) { updateNeeded = true - tags = scw.StringsPtr(s.DesiredTags()) + tags = ptr.To(s.DesiredTags()) } var kubeletArgs *map[string]string - if !maps.Equal(pool.KubeletArgs, s.ManagedMachinePool.ManagedMachinePool.Spec.KubeletArgs) { + if !maps.Equal(pool.KubeletArgs, s.ScalewayManagedMachinePool.Spec.KubeletArgs) { updateNeeded = true - kubeletArgs = &s.ManagedMachinePool.ManagedMachinePool.Spec.KubeletArgs + kubeletArgs = &s.ScalewayManagedMachinePool.Spec.KubeletArgs if *kubeletArgs == nil { kubeletArgs = &map[string]string{} } diff --git a/internal/service/scaleway/k8s/pool/pool_test.go b/internal/service/scaleway/k8s/pool/pool_test.go index de15043..ae7364d 100644 --- a/internal/service/scaleway/k8s/pool/pool_test.go +++ b/internal/service/scaleway/k8s/pool/pool_test.go @@ -6,17 +6,19 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + + "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + "go.uber.org/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" - "github.com/scaleway/scaleway-sdk-go/api/k8s/v1" - "github.com/scaleway/scaleway-sdk-go/scw" - "go.uber.org/mock/gomock" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - "sigs.k8s.io/cluster-api/exp/api/v1beta1" ) const ( @@ -46,45 +48,45 @@ func TestService_Reconcile(t *testing.T) { name: "creating pool", fields: fields{ ManagedMachinePool: &scope.ManagedMachinePool{ - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ - ClusterName: scw.StringPtr("default-controlplane"), + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ + ClusterName: "default-controlplane", Version: "v1.30.0", }, }, - MachinePool: &v1beta1.MachinePool{ - Spec: v1beta1.MachinePoolSpec{ + MachinePool: &clusterv1.MachinePool{ + Spec: clusterv1.MachinePoolSpec{ Replicas: scw.Int32Ptr(2), Template: clusterv1.MachineTemplateSpec{ Spec: clusterv1.MachineSpec{ - Version: scw.StringPtr("v1.30.0"), + Version: "v1.30.0", }, }, }, }, - ManagedMachinePool: &v1alpha1.ScalewayManagedMachinePool{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedMachinePool: &infrav1.ScalewayManagedMachinePool{ + ObjectMeta: metav1.ObjectMeta{ Name: "pool", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedMachinePoolSpec{ - Zone: scw.ZoneFrPar1.String(), - PlacementGroupID: scw.StringPtr(placementGroupID), + Spec: infrav1.ScalewayManagedMachinePoolSpec{ + Zone: infrav1.ScalewayZone(scw.ZoneFrPar1), + PlacementGroupID: placementGroupID, NodeType: "DEV1-M", - Scaling: &v1alpha1.ScalingSpec{ - Autoscaling: scw.BoolPtr(true), + Scaling: infrav1.Scaling{ + Autoscaling: ptr.To(true), MinSize: scw.Int32Ptr(1), MaxSize: scw.Int32Ptr(5), }, - Autohealing: scw.BoolPtr(true), - UpgradePolicy: &v1alpha1.UpgradePolicySpec{ + Autohealing: ptr.To(true), + UpgradePolicy: infrav1.UpgradePolicy{ MaxUnavailable: scw.Int32Ptr(0), MaxSurge: scw.Int32Ptr(2), }, - RootVolumeType: scw.StringPtr("sbs_15k"), - RootVolumeSizeGB: scw.Int64Ptr(42), - PublicIPDisabled: scw.BoolPtr(true), - SecurityGroupID: scw.StringPtr(securityGroupID), + RootVolumeType: "sbs_15k", + RootVolumeSizeGB: 42, + PublicIPDisabled: ptr.To(true), + SecurityGroupID: securityGroupID, AdditionalTags: []string{"tag1"}, KubeletArgs: map[string]string{ "containerLogMaxFiles": "500", @@ -108,8 +110,8 @@ func TestService_Reconcile(t *testing.T) { clusterID, "pool", "DEV1-M", - scw.StringPtr(placementGroupID), - scw.StringPtr(securityGroupID), + ptr.To(placementGroupID), + ptr.To(securityGroupID), true, true, true, @@ -139,7 +141,7 @@ func TestService_Reconcile(t *testing.T) { MinSize: 1, MaxSize: 5, Tags: []string{"caps-namespace=default", "caps-scalewaymanagedmachinepool=pool", "tag1", "created-by=cluster-api-provider-scaleway"}, - PlacementGroupID: scw.StringPtr(placementGroupID), + PlacementGroupID: ptr.To(placementGroupID), SecurityGroupID: securityGroupID, KubeletArgs: map[string]string{ "containerLogMaxFiles": "500", @@ -149,7 +151,7 @@ func TestService_Reconcile(t *testing.T) { MaxSurge: 2, }, RootVolumeType: k8s.PoolVolumeTypeSbs15k, - RootVolumeSize: scw.SizePtr(42 * scw.GB), + RootVolumeSize: ptr.To(42 * scw.GB), }, nil) i.ListNodes(gomock.Any(), clusterID, poolID).Return([]*k8s.Node{ { @@ -161,55 +163,56 @@ func TestService_Reconcile(t *testing.T) { }, nil) }, asserts: func(g *WithT, s *scope.ManagedMachinePool) { - g.Expect(s.ManagedMachinePool.Spec.ProviderIDList).To(Equal([]string{ + g.Expect(s.ScalewayManagedMachinePool.Spec.ProviderIDList).To(Equal([]string{ "providerID1", "providerID2", })) - g.Expect(s.ManagedMachinePool.Status.Replicas).To(BeEquivalentTo(2)) + g.Expect(s.ScalewayManagedMachinePool.Status.Replicas).NotTo(BeNil()) + g.Expect(*s.ScalewayManagedMachinePool.Status.Replicas).To(BeEquivalentTo(2)) }, }, { name: "pool exists and is up-to-date", fields: fields{ ManagedMachinePool: &scope.ManagedMachinePool{ - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ - ClusterName: scw.StringPtr("default-controlplane"), + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ + ClusterName: "default-controlplane", Version: "v1.30.0", }, }, - MachinePool: &v1beta1.MachinePool{ - Spec: v1beta1.MachinePoolSpec{ + MachinePool: &clusterv1.MachinePool{ + Spec: clusterv1.MachinePoolSpec{ Replicas: scw.Int32Ptr(2), Template: clusterv1.MachineTemplateSpec{ Spec: clusterv1.MachineSpec{ - Version: scw.StringPtr("v1.30.0"), + Version: "v1.30.0", }, }, }, }, - ManagedMachinePool: &v1alpha1.ScalewayManagedMachinePool{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedMachinePool: &infrav1.ScalewayManagedMachinePool{ + ObjectMeta: metav1.ObjectMeta{ Name: "pool", Namespace: "default", }, - Spec: v1alpha1.ScalewayManagedMachinePoolSpec{ - Zone: scw.ZoneFrPar1.String(), - PlacementGroupID: scw.StringPtr(placementGroupID), + Spec: infrav1.ScalewayManagedMachinePoolSpec{ + Zone: infrav1.ScalewayZone(scw.ZoneFrPar1), + PlacementGroupID: placementGroupID, NodeType: "DEV1-M", - Scaling: &v1alpha1.ScalingSpec{ - Autoscaling: scw.BoolPtr(true), + Scaling: infrav1.Scaling{ + Autoscaling: ptr.To(true), MinSize: scw.Int32Ptr(1), MaxSize: scw.Int32Ptr(5), }, - Autohealing: scw.BoolPtr(true), - UpgradePolicy: &v1alpha1.UpgradePolicySpec{ + Autohealing: ptr.To(true), + UpgradePolicy: infrav1.UpgradePolicy{ MaxUnavailable: scw.Int32Ptr(0), MaxSurge: scw.Int32Ptr(2), }, - RootVolumeType: scw.StringPtr("sbs_15k"), - RootVolumeSizeGB: scw.Int64Ptr(42), - PublicIPDisabled: scw.BoolPtr(true), - SecurityGroupID: scw.StringPtr(securityGroupID), + RootVolumeType: "sbs_15k", + RootVolumeSizeGB: 42, + PublicIPDisabled: ptr.To(true), + SecurityGroupID: securityGroupID, AdditionalTags: []string{"tag1"}, KubeletArgs: map[string]string{ "containerLogMaxFiles": "500", @@ -239,7 +242,7 @@ func TestService_Reconcile(t *testing.T) { MinSize: 1, MaxSize: 5, Tags: []string{"caps-namespace=default", "caps-scalewaymanagedmachinepool=pool", "tag1", "created-by=cluster-api-provider-scaleway"}, - PlacementGroupID: scw.StringPtr(placementGroupID), + PlacementGroupID: ptr.To(placementGroupID), SecurityGroupID: securityGroupID, KubeletArgs: map[string]string{ "containerLogMaxFiles": "500", @@ -249,7 +252,7 @@ func TestService_Reconcile(t *testing.T) { MaxSurge: 2, }, RootVolumeType: k8s.PoolVolumeTypeSbs15k, - RootVolumeSize: scw.SizePtr(42 * scw.GB), + RootVolumeSize: ptr.To(42 * scw.GB), }, nil) i.ListNodes(gomock.Any(), clusterID, poolID).Return([]*k8s.Node{ { @@ -261,10 +264,11 @@ func TestService_Reconcile(t *testing.T) { }, nil) }, asserts: func(g *WithT, s *scope.ManagedMachinePool) { - g.Expect(s.ManagedMachinePool.Spec.ProviderIDList).To(Equal([]string{ + g.Expect(s.ScalewayManagedMachinePool.Spec.ProviderIDList).To(Equal([]string{ "providerID1", "providerID2", })) - g.Expect(s.ManagedMachinePool.Status.Replicas).To(BeEquivalentTo(2)) + g.Expect(s.ScalewayManagedMachinePool.Status.Replicas).NotTo(BeNil()) + g.Expect(*s.ScalewayManagedMachinePool.Status.Replicas).To(BeEquivalentTo(2)) }, }, } @@ -312,14 +316,14 @@ func TestService_Delete(t *testing.T) { name: "delete pool", fields: fields{ ManagedMachinePool: &scope.ManagedMachinePool{ - ManagedControlPlane: &v1alpha1.ScalewayManagedControlPlane{ - Spec: v1alpha1.ScalewayManagedControlPlaneSpec{ - ClusterName: scw.StringPtr("default-controlplane"), + ScalewayManagedControlPlane: &infrav1.ScalewayManagedControlPlane{ + Spec: infrav1.ScalewayManagedControlPlaneSpec{ + ClusterName: "default-controlplane", Version: "v1.30.0", }, }, - ManagedMachinePool: &v1alpha1.ScalewayManagedMachinePool{ - ObjectMeta: v1.ObjectMeta{ + ScalewayManagedMachinePool: &infrav1.ScalewayManagedMachinePool{ + ObjectMeta: metav1.ObjectMeta{ Name: "pool", Namespace: "default", }, diff --git a/internal/service/scaleway/lb/lb.go b/internal/service/scaleway/lb/lb.go index c8e9a9a..1a0b18d 100644 --- a/internal/service/scaleway/lb/lb.go +++ b/internal/service/scaleway/lb/lb.go @@ -11,17 +11,20 @@ import ( "strings" "time" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common" - lbutil "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb/util" "github.com/scaleway/scaleway-sdk-go/api/ipam/v1" "github.com/scaleway/scaleway-sdk-go/api/lb/v1" "github.com/scaleway/scaleway-sdk-go/scw" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" + "sigs.k8s.io/cluster-api/util/conditions" logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common" + lbutil "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/lb/util" ) const ( @@ -58,9 +61,27 @@ func (s *Service) Name() string { return "lb" } -func (s *Service) Reconcile(ctx context.Context) error { +func (s *Service) Reconcile(ctx context.Context) (retErr error) { + condition := metav1.Condition{ + Type: infrav1.ScalewayClusterLoadBalancersReadyCondition, + Reason: infrav1.ScalewayClusterLoadBalancersInternalErrorReason, + } + + defer func() { + if retErr != nil { + condition.Status = metav1.ConditionFalse + condition.Message = retErr.Error() + } else { + condition.Status = metav1.ConditionTrue + condition.Reason = infrav1.ScalewayClusterLoadBalancersReadyReason + } + + conditions.Set(s.ScalewayCluster, condition) + }() + lb, err := s.ensureLB(ctx) if err != nil { + condition.Reason = infrav1.ScalewayClusterMainLoadBalancerReconciliationFailedReason return err } @@ -76,28 +97,34 @@ func (s *Service) Reconcile(ctx context.Context) error { extraLBs, err := s.ensureExtraLBs(ctx, pnID, false) if err != nil { + condition.Reason = infrav1.ScalewayClusterExtraLoadBalancersReconciliationFailedReason return err } if err := checkLBsReadiness(append(extraLBs, lb)); err != nil { + condition.Reason = infrav1.ScalewayClusterLoadBalancersNotReadyReason return err } if err := s.ensurePrivateNetwork(ctx, append(extraLBs, lb), pnID); err != nil { + condition.Reason = infrav1.ScalewayClusterLoadBalancerPrivateNetworkAttachmentFailedReason return err } backendByLB, err := s.ensureBackend(ctx, lb, extraLBs) if err != nil { + condition.Reason = infrav1.ScalewayClusterBackendReconciliationFailedReason return fmt.Errorf("failed to ensure lb backend: %w", err) } frontendByLB, err := s.ensureFrontend(ctx, backendByLB) if err != nil { + condition.Reason = infrav1.ScalewayClusterFrontendReconciliationFailedReason return fmt.Errorf("failed to ensure lb frontend: %w", err) } if err := s.ensureACLs(ctx, lb, frontendByLB, pnID); err != nil { + condition.Reason = infrav1.ScalewayClusterLoadBalancerACLReconciliationFailedReason return fmt.Errorf("failed to ensure ACLs: %w", err) } @@ -145,7 +172,7 @@ type lbWithPrivateIP struct { // privateIP is the desired private IP, set by the user. If the user didn't set // something, a new IP will be booked in IPAM and this variable will contain it. // If no Private Network is configured, this field must be completely ignored. - privateIP *string + privateIP string } func sliceLBToLBWithPrivateIP(lbs []*lb.LB) []*lbWithPrivateIP { @@ -172,10 +199,7 @@ func checkLBsReadiness(lbs []*lbWithPrivateIP) error { } func (s *Service) ensureLB(ctx context.Context) (*lbWithPrivateIP, error) { - var spec infrav1.LoadBalancerSpec - if s.ScalewayCluster.Spec.Network != nil && s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil { - spec = s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancerSpec - } + spec := s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancer zone, lbType, err := lbutil.LBSpec(s.ScalewayClient, spec) if err != nil { @@ -196,14 +220,14 @@ func (s *Service) ensureLB(ctx context.Context) (*lbWithPrivateIP, error) { } else if lb == nil { var ipID *string - if spec.IP != nil { - foundIP, err := s.ScalewayClient.FindLBIP(ctx, zone, *spec.IP) + if spec.IP != "" { + foundIP, err := s.ScalewayClient.FindLBIP(ctx, zone, string(spec.IP)) if err != nil { if client.IsNotFoundError(err) { - return nil, scaleway.WithTerminalError(fmt.Errorf("failed to find IP %q: %w", *spec.IP, err)) + return nil, fmt.Errorf("failed to find IP %q: %w", spec.IP, err) } - return nil, fmt.Errorf("failed to find IP %q: %w", *spec.IP, err) + return nil, fmt.Errorf("failed to find IP %q: %w", spec.IP, err) } ipID = &foundIP.ID @@ -226,15 +250,12 @@ func (s *Service) ensureLB(ctx context.Context) (*lbWithPrivateIP, error) { return &lbWithPrivateIP{ LB: lb, - privateIP: spec.PrivateIP, + privateIP: string(spec.PrivateIP), }, nil } func (s *Service) ensureDeleteLB(ctx context.Context) error { - var spec infrav1.LoadBalancerSpec - if s.ScalewayCluster.Spec.Network != nil && s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil { - spec = s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancerSpec - } + spec := s.ScalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancer zone, _, err := lbutil.LBSpec(s.ScalewayClient, spec) if err != nil { @@ -253,7 +274,7 @@ func (s *Service) ensureDeleteLB(ctx context.Context) error { } logf.FromContext(ctx).Info("Deleting main LB") - if err := s.ScalewayClient.DeleteLB(ctx, zone, lb.ID, spec.IP == nil); err != nil { + if err := s.ScalewayClient.DeleteLB(ctx, zone, lb.ID, spec.IP == ""); err != nil { return fmt.Errorf("failed to delete lb: %w", err) } @@ -262,11 +283,11 @@ func (s *Service) ensureDeleteLB(ctx context.Context) error { func getLBIPv4(lb *lbWithPrivateIP, private bool) (string, error) { if private { - if lb.privateIP == nil { + if lb.privateIP == "" { return "", fmt.Errorf("did not find private ipv4 for lb %s", lb.ID) } - return *lb.privateIP, nil + return lb.privateIP, nil } for _, ip := range lb.IP { @@ -284,13 +305,13 @@ func getLBIPv4(lb *lbWithPrivateIP, private bool) (string, error) { } func (s *Service) ensureExtraLBs(ctx context.Context, pnID *string, delete bool) ([]*lbWithPrivateIP, error) { - var desired []infrav1.LoadBalancerSpec + var desired []infrav1.LoadBalancer // When delete is set, we ensure an empty list of LBs to remove everything. - if !delete && s.ScalewayCluster.Spec.Network != nil { + if !delete { desired = s.ScalewayCluster.Spec.Network.ControlPlaneExtraLoadBalancers } - drle := &common.ResourceEnsurer[infrav1.LoadBalancerSpec, *lbWithPrivateIP]{ + drle := &common.ResourceEnsurer[infrav1.LoadBalancer, *lbWithPrivateIP]{ ResourceReconciler: &desiredResourceListManager{s.Cluster, pnID}, } return drle.Do(ctx, desired) @@ -333,14 +354,14 @@ func (d *desiredResourceListManager) GetResourceName(resource *lbWithPrivateIP) return resource.Name } -func (d *desiredResourceListManager) GetDesiredZone(desired infrav1.LoadBalancerSpec) (scw.Zone, error) { - return d.ScalewayClient.GetZoneOrDefault(desired.Zone) +func (d *desiredResourceListManager) GetDesiredZone(desired infrav1.LoadBalancer) (scw.Zone, error) { + return d.ScalewayClient.GetZoneOrDefault(string(desired.Zone)) } func (d *desiredResourceListManager) ShouldKeepResource( ctx context.Context, resource *lbWithPrivateIP, - desired infrav1.LoadBalancerSpec, + desired infrav1.LoadBalancer, ) (bool, error) { if !d.ControlPlaneLoadBalancerPrivate() { // If LB does not have a public IP, remove it and recreate it. @@ -348,18 +369,18 @@ func (d *desiredResourceListManager) ShouldKeepResource( return false, nil } - if desired.IP == nil && !slices.Contains(resource.Tags, capsManagedIPTag) { + if desired.IP == "" && !slices.Contains(resource.Tags, capsManagedIPTag) { return false, nil } - if desired.IP != nil && !slices.ContainsFunc(resource.IP, func(ip *lb.IP) bool { - return ip.IPAddress == *desired.IP + if desired.IP != "" && !slices.ContainsFunc(resource.IP, func(ip *lb.IP) bool { + return ip.IPAddress == string(desired.IP) }) { return false, nil } } - if desired.PrivateIP != nil && d.pnID != nil { + if desired.PrivateIP != "" && d.pnID != nil { ips, err := d.ScalewayClient.FindLBServersIPs(ctx, *d.pnID, []string{resource.ID}) if err != nil { return false, err @@ -368,7 +389,7 @@ func (d *desiredResourceListManager) ShouldKeepResource( // If no IP is found, the Private Network is probably not attached yet. // We should only replace the LB if there is at least one IP attached and // none of the IPs match the desired IP. - if len(ips) > 0 && !slices.ContainsFunc(ips, ipMatchFunc(*desired.PrivateIP)) { + if len(ips) > 0 && !slices.ContainsFunc(ips, ipMatchFunc(string(desired.PrivateIP))) { return false, nil } } @@ -384,7 +405,7 @@ func (d *desiredResourceListManager) CreateResource( ctx context.Context, zone scw.Zone, name string, - desired infrav1.LoadBalancerSpec, + desired infrav1.LoadBalancer, ) (*lbWithPrivateIP, error) { _, lbType, err := lbutil.LBSpec(d.ScalewayClient, desired) if err != nil { @@ -394,14 +415,14 @@ func (d *desiredResourceListManager) CreateResource( tags := d.ResourceTags(CAPSExtraLBTag) var ipID *string - if desired.IP != nil { - foundIP, err := d.ScalewayClient.FindLBIP(ctx, zone, *desired.IP) + if desired.IP != "" { + foundIP, err := d.ScalewayClient.FindLBIP(ctx, zone, string(desired.IP)) if err != nil { if client.IsNotFoundError(err) { - return nil, scaleway.WithTerminalError(fmt.Errorf("failed to find IP %q: %w", *desired.IP, err)) + return nil, fmt.Errorf("failed to find IP %q: %w", desired.IP, err) } - return nil, fmt.Errorf("failed to find IP %q: %w", *desired.IP, err) + return nil, fmt.Errorf("failed to find IP %q: %w", desired.IP, err) } ipID = &foundIP.ID @@ -417,18 +438,18 @@ func (d *desiredResourceListManager) CreateResource( return &lbWithPrivateIP{ LB: lb, - privateIP: desired.IP, + privateIP: string(desired.IP), }, nil } func (d *desiredResourceListManager) UpdateResource( ctx context.Context, resource *lbWithPrivateIP, - desired infrav1.LoadBalancerSpec, + desired infrav1.LoadBalancer, ) (*lbWithPrivateIP, error) { - if desired.Type != nil && !strings.EqualFold(*desired.Type, resource.Type) { - logf.FromContext(ctx).Info("Migrating extra LB", "lbName", resource.Name, "zone", resource, "type", *desired.Type) - lb, err := d.ScalewayClient.MigrateLB(ctx, resource.Zone, resource.ID, *desired.Type) + if desired.Type != "" && !strings.EqualFold(desired.Type, resource.Type) { + logf.FromContext(ctx).Info("Migrating extra LB", "lbName", resource.Name, "zone", resource, "type", desired.Type) + lb, err := d.ScalewayClient.MigrateLB(ctx, resource.Zone, resource.ID, desired.Type) if err != nil { return nil, err } @@ -437,7 +458,7 @@ func (d *desiredResourceListManager) UpdateResource( } // Set desired private IP. - resource.privateIP = desired.PrivateIP + resource.privateIP = string(desired.PrivateIP) return resource, nil } @@ -549,7 +570,7 @@ func (s *Service) ensurePrivateNetwork(ctx context.Context, lbs []*lbWithPrivate if lbPN == nil { var ipID *string - if lb.privateIP != nil { + if lb.privateIP != "" { // Lazy load available IPs. if len(availableIPs) == 0 { availableIPs, err = s.ScalewayClient.FindAvailableIPs(ctx, *pnID) @@ -558,9 +579,9 @@ func (s *Service) ensurePrivateNetwork(ctx context.Context, lbs []*lbWithPrivate } } - ipIndex := slices.IndexFunc(availableIPs, ipMatchFunc(*lb.privateIP)) + ipIndex := slices.IndexFunc(availableIPs, ipMatchFunc(lb.privateIP)) if ipIndex == -1 { - return scaleway.WithTerminalError(fmt.Errorf("did not find available IP with address %s in IPAM", *lb.privateIP)) + return fmt.Errorf("did not find available IP with address %s in IPAM", lb.privateIP) } ipID = &availableIPs[ipIndex].ID @@ -571,7 +592,7 @@ func (s *Service) ensurePrivateNetwork(ctx context.Context, lbs []*lbWithPrivate } } - if lb.privateIP == nil { + if lb.privateIP == "" { lbIDsWithMissingIP = append(lbIDsWithMissingIP, lb.ID) } } @@ -582,7 +603,7 @@ func (s *Service) ensurePrivateNetwork(ctx context.Context, lbs []*lbWithPrivate } for _, lb := range lbs { - if lb.privateIP != nil { + if lb.privateIP != "" { continue } @@ -591,7 +612,7 @@ func (s *Service) ensurePrivateNetwork(ctx context.Context, lbs []*lbWithPrivate return scaleway.WithTransientError(fmt.Errorf("private IP for lb %s is not yet available in IPAM", lb.Name), 3*time.Second) } - lb.privateIP = scw.StringPtr(missingIPs[ipIndex].Address.IP.String()) + lb.privateIP = missingIPs[ipIndex].Address.IP.String() } return nil diff --git a/internal/service/scaleway/lb/lb_test.go b/internal/service/scaleway/lb/lb_test.go index 37279de..279c32f 100644 --- a/internal/service/scaleway/lb/lb_test.go +++ b/internal/service/scaleway/lb/lb_test.go @@ -6,19 +6,20 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" + "github.com/scaleway/scaleway-sdk-go/api/ipam/v1" "github.com/scaleway/scaleway-sdk-go/api/lb/v1" "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" - "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -65,14 +66,14 @@ func TestService_Reconcile(t *testing.T) { name: "public LB, no extra LB, no Private Network, no ACL: create", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, }, - Cluster: &v1beta1.Cluster{ - ObjectMeta: v1.ObjectMeta{ + Cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, @@ -86,7 +87,7 @@ func TestService_Reconcile(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Main LB - i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("").Return(scw.ZoneFrPar1, nil) i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(tags, CAPSMainLBTag)).Return(nil, client.ErrNoItemFound) i.CreateLB(gomock.Any(), scw.ZoneFrPar1, "cluster", "LB-S", nil, false, append(tags, CAPSMainLBTag)).Return(&lb.LB{ ID: lbID, @@ -126,27 +127,27 @@ func TestService_Reconcile(t *testing.T) { }, asserts: func(g *WithT, c *scope.Cluster) { g.Expect(c.ScalewayCluster.Status.Network).ToNot(BeNil()) - g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(Equal(scw.StringPtr("42.42.42.42"))) + g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(BeEquivalentTo("42.42.42.42")) }, }, { name: "custom public LB, no extra LB, no Private Network, no ACL: create", fields: fields{ Cluster: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, }, - Cluster: &v1beta1.Cluster{ - ObjectMeta: v1.ObjectMeta{ + Cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1beta1.ClusterSpec{ - ClusterNetwork: &v1beta1.ClusterNetwork{ - APIServerPort: ptr.To(int32(4242)), + Spec: clusterv1.ClusterSpec{ + ClusterNetwork: clusterv1.ClusterNetwork{ + APIServerPort: 4242, }, }, }, @@ -159,7 +160,7 @@ func TestService_Reconcile(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Main LB - i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("").Return(scw.ZoneFrPar1, nil) i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(tags, CAPSMainLBTag)).Return(nil, client.ErrNoItemFound) i.CreateLB(gomock.Any(), scw.ZoneFrPar1, "cluster", "LB-S", nil, false, append(tags, CAPSMainLBTag)).Return(&lb.LB{ ID: lbID, @@ -199,7 +200,7 @@ func TestService_Reconcile(t *testing.T) { }, asserts: func(g *WithT, c *scope.Cluster) { g.Expect(c.ScalewayCluster.Status.Network).ToNot(BeNil()) - g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(Equal(scw.StringPtr("42.42.42.42"))) + g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(BeEquivalentTo("42.42.42.42")) }, }, { @@ -207,33 +208,33 @@ func TestService_Reconcile(t *testing.T) { fields: fields{ Cluster: &scope.Cluster{ ScalewayCluster: &infrav1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{ + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ AllowedRanges: []infrav1.CIDR{"10.10.0.0/16"}, }, - ControlPlaneExtraLoadBalancers: []infrav1.LoadBalancerSpec{ - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-2")}, + ControlPlaneExtraLoadBalancers: []infrav1.LoadBalancer{ + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-2")}, }, }, }, Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: infrav1.UUID(privateNetworkID), }, }, }, - Cluster: &v1beta1.Cluster{ - ObjectMeta: v1.ObjectMeta{ + Cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, @@ -247,7 +248,7 @@ func TestService_Reconcile(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Main LB - i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("").Return(scw.ZoneFrPar1, nil) i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(tags, CAPSMainLBTag)).Return(&lb.LB{ ID: lbID, Name: "cluster", @@ -258,8 +259,8 @@ func TestService_Reconcile(t *testing.T) { }, nil) // Extra LBs - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil).Times(2) - i.GetZoneOrDefault(scw.StringPtr("fr-par-2")).Return(scw.ZoneFrPar2, nil) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil).Times(2) + i.GetZoneOrDefault("fr-par-2").Return(scw.ZoneFrPar2, nil) i.FindLBs(gomock.Any(), append(tags, CAPSExtraLBTag)).Return([]*lb.LB{ { ID: lbID1, @@ -475,8 +476,8 @@ func TestService_Reconcile(t *testing.T) { }, asserts: func(g *WithT, c *scope.Cluster) { g.Expect(c.ScalewayCluster.Status.Network).ToNot(BeNil()) - g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(Equal(scw.StringPtr(lbIP))) - g.Expect(c.ScalewayCluster.Status.Network.ExtraLoadBalancerIPs).To(Equal([]string{lbIP1, lbIP2, lbIP3})) + g.Expect(c.ScalewayCluster.Status.Network.LoadBalancerIP).To(BeEquivalentTo(lbIP)) + g.Expect(c.ScalewayCluster.Status.Network.ExtraLoadBalancerIPs).To(Equal([]infrav1.IPv4{lbIP1, lbIP2, lbIP3})) }, }, } @@ -525,28 +526,28 @@ func TestService_Delete(t *testing.T) { fields: fields{ Cluster: &scope.Cluster{ ScalewayCluster: &infrav1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, Spec: infrav1.ScalewayClusterSpec{ - Network: &infrav1.NetworkSpec{ - PrivateNetwork: &infrav1.PrivateNetworkSpec{ - Enabled: true, + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - ControlPlaneLoadBalancer: &infrav1.ControlPlaneLoadBalancerSpec{ + ControlPlaneLoadBalancer: infrav1.ControlPlaneLoadBalancer{ AllowedRanges: []infrav1.CIDR{"10.10.0.0/16"}, }, - ControlPlaneExtraLoadBalancers: []infrav1.LoadBalancerSpec{ - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-2")}, + ControlPlaneExtraLoadBalancers: []infrav1.LoadBalancer{ + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-2")}, }, }, }, Status: infrav1.ScalewayClusterStatus{ - Network: &infrav1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: infrav1.UUID(privateNetworkID), }, }, }, @@ -559,7 +560,7 @@ func TestService_Delete(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Main LB - i.GetZoneOrDefault(nil).Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("").Return(scw.ZoneFrPar1, nil) i.FindLB(gomock.Any(), scw.ZoneFrPar1, append(tags, CAPSMainLBTag)).Return(&lb.LB{ ID: lbID, Name: "cluster", diff --git a/internal/service/scaleway/lb/util/spec.go b/internal/service/scaleway/lb/util/spec.go index 74f85a9..d8cab11 100644 --- a/internal/service/scaleway/lb/util/spec.go +++ b/internal/service/scaleway/lb/util/spec.go @@ -1,9 +1,10 @@ package util import ( - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/scaleway-sdk-go/scw" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" ) // lbDefaultType is the default type of LB to be created if no type is provided by user. @@ -12,26 +13,16 @@ const lbDefaultType = "LB-S" // LBSpec returns the zone and type of the LoadBalancer based on the provided LoadBalancerSpec. // If the zone is not specified in the spec, it defaults to the zone of the Scaleway client. // If the type is not specified, it defaults to lbDefaultType. -func LBSpec(c client.Zones, spec infrav1.LoadBalancerSpec) (zone scw.Zone, lbType string, err error) { - zone, err = c.GetZoneOrDefault(spec.Zone) +func LBSpec(c client.Zones, spec infrav1.LoadBalancer) (zone scw.Zone, lbType string, err error) { + zone, err = c.GetZoneOrDefault(string(spec.Zone)) if err != nil { return } lbType = lbDefaultType - if spec.Type != nil { - lbType = *spec.Type + if spec.Type != "" { + lbType = spec.Type } return } - -// MainLBSpec returns the zone and type of the main LoadBalancer for the ScalewayCluster. -func MainLBSpec(c client.Zones, scalewayCluster *infrav1.ScalewayCluster) (zone scw.Zone, lbType string, err error) { - var spec infrav1.LoadBalancerSpec - if scalewayCluster.Spec.Network != nil && scalewayCluster.Spec.Network.ControlPlaneLoadBalancer != nil { - spec = scalewayCluster.Spec.Network.ControlPlaneLoadBalancer.LoadBalancerSpec - } - - return LBSpec(c, spec) -} diff --git a/internal/service/scaleway/vpc/vpc.go b/internal/service/scaleway/vpc/vpc.go index 1d27289..3c1cc12 100644 --- a/internal/service/scaleway/vpc/vpc.go +++ b/internal/service/scaleway/vpc/vpc.go @@ -6,21 +6,26 @@ import ( "fmt" "time" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/utils/ptr" + "sigs.k8s.io/cluster-api/util/conditions" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" - "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" - utilerrors "k8s.io/apimachinery/pkg/util/errors" ) type Scope interface { scope.Interface + conditions.Setter HasPrivateNetwork() bool IsVPCStatusSet() bool SetVPCStatus(privateNetworkID, VPCID string) - PrivateNetworkParams() infrav1.PrivateNetworkParams + PrivateNetwork() infrav1.PrivateNetwork } type Service struct { @@ -40,18 +45,19 @@ func (s *Service) Delete(ctx context.Context) error { return nil } - params := s.PrivateNetworkParams() + params := s.PrivateNetwork() // User has provided his private network, we should not touch it. - if params.ID != nil { + if params.ID != "" { return nil } - pn, err := s.Cloud().FindPrivateNetwork( - ctx, - s.ResourceTags(), - params.VPCID, - ) + var vpcID *string + if params.VPCID != "" { + vpcID = ptr.To(string(params.VPCID)) + } + + pn, err := s.Cloud().FindPrivateNetwork(ctx, s.ResourceTags(), vpcID) if err != nil { if errors.Is(err, client.ErrNoItemFound) { return nil @@ -80,63 +86,84 @@ func (s *Service) Delete(ctx context.Context) error { func (s *Service) Reconcile(ctx context.Context) error { if !s.HasPrivateNetwork() { + conditions.Set(s, metav1.Condition{ + Type: infrav1.PrivateNetworkReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.NoPrivateNetworkReason, + }) return nil } - // If the VPC and Private Network IDs are already set in the status, we don't need to do anything. - if s.IsVPCStatusSet() { + // If status is already configured, we don't need to do anything. + if conditions.IsTrue(s, infrav1.PrivateNetworkReadyCondition) && s.IsVPCStatusSet() { return nil } - params := s.PrivateNetworkParams() + params := s.PrivateNetwork() var err error var pn *vpc.PrivateNetwork - if pnID := params.ID; pnID != nil { - pn, err = s.Cloud().GetPrivateNetwork(ctx, *pnID) + if pnID := params.ID; pnID != "" { + pn, err = s.Cloud().GetPrivateNetwork(ctx, string(pnID)) if err != nil { + conditions.Set(s, metav1.Condition{ + Type: infrav1.PrivateNetworkReadyCondition, + Status: metav1.ConditionFalse, + Reason: infrav1.PrivateNetworkNotFoundReason, + Message: err.Error(), + }) return fmt.Errorf("failed to get existing Private Network: %w", err) } } else { pn, err = s.getOrCreatePN(ctx, params) if err != nil { + conditions.Set(s, metav1.Condition{ + Type: infrav1.PrivateNetworkReadyCondition, + Status: metav1.ConditionFalse, + Reason: infrav1.CreationFailedReason, + Message: err.Error(), + }) return fmt.Errorf("failed to get or create Private Network: %w", err) } } s.SetVPCStatus(pn.ID, pn.VpcID) + conditions.Set(s, metav1.Condition{ + Type: infrav1.PrivateNetworkReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.ReadyReason, + }) + return nil } -func (s *Service) getOrCreatePN(ctx context.Context, params infrav1.PrivateNetworkParams) (*vpc.PrivateNetwork, error) { - pn, err := s.Cloud().FindPrivateNetwork( - ctx, - s.ResourceTags(), - params.VPCID, - ) +func (s *Service) getOrCreatePN(ctx context.Context, params infrav1.PrivateNetwork) (*vpc.PrivateNetwork, error) { + var vpcID *string + if params.VPCID != "" { + vpcID = ptr.To(string(params.VPCID)) + } + + pn, err := s.Cloud().FindPrivateNetwork(ctx, s.ResourceTags(), vpcID) if err := utilerrors.FilterOut(err, client.IsNotFoundError); err != nil { return nil, err } + var subnet *string + if params.Subnet != "" { + subnet = ptr.To(string(params.Subnet)) + } + if pn == nil { - pn, err = s.Cloud().CreatePrivateNetwork( - ctx, - s.ResourceName(), - params.VPCID, - params.Subnet, - s.ResourceTags(), - ) + pn, err = s.Cloud().CreatePrivateNetwork(ctx, s.ResourceName(), vpcID, subnet, s.ResourceTags()) if err != nil { return nil, err } } if !pn.DHCPEnabled { - return nil, scaleway.WithTerminalError( - fmt.Errorf("Private Network with ID %s is not supported: DHCP is not enabled", pn.ID), - ) + return nil, fmt.Errorf("Private Network with ID %s is not supported: DHCP is not enabled", pn.ID) } return pn, nil diff --git a/internal/service/scaleway/vpc/vpc_test.go b/internal/service/scaleway/vpc/vpc_test.go index dbfa4d2..bf89600 100644 --- a/internal/service/scaleway/vpc/vpc_test.go +++ b/internal/service/scaleway/vpc/vpc_test.go @@ -5,14 +5,16 @@ import ( "testing" . "github.com/onsi/gomega" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + + "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" + "go.uber.org/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" - "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" - "github.com/scaleway/scaleway-sdk-go/scw" - "go.uber.org/mock/gomock" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( @@ -40,7 +42,7 @@ func TestService_Reconcile(t *testing.T) { name: "no private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{}, + ScalewayCluster: &infrav1.ScalewayCluster{}, }, }, args: args{ @@ -53,18 +55,25 @@ func TestService_Reconcile(t *testing.T) { name: "IDs already set in status", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + ScalewayCluster: &infrav1.ScalewayCluster{ + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - VPCID: scw.StringPtr(vpcID), - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + VPCID: infrav1.UUID(vpcID), + PrivateNetworkID: infrav1.UUID(privateNetworkID), + }, + Conditions: []metav1.Condition{ + { + Type: infrav1.PrivateNetworkReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.ReadyReason, + }, }, }, }, @@ -80,15 +89,15 @@ func TestService_Reconcile(t *testing.T) { name: "managed private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, @@ -115,25 +124,25 @@ func TestService_Reconcile(t *testing.T) { clusterScope, ok := s.(*scope.Cluster) g.Expect(ok).To(BeTrue()) g.Expect(clusterScope.ScalewayCluster.Status.Network).NotTo(BeNil()) - g.Expect(clusterScope.ScalewayCluster.Status.Network.PrivateNetworkID).To(Equal(scw.StringPtr(privateNetworkID))) - g.Expect(clusterScope.ScalewayCluster.Status.Network.VPCID).To(Equal(scw.StringPtr(vpcID))) + g.Expect(clusterScope.ScalewayCluster.Status.Network.PrivateNetworkID).To(BeEquivalentTo(privateNetworkID)) + g.Expect(clusterScope.ScalewayCluster.Status.Network.VPCID).To(BeEquivalentTo(vpcID)) }, }, { name: "existing private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, - PrivateNetworkParams: v1alpha1.PrivateNetworkParams{ - ID: scw.StringPtr(privateNetworkID), + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), + PrivateNetwork: infrav1.PrivateNetwork{ + ID: infrav1.UUID(privateNetworkID), }, }, }, @@ -155,8 +164,8 @@ func TestService_Reconcile(t *testing.T) { g.Expect(ok).To(BeTrue()) g.Expect(clusterScope.ScalewayCluster.Status.Network).NotTo(BeNil()) - g.Expect(clusterScope.ScalewayCluster.Status.Network.PrivateNetworkID).To(Equal(scw.StringPtr(privateNetworkID))) - g.Expect(clusterScope.ScalewayCluster.Status.Network.VPCID).To(Equal(scw.StringPtr(vpcID))) + g.Expect(clusterScope.ScalewayCluster.Status.Network.PrivateNetworkID).To(BeEquivalentTo(privateNetworkID)) + g.Expect(clusterScope.ScalewayCluster.Status.Network.VPCID).To(BeEquivalentTo(vpcID)) }, }, } @@ -204,7 +213,7 @@ func TestService_Delete(t *testing.T) { name: "no private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{}, + ScalewayCluster: &infrav1.ScalewayCluster{}, }, }, args: args{ @@ -216,15 +225,15 @@ func TestService_Delete(t *testing.T) { name: "find and delete", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, @@ -251,17 +260,17 @@ func TestService_Delete(t *testing.T) { name: "do not remove user-provided private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, - PrivateNetworkParams: v1alpha1.PrivateNetworkParams{ - ID: scw.StringPtr(privateNetworkID), + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), + PrivateNetwork: infrav1.PrivateNetwork{ + ID: infrav1.UUID(privateNetworkID), }, }, }, diff --git a/internal/service/scaleway/vpcgw/vpcgw.go b/internal/service/scaleway/vpcgw/vpcgw.go index bdf4b85..ed278ff 100644 --- a/internal/service/scaleway/vpcgw/vpcgw.go +++ b/internal/service/scaleway/vpcgw/vpcgw.go @@ -7,14 +7,17 @@ import ( "strconv" "time" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" + "github.com/scaleway/scaleway-sdk-go/scw" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/cluster-api/util/conditions" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client" "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/common" - "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" - "github.com/scaleway/scaleway-sdk-go/scw" - logf "sigs.k8s.io/controller-runtime/pkg/log" ) // When capsManagedIPTag is set on a Public Gateway, its IP will be removed on @@ -23,10 +26,11 @@ const capsManagedIPTag = "caps-vpcgw-ip=managed" type Scope interface { scope.Interface + conditions.Setter HasPrivateNetwork() bool PrivateNetworkID() (string, error) - PublicGateways() []infrav1.PublicGatewaySpec + PublicGateways() []infrav1.PublicGateway } type Service struct { Scope @@ -41,13 +45,13 @@ func (s Service) Name() string { } func (s *Service) ensureGateways(ctx context.Context, delete bool) ([]*vpcgw.Gateway, error) { - var desired []infrav1.PublicGatewaySpec + var desired []infrav1.PublicGateway // When delete is set, we ensure an empty list of Gateways to remove everything. if !delete { desired = s.PublicGateways() } - drle := &common.ResourceEnsurer[infrav1.PublicGatewaySpec, *vpcgw.Gateway]{ + drle := &common.ResourceEnsurer[infrav1.PublicGateway, *vpcgw.Gateway]{ ResourceReconciler: &desiredResourceListManager{s.Scope, make(map[scw.Zone][]string)}, } return drle.Do(ctx, desired) @@ -78,11 +82,22 @@ func (s *Service) ensureGatewaysAttachment(ctx context.Context, gateways []*vpcg func (s *Service) Reconcile(ctx context.Context) error { if !s.HasPrivateNetwork() { + conditions.Set(s, metav1.Condition{ + Type: infrav1.PublicGatewaysReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.NoPrivateNetworkReason, + }) return nil } gateways, err := s.ensureGateways(ctx, false) if err != nil { + conditions.Set(s, metav1.Condition{ + Type: infrav1.PublicGatewaysReadyCondition, + Status: metav1.ConditionFalse, + Reason: infrav1.ReconciliationFailedReason, + Message: err.Error(), + }) return err } @@ -92,9 +107,21 @@ func (s *Service) Reconcile(ctx context.Context) error { } if err := s.ensureGatewaysAttachment(ctx, gateways, pnID); err != nil { + conditions.Set(s, metav1.Condition{ + Type: infrav1.PublicGatewaysReadyCondition, + Status: metav1.ConditionFalse, + Reason: infrav1.PrivateNetworkAttachmentFailedReason, + Message: err.Error(), + }) return err } + conditions.Set(s, metav1.Condition{ + Type: infrav1.PublicGatewaysReadyCondition, + Status: metav1.ConditionTrue, + Reason: infrav1.ProvisionedReason, + }) + return nil } @@ -139,17 +166,17 @@ func (d *desiredResourceListManager) DeleteResource(ctx context.Context, resourc func (d *desiredResourceListManager) UpdateResource( ctx context.Context, resource *vpcgw.Gateway, - desired infrav1.PublicGatewaySpec, + desired infrav1.PublicGateway, ) (*vpcgw.Gateway, error) { - if desired.Type != nil && *desired.Type != resource.Type { - canUpgradeType, err := d.canUpgradeType(ctx, resource.Zone, resource.Type, *desired.Type) + if desired.Type != "" && desired.Type != resource.Type { + canUpgradeType, err := d.canUpgradeType(ctx, resource.Zone, resource.Type, desired.Type) if err != nil { return nil, err } if canUpgradeType { logf.FromContext(ctx).Info("Upgrading Gateway", "gatewayName", resource.Name, "zone", resource.Zone) - return d.Cloud().UpgradeGateway(ctx, resource.Zone, resource.ID, *desired.Type) + return d.Cloud().UpgradeGateway(ctx, resource.Zone, resource.ID, desired.Type) } } @@ -164,22 +191,22 @@ func (d *desiredResourceListManager) GetResourceName(resource *vpcgw.Gateway) st return resource.Name } -func (d *desiredResourceListManager) GetDesiredZone(desired infrav1.PublicGatewaySpec) (scw.Zone, error) { - return d.Cloud().GetZoneOrDefault(desired.Zone) +func (d *desiredResourceListManager) GetDesiredZone(desired infrav1.PublicGateway) (scw.Zone, error) { + return d.Cloud().GetZoneOrDefault(string(desired.Zone)) } func (d *desiredResourceListManager) ShouldKeepResource( ctx context.Context, resource *vpcgw.Gateway, - desired infrav1.PublicGatewaySpec, + desired infrav1.PublicGateway, ) (bool, error) { // Gateway has no IPv4, remove it and recreate it. if resource.IPv4 == nil { return false, nil } - if desired.Type != nil && *desired.Type != resource.Type { - canUpgradeType, err := d.canUpgradeType(ctx, resource.Zone, resource.Type, *desired.Type) + if desired.Type != "" && desired.Type != resource.Type { + canUpgradeType, err := d.canUpgradeType(ctx, resource.Zone, resource.Type, desired.Type) if err != nil { return false, err } @@ -189,11 +216,11 @@ func (d *desiredResourceListManager) ShouldKeepResource( } } - if desired.IP == nil && !slices.Contains(resource.Tags, capsManagedIPTag) { + if desired.IP == "" && !slices.Contains(resource.Tags, capsManagedIPTag) { return false, nil } - if desired.IP != nil && resource.IPv4.Address.String() != *desired.IP { + if desired.IP != "" && resource.IPv4.Address.String() != string(desired.IP) { return false, nil } @@ -208,17 +235,17 @@ func (d *desiredResourceListManager) CreateResource( ctx context.Context, zone scw.Zone, name string, - desired infrav1.PublicGatewaySpec, + desired infrav1.PublicGateway, ) (*vpcgw.Gateway, error) { var ipID *string var gwType string tags := d.ResourceTags() - if desired.IP != nil { - ip, err := d.Cloud().FindGatewayIP(ctx, zone, *desired.IP) + if desired.IP != "" { + ip, err := d.Cloud().FindGatewayIP(ctx, zone, string(desired.IP)) if err != nil { if client.IsNotFoundError(err) { - return nil, scaleway.WithTerminalError(fmt.Errorf("failed to find gateway ip: %w", err)) + return nil, fmt.Errorf("failed to find gateway ip: %w", err) } return nil, fmt.Errorf("failed to find gateway ip: %w", err) @@ -229,8 +256,8 @@ func (d *desiredResourceListManager) CreateResource( tags = append(tags, capsManagedIPTag) } - if desired.Type != nil { - gwType = *desired.Type + if desired.Type != "" { + gwType = desired.Type } logf.FromContext(ctx).Info("Creating Gateway", "gatewayName", name, "zone", zone) diff --git a/internal/service/scaleway/vpcgw/vpcgw_test.go b/internal/service/scaleway/vpcgw/vpcgw_test.go index 1fcba54..be95062 100644 --- a/internal/service/scaleway/vpcgw/vpcgw_test.go +++ b/internal/service/scaleway/vpcgw/vpcgw_test.go @@ -4,13 +4,15 @@ import ( "context" "testing" - "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" - "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" "github.com/scaleway/scaleway-sdk-go/api/vpcgw/v2" "github.com/scaleway/scaleway-sdk-go/scw" "go.uber.org/mock/gomock" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" + "github.com/scaleway/cluster-api-provider-scaleway/internal/service/scaleway/client/mock_client" ) const ( @@ -120,7 +122,7 @@ func TestService_Reconcile(t *testing.T) { name: "no private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{}, + ScalewayCluster: &infrav1.ScalewayCluster{}, }, }, expect: func(i *mock_client.MockInterfaceMockRecorder) {}, @@ -129,21 +131,21 @@ func TestService_Reconcile(t *testing.T) { name: "no gateway configured", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -158,21 +160,21 @@ func TestService_Reconcile(t *testing.T) { name: "no gateway configured: delete existing", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -192,26 +194,26 @@ func TestService_Reconcile(t *testing.T) { name: "gateways configured: up-to-date", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - PublicGateways: []v1alpha1.PublicGatewaySpec{ - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-2")}, - {Zone: scw.StringPtr("fr-par-3")}, + PublicGateways: []infrav1.PublicGateway{ + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-2")}, + {Zone: infrav1.ScalewayZone("fr-par-3")}, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -221,9 +223,9 @@ func TestService_Reconcile(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Indexing desired gateways by zone. - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil) - i.GetZoneOrDefault(scw.StringPtr("fr-par-2")).Return(scw.ZoneFrPar2, nil) - i.GetZoneOrDefault(scw.StringPtr("fr-par-3")).Return(scw.ZoneFrPar3, nil) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("fr-par-2").Return(scw.ZoneFrPar2, nil) + i.GetZoneOrDefault("fr-par-3").Return(scw.ZoneFrPar3, nil) i.FindGateways(gomock.Any(), tags).Return([]*vpcgw.Gateway{ { @@ -260,27 +262,27 @@ func TestService_Reconcile(t *testing.T) { name: "gateways configured: create missing", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - PublicGateways: []v1alpha1.PublicGatewaySpec{ - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-1"), IP: scw.StringPtr("42.42.42.42")}, - {Zone: scw.StringPtr("fr-par-1")}, + PublicGateways: []infrav1.PublicGateway{ + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-1"), IP: infrav1.IPv4("42.42.42.42")}, + {Zone: infrav1.ScalewayZone("fr-par-1")}, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -290,7 +292,7 @@ func TestService_Reconcile(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Indexing desired gateways by zone. - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil).Times(4) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil).Times(4) i.FindGateways(gomock.Any(), tags).Return([]*vpcgw.Gateway{ { @@ -322,7 +324,7 @@ func TestService_Reconcile(t *testing.T) { scw.ZoneFrPar1, "cluster-2", "", tags, - scw.StringPtr(ipID), + ptr.To(ipID), ).Return(&vpcgw.Gateway{ ID: gwID3, Name: "cluster-2", @@ -351,26 +353,26 @@ func TestService_Reconcile(t *testing.T) { name: "gateways configured: upgrade", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - PublicGateways: []v1alpha1.PublicGatewaySpec{ - {Zone: scw.StringPtr("fr-par-1"), Type: scw.StringPtr("VPC-GW-S")}, - {Zone: scw.StringPtr("fr-par-2"), Type: scw.StringPtr("VPC-GW-S")}, - {Zone: scw.StringPtr("fr-par-3"), Type: scw.StringPtr("VPC-GW-M")}, + PublicGateways: []infrav1.PublicGateway{ + {Zone: infrav1.ScalewayZone("fr-par-1"), Type: "VPC-GW-S"}, + {Zone: infrav1.ScalewayZone("fr-par-2"), Type: "VPC-GW-S"}, + {Zone: infrav1.ScalewayZone("fr-par-3"), Type: "VPC-GW-M"}, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, @@ -380,9 +382,9 @@ func TestService_Reconcile(t *testing.T) { tags := []string{"caps-namespace=default", "caps-scalewaycluster=cluster"} // Indexing desired gateways by zone. - i.GetZoneOrDefault(scw.StringPtr("fr-par-1")).Return(scw.ZoneFrPar1, nil) - i.GetZoneOrDefault(scw.StringPtr("fr-par-2")).Return(scw.ZoneFrPar2, nil) - i.GetZoneOrDefault(scw.StringPtr("fr-par-3")).Return(scw.ZoneFrPar3, nil) + i.GetZoneOrDefault("fr-par-1").Return(scw.ZoneFrPar1, nil) + i.GetZoneOrDefault("fr-par-2").Return(scw.ZoneFrPar2, nil) + i.GetZoneOrDefault("fr-par-3").Return(scw.ZoneFrPar3, nil) i.FindGateways(gomock.Any(), tags).Return([]*vpcgw.Gateway{ { @@ -473,7 +475,7 @@ func TestService_Delete(t *testing.T) { name: "no private network", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{}, + ScalewayCluster: &infrav1.ScalewayCluster{}, }, }, expect: func(i *mock_client.MockInterfaceMockRecorder) {}, @@ -482,26 +484,26 @@ func TestService_Delete(t *testing.T) { name: "delete gateways", fields: fields{ Scope: &scope.Cluster{ - ScalewayCluster: &v1alpha1.ScalewayCluster{ - ObjectMeta: v1.ObjectMeta{ + ScalewayCluster: &infrav1.ScalewayCluster{ + ObjectMeta: metav1.ObjectMeta{ Name: "cluster", Namespace: "default", }, - Spec: v1alpha1.ScalewayClusterSpec{ - Network: &v1alpha1.NetworkSpec{ - PrivateNetwork: &v1alpha1.PrivateNetworkSpec{ - Enabled: true, + Spec: infrav1.ScalewayClusterSpec{ + Network: infrav1.ScalewayClusterNetwork{ + PrivateNetwork: infrav1.PrivateNetworkSpec{ + Enabled: ptr.To(true), }, - PublicGateways: []v1alpha1.PublicGatewaySpec{ - {Zone: scw.StringPtr("fr-par-1")}, - {Zone: scw.StringPtr("fr-par-2")}, - {Zone: scw.StringPtr("fr-par-3")}, + PublicGateways: []infrav1.PublicGateway{ + {Zone: infrav1.ScalewayZone("fr-par-1")}, + {Zone: infrav1.ScalewayZone("fr-par-2")}, + {Zone: infrav1.ScalewayZone("fr-par-3")}, }, }, }, - Status: v1alpha1.ScalewayClusterStatus{ - Network: &v1alpha1.NetworkStatus{ - PrivateNetworkID: scw.StringPtr(privateNetworkID), + Status: infrav1.ScalewayClusterStatus{ + Network: infrav1.ScalewayClusterNetworkStatus{ + PrivateNetworkID: privateNetworkID, }, }, }, diff --git a/internal/webhook/v1alpha2/scalewaycluster_webhook.go b/internal/webhook/v1alpha2/scalewaycluster_webhook.go new file mode 100644 index 0000000..d46e701 --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaycluster_webhook.go @@ -0,0 +1,18 @@ +package v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// nolint:unused +// log is for logging in this package. +var scalewayclusterlog = logf.Log.WithName("scalewaycluster-resource") + +// SetupScalewayClusterWebhookWithManager registers the webhook for ScalewayCluster in the manager. +func SetupScalewayClusterWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayCluster{}). + Complete() +} diff --git a/internal/webhook/v1alpha2/scalewayclustertemplate_webhook.go b/internal/webhook/v1alpha2/scalewayclustertemplate_webhook.go new file mode 100644 index 0000000..d3f30f8 --- /dev/null +++ b/internal/webhook/v1alpha2/scalewayclustertemplate_webhook.go @@ -0,0 +1,18 @@ +package v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// nolint:unused +// log is for logging in this package. +var scalewayclustertemplatelog = logf.Log.WithName("scalewayclustertemplate-resource") + +// SetupScalewayClusterTemplateWebhookWithManager registers the webhook for ScalewayClusterTemplate in the manager. +func SetupScalewayClusterTemplateWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayClusterTemplate{}). + Complete() +} diff --git a/internal/webhook/v1alpha2/scalewaymachine_webhook.go b/internal/webhook/v1alpha2/scalewaymachine_webhook.go new file mode 100644 index 0000000..4649b5c --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaymachine_webhook.go @@ -0,0 +1,18 @@ +package v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// nolint:unused +// log is for logging in this package. +var scalewaymachinelog = logf.Log.WithName("scalewaymachine-resource") + +// SetupScalewayMachineWebhookWithManager registers the webhook for ScalewayMachine in the manager. +func SetupScalewayMachineWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayMachine{}). + Complete() +} diff --git a/internal/webhook/v1alpha2/scalewaymachinetemplate_webhook.go b/internal/webhook/v1alpha2/scalewaymachinetemplate_webhook.go new file mode 100644 index 0000000..a3b4dde --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaymachinetemplate_webhook.go @@ -0,0 +1,18 @@ +package v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// nolint:unused +// log is for logging in this package. +var scalewaymachinetemplatelog = logf.Log.WithName("scalewaymachinetemplate-resource") + +// SetupScalewayMachineTemplateWebhookWithManager registers the webhook for ScalewayMachineTemplate in the manager. +func SetupScalewayMachineTemplateWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayMachineTemplate{}). + Complete() +} diff --git a/internal/webhook/v1alpha2/scalewaymanagedcluster_webhook.go b/internal/webhook/v1alpha2/scalewaymanagedcluster_webhook.go new file mode 100644 index 0000000..63763a7 --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaymanagedcluster_webhook.go @@ -0,0 +1,18 @@ +package v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// nolint:unused +// log is for logging in this package. +var scalewaymanagedclusterlog = logf.Log.WithName("scalewaymanagedcluster-resource") + +// SetupScalewayManagedClusterWebhookWithManager registers the webhook for ScalewayManagedCluster in the manager. +func SetupScalewayManagedClusterWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayManagedCluster{}). + Complete() +} diff --git a/internal/webhook/v1alpha2/scalewaymanagedcontrolplane_webhook.go b/internal/webhook/v1alpha2/scalewaymanagedcontrolplane_webhook.go new file mode 100644 index 0000000..fbf485b --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaymanagedcontrolplane_webhook.go @@ -0,0 +1,58 @@ +package v1alpha2 + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" + "github.com/scaleway/cluster-api-provider-scaleway/internal/scope" +) + +// nolint:unused +// log is for logging in this package. +var scalewaymanagedcontrolplanelog = logf.Log.WithName("scalewaymanagedcontrolplane-resource") + +// SetupScalewayManagedControlPlaneWebhookWithManager registers the webhook for ScalewayManagedControlPlane in the manager. +func SetupScalewayManagedControlPlaneWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayManagedControlPlane{}). + WithDefaulter(&ScalewayManagedControlPlaneCustomDefaulter{}). + Complete() +} + +// +kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1alpha2-scalewaymanagedcontrolplane,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=scalewaymanagedcontrolplanes,verbs=create;update,versions=v1alpha2,name=mscalewaymanagedcontrolplane-v1alpha2.kb.io,admissionReviewVersions=v1 + +// ScalewayManagedControlPlaneCustomDefaulter struct is responsible for setting default values on the custom resource of the +// Kind ScalewayManagedControlPlane when those are created or updated. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as it is used only for temporary operations and does not need to be deeply copied. +type ScalewayManagedControlPlaneCustomDefaulter struct { +} + +var _ webhook.CustomDefaulter = &ScalewayManagedControlPlaneCustomDefaulter{} + +// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind ScalewayManagedControlPlane. +func (d *ScalewayManagedControlPlaneCustomDefaulter) Default(_ context.Context, obj runtime.Object) error { + scalewaymanagedcontrolplane, ok := obj.(*infrav1.ScalewayManagedControlPlane) + if !ok { + return fmt.Errorf("expected an ScalewayManagedControlPlane object but got %T", obj) + } + + scalewaymanagedcontrolplanelog.Info("Defaulting for ScalewayManagedControlPlane", "name", scalewaymanagedcontrolplane.GetName()) + + if scalewaymanagedcontrolplane.Spec.ClusterName == "" { + name, err := scope.GenerateClusterName(scalewaymanagedcontrolplane) + if err != nil { + return err + } + + scalewaymanagedcontrolplane.Spec.ClusterName = name + } + + return nil +} diff --git a/internal/webhook/v1alpha2/scalewaymanagedcontrolplane_webhook_test.go b/internal/webhook/v1alpha2/scalewaymanagedcontrolplane_webhook_test.go new file mode 100644 index 0000000..88ed471 --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaymanagedcontrolplane_webhook_test.go @@ -0,0 +1,46 @@ +package v1alpha2 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +var _ = Describe("ScalewayManagedControlPlane Webhook", func() { + var ( + obj *infrav1.ScalewayManagedControlPlane + oldObj *infrav1.ScalewayManagedControlPlane + defaulter ScalewayManagedControlPlaneCustomDefaulter + ) + + BeforeEach(func() { + obj = &infrav1.ScalewayManagedControlPlane{} + oldObj = &infrav1.ScalewayManagedControlPlane{} + defaulter = ScalewayManagedControlPlaneCustomDefaulter{} + Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized") + Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") + Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") + }) + + Context("When creating ScalewayManagedControlPlane under Defaulting Webhook", func() { + It("Should initialize clusterName if not set", func() { + obj.Name = "mycluster" + obj.Namespace = "default" + By("calling the Default method to apply defaults") + Expect(defaulter.Default(ctx, obj)).To(Succeed()) + By("checking that the default values are set") + Expect(obj.Spec.ClusterName).To(Equal("default-mycluster")) + }) + + It("Should keep existing clusterName", func() { + obj.Name = "mycluster" + obj.Namespace = "default" + obj.Spec.ClusterName = "test-cluster-1" + By("calling the Default method to apply defaults") + Expect(defaulter.Default(ctx, obj)).To(Succeed()) + By("checking that the default values are set") + Expect(obj.Spec.ClusterName).To(Equal("test-cluster-1")) + }) + }) +}) diff --git a/internal/webhook/v1alpha2/scalewaymanagedmachinepool_webhook.go b/internal/webhook/v1alpha2/scalewaymanagedmachinepool_webhook.go new file mode 100644 index 0000000..676b449 --- /dev/null +++ b/internal/webhook/v1alpha2/scalewaymanagedmachinepool_webhook.go @@ -0,0 +1,18 @@ +package v1alpha2 + +import ( + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// nolint:unused +// log is for logging in this package. +var scalewaymanagedmachinepoollog = logf.Log.WithName("scalewaymanagedmachinepool-resource") + +// SetupScalewayManagedMachinePoolWebhookWithManager registers the webhook for ScalewayManagedMachinePool in the manager. +func SetupScalewayManagedMachinePoolWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&infrav1.ScalewayManagedMachinePool{}). + Complete() +} diff --git a/internal/webhook/v1alpha2/webhook_suite_test.go b/internal/webhook/v1alpha2/webhook_suite_test.go new file mode 100644 index 0000000..a2f1138 --- /dev/null +++ b/internal/webhook/v1alpha2/webhook_suite_test.go @@ -0,0 +1,147 @@ +package v1alpha2 + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "os" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + k8sClient client.Client + cfg *rest.Config + testEnv *envtest.Environment +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Webhook Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = infrav1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: false, + + WebhookInstallOptions: envtest.WebhookInstallOptions{ + Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, + }, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + // start webhook server using Manager. + webhookInstallOptions := &testEnv.WebhookInstallOptions + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + LeaderElection: false, + Metrics: metricsserver.Options{BindAddress: "0"}, + }) + Expect(err).NotTo(HaveOccurred()) + + err = SetupScalewayManagedControlPlaneWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:webhook + + go func() { + defer GinkgoRecover() + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + + // wait for the webhook server to get ready. + dialer := &net.Dialer{Timeout: time.Second} + addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) + Eventually(func() error { + conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if err != nil { + return err + } + + return conn.Close() + }).Should(Succeed()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/metadata.yaml b/metadata.yaml index 1045d3d..cb2c5ce 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -6,6 +6,9 @@ apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 kind: Metadata releaseSeries: + - major: 0 + minor: 2 + contract: v1beta2 - major: 0 minor: 1 contract: v1beta1 diff --git a/templates/cluster-template-managed.yaml b/templates/cluster-template-managed.yaml index 17e628c..e984779 100644 --- a/templates/cluster-template-managed.yaml +++ b/templates/cluster-template-managed.yaml @@ -1,18 +1,18 @@ -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: ${CLUSTER_NAME} spec: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayManagedCluster name: ${CLUSTER_NAME} controlPlaneRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayManagedControlPlane name: ${CLUSTER_NAME}-control-plane --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedCluster metadata: name: ${CLUSTER_NAME} @@ -20,11 +20,7 @@ spec: projectID: ${SCW_PROJECT_ID} region: ${SCW_REGION} scalewaySecretName: ${CLUSTER_NAME} - network: - privateNetwork: - id: ${PRIVATE_NETWORK_ID:=null} - vpcID: ${VPC_ID:=null} - publicGateways: ${PUBLIC_GATEWAYS:=null} + network: ${SMC_NETWORK:=null} --- apiVersion: v1 kind: Secret @@ -35,7 +31,7 @@ stringData: SCW_ACCESS_KEY: ${SCW_ACCESS_KEY} SCW_SECRET_KEY: ${SCW_SECRET_KEY} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedControlPlane metadata: name: ${CLUSTER_NAME}-control-plane @@ -45,7 +41,7 @@ spec: onDelete: withAdditionalResources: true --- -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: MachinePool metadata: name: "${CLUSTER_NAME}-mp-0" @@ -58,12 +54,12 @@ spec: dataSecretName: "" clusterName: "${CLUSTER_NAME}" infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayManagedMachinePool name: "${CLUSTER_NAME}-mp-0" version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayManagedMachinePool metadata: name: "${CLUSTER_NAME}-mp-0" diff --git a/templates/cluster-template-private-network.yaml b/templates/cluster-template-private-network.yaml index f258579..0c19342 100644 --- a/templates/cluster-template-private-network.yaml +++ b/templates/cluster-template-private-network.yaml @@ -1,4 +1,4 @@ -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: ${CLUSTER_NAME} @@ -9,15 +9,15 @@ spec: cidrBlocks: - 10.244.0.0/16 controlPlaneRef: - apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + apiGroup: controlplane.cluster.x-k8s.io kind: KubeadmControlPlane name: ${CLUSTER_NAME}-control-plane infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayCluster name: ${CLUSTER_NAME} --- -apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +apiVersion: controlplane.cluster.x-k8s.io/v1beta2 kind: KubeadmControlPlane metadata: name: ${CLUSTER_NAME}-control-plane @@ -28,9 +28,12 @@ spec: advertiseAddress: "[[[ .NodeIP ]]]" nodeRegistration: kubeletExtraArgs: - provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" - node-ip: "[[[ .NodeIP ]]]" - cloud-provider: external + - name: provider-id + value: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" + - name: node-ip + value: "[[[ .NodeIP ]]]" + - name: cloud-provider + value: external name: "{{ ds.meta_data.hostname }}" joinConfiguration: controlPlane: @@ -38,19 +41,23 @@ spec: advertiseAddress: "[[[ .NodeIP ]]]" nodeRegistration: kubeletExtraArgs: - provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" - node-ip: "[[[ .NodeIP ]]]" - cloud-provider: external + - name: provider-id + value: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" + - name: node-ip + value: "[[[ .NodeIP ]]]" + - name: cloud-provider + value: external name: "{{ ds.meta_data.hostname }}" machineTemplate: - infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 - kind: ScalewayMachineTemplate - name: ${CLUSTER_NAME}-control-plane + spec: + infrastructureRef: + apiGroup: infrastructure.cluster.x-k8s.io + kind: ScalewayMachineTemplate + name: ${CLUSTER_NAME}-control-plane replicas: ${CONTROL_PLANE_MACHINE_COUNT:=1} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: ${CLUSTER_NAME} @@ -67,7 +74,7 @@ spec: vpcID: ${VPC_ID:=null} controlPlaneLoadBalancer: private: ${CONTROL_PLANE_LOAD_BALANCER_PRIVATE:=false} - publicGateways: ${PUBLIC_GATEWAYS:=[]} + publicGateways: ${PUBLIC_GATEWAYS:=null} --- apiVersion: v1 kind: Secret @@ -78,7 +85,7 @@ stringData: SCW_ACCESS_KEY: ${SCW_ACCESS_KEY} SCW_SECRET_KEY: ${SCW_SECRET_KEY} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -93,7 +100,7 @@ spec: publicNetwork: enableIPv4: ${CONTROL_PLANE_MACHINE_IPV4:=true} --- -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: MachineDeployment metadata: name: ${CLUSTER_NAME}-md-0 @@ -106,18 +113,18 @@ spec: spec: bootstrap: configRef: - apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + apiGroup: bootstrap.cluster.x-k8s.io kind: KubeadmConfigTemplate name: ${CLUSTER_NAME}-md-0 clusterName: ${CLUSTER_NAME} failureDomain: ${WORKER_FAILURE_DOMAIN:=${SCW_REGION}-1} infrastructureRef: name: ${CLUSTER_NAME}-md-0 - apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayMachineTemplate version: ${KUBERNETES_VERSION} --- -apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 kind: KubeadmConfigTemplate metadata: name: ${CLUSTER_NAME}-md-0 @@ -127,12 +134,15 @@ spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: - provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" - node-ip: "[[[ .NodeIP ]]]" - cloud-provider: external + - name: provider-id + value: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" + - name: node-ip + value: "[[[ .NodeIP ]]]" + - name: cloud-provider + value: external name: "{{ ds.meta_data.hostname }}" --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 diff --git a/templates/cluster-template.yaml b/templates/cluster-template.yaml index 0c891a1..d9a040c 100644 --- a/templates/cluster-template.yaml +++ b/templates/cluster-template.yaml @@ -1,4 +1,4 @@ -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: ${CLUSTER_NAME} @@ -9,15 +9,15 @@ spec: cidrBlocks: - 10.244.0.0/16 controlPlaneRef: - apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + apiGroup: controlplane.cluster.x-k8s.io kind: KubeadmControlPlane name: ${CLUSTER_NAME}-control-plane infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayCluster name: ${CLUSTER_NAME} --- -apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +apiVersion: controlplane.cluster.x-k8s.io/v1beta2 kind: KubeadmControlPlane metadata: name: ${CLUSTER_NAME}-control-plane @@ -26,24 +26,29 @@ spec: initConfiguration: nodeRegistration: kubeletExtraArgs: - provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" - cloud-provider: external + - name: provider-id + value: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" + - name: cloud-provider + value: external name: "{{ ds.meta_data.hostname }}" joinConfiguration: nodeRegistration: kubeletExtraArgs: - provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" - cloud-provider: external + - name: provider-id + value: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" + - name: cloud-provider + value: external name: "{{ ds.meta_data.hostname }}" machineTemplate: - infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 - kind: ScalewayMachineTemplate - name: ${CLUSTER_NAME}-control-plane + spec: + infrastructureRef: + apiGroup: infrastructure.cluster.x-k8s.io + kind: ScalewayMachineTemplate + name: ${CLUSTER_NAME}-control-plane replicas: ${CONTROL_PLANE_MACHINE_COUNT:=1} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayCluster metadata: name: ${CLUSTER_NAME} @@ -63,7 +68,7 @@ stringData: SCW_ACCESS_KEY: ${SCW_ACCESS_KEY} SCW_SECRET_KEY: ${SCW_SECRET_KEY} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -78,7 +83,7 @@ spec: publicNetwork: enableIPv4: true --- -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: MachineDeployment metadata: name: ${CLUSTER_NAME}-md-0 @@ -91,18 +96,18 @@ spec: spec: bootstrap: configRef: - apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + apiGroup: bootstrap.cluster.x-k8s.io kind: KubeadmConfigTemplate name: ${CLUSTER_NAME}-md-0 clusterName: ${CLUSTER_NAME} failureDomain: ${WORKER_FAILURE_DOMAIN:=${SCW_REGION}-1} infrastructureRef: - name: ${CLUSTER_NAME}-md-0 - apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 + apiGroup: infrastructure.cluster.x-k8s.io kind: ScalewayMachineTemplate + name: ${CLUSTER_NAME}-md-0 version: ${KUBERNETES_VERSION} --- -apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +apiVersion: bootstrap.cluster.x-k8s.io/v1beta2 kind: KubeadmConfigTemplate metadata: name: ${CLUSTER_NAME}-md-0 @@ -112,11 +117,13 @@ spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: - provider-id: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" - cloud-provider: external + - name: provider-id + value: "scaleway://instance/{{ ds.meta_data.zone }}/{{ ds.meta_data.id }}" + - name: cloud-provider + value: external name: "{{ ds.meta_data.hostname }}" --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: ScalewayMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 diff --git a/test/e2e/caps.go b/test/e2e/caps.go index fbff362..d96cd83 100644 --- a/test/e2e/caps.go +++ b/test/e2e/caps.go @@ -8,6 +8,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" "k8s.io/utils/ptr" capi_e2e "sigs.k8s.io/cluster-api/test/e2e" diff --git a/test/e2e/common.go b/test/e2e/common.go index f508b64..350b50e 100644 --- a/test/e2e/common.go +++ b/test/e2e/common.go @@ -6,8 +6,9 @@ import ( "path/filepath" . "github.com/onsi/ginkgo/v2" + corev1 "k8s.io/api/core/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/test/framework" "sigs.k8s.io/cluster-api/util" ) diff --git a/test/e2e/config/scaleway.yaml b/test/e2e/config/scaleway.yaml index 14187ac..f79d77b 100644 --- a/test/e2e/config/scaleway.yaml +++ b/test/e2e/config/scaleway.yaml @@ -8,45 +8,45 @@ providers: - name: cluster-api type: CoreProvider versions: - - name: v1.10.3 - value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.10.3/core-components.yaml + - name: v1.11.3 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.11.3/core-components.yaml type: url - contract: v1beta1 + contract: v1beta2 files: - - sourcePath: "../data/shared/v1beta1/metadata.yaml" + - sourcePath: "../data/shared/v1beta2/metadata.yaml" - name: kubeadm type: BootstrapProvider versions: - - name: v1.10.3 - value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.10.3/bootstrap-components.yaml + - name: v1.11.3 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.11.3/bootstrap-components.yaml type: url - contract: v1beta1 + contract: v1beta2 files: - - sourcePath: "../data/shared/v1beta1/metadata.yaml" + - sourcePath: "../data/shared/v1beta2/metadata.yaml" - name: kubeadm type: ControlPlaneProvider versions: - - name: v1.10.3 - value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.10.3/control-plane-components.yaml + - name: v1.11.3 + value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.11.3/control-plane-components.yaml type: url - contract: v1beta1 + contract: v1beta2 files: - - sourcePath: "../data/shared/v1beta1/metadata.yaml" + - sourcePath: "../data/shared/v1beta2/metadata.yaml" - name: scaleway type: InfrastructureProvider versions: - - name: v0.1.99 + - name: v0.2.99 value: "../../../config/default" - contract: v1beta1 + contract: v1beta2 files: - sourcePath: "../../../metadata.yaml" # Following files are built using `make generate-e2e`. - - sourcePath: "../data/infrastructure-scaleway/v1beta1/cluster-template.yaml" - - sourcePath: "../data/infrastructure-scaleway/v1beta1/cluster-template-private-network.yaml" - - sourcePath: "../data/infrastructure-scaleway/v1beta1/cluster-template-managed.yaml" + - sourcePath: "../data/infrastructure-scaleway/v1beta2/cluster-template.yaml" + - sourcePath: "../data/infrastructure-scaleway/v1beta2/cluster-template-private-network.yaml" + - sourcePath: "../data/infrastructure-scaleway/v1beta2/cluster-template-managed.yaml" replacements: - old: "imagePullPolicy: Always" new: "imagePullPolicy: IfNotPresent" diff --git a/test/e2e/data/addons/flannel.yaml b/test/e2e/data/addons/flannel.yaml index 3d45f13..a7daead 100644 --- a/test/e2e/data/addons/flannel.yaml +++ b/test/e2e/data/addons/flannel.yaml @@ -145,7 +145,7 @@ spec: value: "5000" - name: CONT_WHEN_CACHE_NOT_READY value: "false" - image: ghcr.io/flannel-io/flannel:v0.27.1 + image: ghcr.io/flannel-io/flannel:v0.27.3 name: kube-flannel resources: requests: @@ -183,7 +183,7 @@ spec: - /etc/cni/net.d/10-flannel.conflist command: - cp - image: ghcr.io/flannel-io/flannel:v0.27.1 + image: ghcr.io/flannel-io/flannel:v0.27.3 name: install-cni volumeMounts: - mountPath: /etc/cni/net.d diff --git a/test/e2e/data/infrastructure-scaleway/v1beta1/bases/crs-cni.yaml b/test/e2e/data/infrastructure-scaleway/v1beta2/bases/crs-cni.yaml similarity index 93% rename from test/e2e/data/infrastructure-scaleway/v1beta1/bases/crs-cni.yaml rename to test/e2e/data/infrastructure-scaleway/v1beta2/bases/crs-cni.yaml index 68a271a..107cabb 100644 --- a/test/e2e/data/infrastructure-scaleway/v1beta1/bases/crs-cni.yaml +++ b/test/e2e/data/infrastructure-scaleway/v1beta2/bases/crs-cni.yaml @@ -10,7 +10,7 @@ binaryData: --- # ClusterResourceSet object with # a selector that targets all the Cluster with label cni=${CLUSTER_NAME}-crs-cni -apiVersion: addons.cluster.x-k8s.io/v1beta1 +apiVersion: addons.cluster.x-k8s.io/v1beta2 kind: ClusterResourceSet metadata: name: "${CLUSTER_NAME}-crs-cni" diff --git a/test/e2e/data/infrastructure-scaleway/v1beta1/cluster-template-managed/kustomization.yaml b/test/e2e/data/infrastructure-scaleway/v1beta2/cluster-template-managed/kustomization.yaml similarity index 100% rename from test/e2e/data/infrastructure-scaleway/v1beta1/cluster-template-managed/kustomization.yaml rename to test/e2e/data/infrastructure-scaleway/v1beta2/cluster-template-managed/kustomization.yaml diff --git a/test/e2e/data/infrastructure-scaleway/v1beta1/cluster-template-private-network/kustomization.yaml b/test/e2e/data/infrastructure-scaleway/v1beta2/cluster-template-private-network/kustomization.yaml similarity index 100% rename from test/e2e/data/infrastructure-scaleway/v1beta1/cluster-template-private-network/kustomization.yaml rename to test/e2e/data/infrastructure-scaleway/v1beta2/cluster-template-private-network/kustomization.yaml diff --git a/test/e2e/data/infrastructure-scaleway/v1beta1/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-scaleway/v1beta2/cluster-template/kustomization.yaml similarity index 100% rename from test/e2e/data/infrastructure-scaleway/v1beta1/cluster-template/kustomization.yaml rename to test/e2e/data/infrastructure-scaleway/v1beta2/cluster-template/kustomization.yaml diff --git a/test/e2e/data/infrastructure-scaleway/v1beta1/patches/cluster_patch.yaml b/test/e2e/data/infrastructure-scaleway/v1beta2/patches/cluster_patch.yaml similarity index 71% rename from test/e2e/data/infrastructure-scaleway/v1beta1/patches/cluster_patch.yaml rename to test/e2e/data/infrastructure-scaleway/v1beta2/patches/cluster_patch.yaml index a7ba68d..df75095 100644 --- a/test/e2e/data/infrastructure-scaleway/v1beta1/patches/cluster_patch.yaml +++ b/test/e2e/data/infrastructure-scaleway/v1beta2/patches/cluster_patch.yaml @@ -1,4 +1,4 @@ -apiVersion: cluster.x-k8s.io/v1beta1 +apiVersion: cluster.x-k8s.io/v1beta2 kind: Cluster metadata: name: "${CLUSTER_NAME}" diff --git a/test/e2e/data/shared/v1beta1/metadata.yaml b/test/e2e/data/shared/v1beta2/metadata.yaml similarity index 92% rename from test/e2e/data/shared/v1beta1/metadata.yaml rename to test/e2e/data/shared/v1beta2/metadata.yaml index c95dcf9..346957a 100644 --- a/test/e2e/data/shared/v1beta1/metadata.yaml +++ b/test/e2e/data/shared/v1beta2/metadata.yaml @@ -1,6 +1,9 @@ apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 kind: Metadata releaseSeries: + - major: 1 + minor: 11 + contract: v1beta2 - major: 1 minor: 10 contract: v1beta1 diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index fa8b3ef..fba2279 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -13,7 +13,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/clientcmd" "k8s.io/klog/v2" @@ -22,6 +22,8 @@ import ( "sigs.k8s.io/cluster-api/test/framework/bootstrap" "sigs.k8s.io/cluster-api/test/framework/clusterctl" ctrl "sigs.k8s.io/controller-runtime" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) // Test suite flags diff --git a/test/e2e/managed.go b/test/e2e/managed.go index cf32a44..624eed1 100644 --- a/test/e2e/managed.go +++ b/test/e2e/managed.go @@ -7,14 +7,14 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "k8s.io/klog/v2" - "sigs.k8s.io/controller-runtime/pkg/client" - infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" + "k8s.io/klog/v2" + clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2" "sigs.k8s.io/cluster-api/test/framework" "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/controller-runtime/pkg/client" + + infrav1 "github.com/scaleway/cluster-api-provider-scaleway/api/v1alpha2" ) const ( @@ -43,7 +43,7 @@ type ApplyManagedClusterTemplateAndWaitResult struct { ClusterClass *clusterv1.ClusterClass Cluster *clusterv1.Cluster ControlPlane *infrav1.ScalewayManagedControlPlane - MachinePools []*expv1.MachinePool + MachinePools []*clusterv1.MachinePool } // ApplyManagedClusterTemplateAndWait gets a managed cluster template using clusterctl, and waits for the cluster to be ready. diff --git a/test/e2e/scaleway_managed_test.go b/test/e2e/scaleway_managed_test.go index ca94ab3..1e768e4 100644 --- a/test/e2e/scaleway_managed_test.go +++ b/test/e2e/scaleway_managed_test.go @@ -8,6 +8,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" "k8s.io/utils/ptr" capi_e2e "sigs.k8s.io/cluster-api/test/e2e" @@ -80,7 +81,7 @@ var _ = Describe("Managed workload cluster creation", func() { WorkerMachineCount: ptr.To[int64](3), ClusterctlVariables: map[string]string{ "WORKER_PUBLIC_IP_DISABLED": "true", - "PUBLIC_GATEWAYS": "[{}]", + "SMC_NETWORK": "{publicGateways: [{}]}", }, }, WaitForClusterIntervals: e2eConfig.GetIntervals(specName, "wait-cluster"),