diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..5f57e94c --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,51 @@ +version: 2 +updates: +# GitHub Actions +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + all-github-actions: + patterns: [ "*" ] + labels: + - "dependabot" + - "ok-to-test" + +# Go modules +- package-ecosystem: "gomod" + directories: + - "/" + - "/test/e2e" + - "/hack/tools" + schedule: + interval: "weekly" + day: "monday" + ## group all dependencies into a single PR. + groups: + all-go-mod-patch-and-minor: + patterns: [ "*" ] + update-types: [ "patch", "minor" ] + ignore: + # Ignore controller-runtime as its upgraded manually. + - dependency-name: "sigs.k8s.io/controller-runtime" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + # Ignore capi since it's tied to controller-runtime. + - dependency-name: "sigs.k8s.io/cluster-api" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + - dependency-name: "sigs.k8s.io/cluster-api/test" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + # Ignore k8s and its transitives modules as they are upgraded manually together with controller-runtime. + - dependency-name: "k8s.io/*" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + - dependency-name: "github.com/prometheus/*" + update-types: [ "version-update:semver-major", "version-update:semver-minor"] + # Ignore kind as its upgraded manually. + - dependency-name: "sigs.k8s.io/kind" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + # Bumping the kustomize API independently can break compatibility with client-go as they share k8s.io/kube-openapi as a dependency. + - dependency-name: "sigs.k8s.io/kustomize/api" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] + labels: + - "dependabot" + - "ok-to-test" diff --git a/.github/workflows/go-coverage.yml b/.github/workflows/go-coverage.yml index df8e8b86..d0d8e1f7 100644 --- a/.github/workflows/go-coverage.yml +++ b/.github/workflows/go-coverage.yml @@ -11,13 +11,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: 1.19.3 + go-version: 1.23 - name: Run go test with coverage run: COVER_PROFILE=coverage.txt make test - name: Codecov upload - uses: codecov/codecov-action@v3.1.0 + uses: codecov/codecov-action@v5.4.0 with: files: ./cover.out diff --git a/.golangci.yml b/.golangci.yml index 5cf6680a..b5e69d73 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -32,9 +32,7 @@ linters: run: issues-exit-code: 1 - skip-dirs: - - pkg/mocks - - test + tests: false issues: # Excluding configuration per-path, per-linter, per-text and per-source @@ -43,3 +41,6 @@ issues: - path: _test\.go linters: - gosec + exclude-dirs: + - pkg/mocks + - test diff --git a/Makefile b/Makefile index 2f388f43..8c8adb17 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,8 @@ GH_REPO ?= kubernetes-sigs/cluster-api-provider-cloudstack # Binaries CONTROLLER_GEN := $(TOOLS_BIN_DIR)/controller-gen -GINKGO_V1 := $(TOOLS_BIN_DIR)/ginkgo_v1 -GINKGO_V2 := $(TOOLS_BIN_DIR)/ginkgo_v2 +CONVERSION_GEN := $(TOOLS_BIN_DIR)/conversion-gen +GINKGO := $(TOOLS_BIN_DIR)/ginkgo GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize MOCKGEN := $(TOOLS_BIN_DIR)/mockgen @@ -70,10 +70,6 @@ endif # Options are set to exit when a recipe line exits non-zero or a piped command fails. # SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec -# Quiet Ginkgo for now. -# The warnings are in regards to a future release. -export ACK_GINKGO_DEPRECATIONS := 1.16.5 -export ACK_GINKGO_RC=true export PATH := $(TOOLS_BIN_DIR):$(PATH) @@ -85,7 +81,7 @@ all: build ## -------------------------------------- .PHONY: binaries -binaries: $(CONTROLLER_GEN) $(GOLANGCI_LINT) $(STATIC_CHECK) $(GINKGO_V1) $(GINKGO_V2) $(MOCKGEN) $(KUSTOMIZE) managers # Builds and installs all binaries +binaries: $(CONTROLLER_GEN) $(CONVERSION_GEN) $(GOLANGCI_LINT) $(STATIC_CHECK) $(GINKGO) $(MOCKGEN) $(KUSTOMIZE) managers # Builds and installs all binaries .PHONY: managers managers: @@ -95,7 +91,7 @@ managers: manager-cloudstack-infrastructure: ## Build manager binary. CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} go build -ldflags "${LDFLAGS} -extldflags '-static'" -o $(BIN_DIR)/manager . -export K8S_VERSION=1.25.0 +export K8S_VERSION=1.28.3 $(KUBECTL) $(API_SERVER) $(ETCD) &: cd $(TOOLS_DIR) && curl --silent -L "https://go.kubebuilder.io/test-tools/$%7BK8S_VERSION%7D/$(shell%20go%20env%20GOOS)/$(shell%20go%20env%20GOARCH)" --output - | \ tar -C ./ --strip-components=1 -zvxf - @@ -133,8 +129,8 @@ lint: $(GOLANGCI_LINT) $(STATIC_CHECK) generate-mocks ## Run linting for the pro .PHONY: modules modules: ## Runs go mod to ensure proper vendoring. - go mod tidy -compat=1.19 - cd $(TOOLS_DIR); go mod tidy -compat=1.19 + go mod tidy -compat=1.23 + cd $(TOOLS_DIR); go mod tidy -compat=1.23 .PHONY: generate-all generate-all: generate-mocks generate-deepcopy generate-manifests @@ -149,7 +145,7 @@ DEEPCOPY_GEN_INPUTS=$(shell find ./api -name "*test*" -prune -o -name "*zz_gener .PHONY: generate-deepcopy generate-deepcopy: $(DEEPCOPY_GEN_TARGETS) ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. api/%/zz_generated.deepcopy.go: $(CONTROLLER_GEN) $(DEEPCOPY_GEN_INPUTS) - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + CGO_ENABLED=0 $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." MANIFEST_GEN_INPUTS=$(shell find ./api ./controllers -type f -name "*test*" -prune -o -name "*zz_generated*" -prune -o -print) # Using a flag file here as config output is too complicated to be a target. @@ -159,16 +155,19 @@ $(shell grep -qs "$(IMG)" config/default/manager_image_patch_edited.yaml || rm - generate-manifests: config/.flag.mk ## Generates crd, webhook, rbac, and other configuration manifests from kubebuilder instructions in go comments. config/.flag.mk: $(CONTROLLER_GEN) $(MANIFEST_GEN_INPUTS) sed -e 's@image: .*@image: '"$(IMG)"'@' config/default/manager_image_patch.yaml > config/default/manager_image_patch_edited.yaml - $(CONTROLLER_GEN) crd:crdVersions=v1 rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) crd:crdVersions=v1 rbac:roleName=manager-role webhook paths="{./api/...,./controllers/...}" output:crd:artifacts:config=config/crd/bases @touch config/.flag.mk -CONVERSION_GEN_TARGET=$(shell find api -type d -name "v*1" -exec echo {}\/zz_generated.conversion.go \;) -CONVERSION_GEN_INPUTS=$(shell find ./api -name "*test*" -prune -o -name "*zz_generated*" -prune -o -type f -print) .PHONY: generate-conversion -generate-conversion: $(CONVERSION_GEN_TARGET) ## Generate code to convert api/v1beta1 to api/v1beta2 -api/%/zz_generated.conversion.go: bin/conversion-gen $(CONVERSION_GEN_INPUTS) - conversion-gen --go-header-file "./hack/boilerplate.go.txt" --input-dirs "./api/v1beta1" \ - --output-base "." --output-file-base="zz_generated.conversion" --skip-unsafe=true +generate-conversion: $(CONVERSION_GEN) ## Generate code to convert api/v1beta1 and api/v1beta2 to api/v1beta3 + $(CONVERSION_GEN) \ + --go-header-file=./hack/boilerplate.go.txt \ + --output-file=zz_generated.conversion.go \ + ./api/v1beta1 + $(CONVERSION_GEN) \ + --go-header-file=./hack/boilerplate.go.txt \ + --output-file=zz_generated.conversion.go \ + ./api/v1beta2 ##@ Build ## -------------------------------------- @@ -229,15 +228,23 @@ docker-push: .dockerflag.mk ## Push docker image with the manager. ## -------------------------------------- .PHONY: tilt-up -tilt-up: cluster-api kind-cluster cluster-api/tilt-settings.json generate-manifests ## Setup and run tilt for development. +tilt-up: cluster-api create-kind-cluster cluster-api/tilt-settings.json generate-manifests ## Setup and run tilt for development. cd cluster-api && tilt up -.PHONY: kind-cluster -kind-cluster: cluster-api ## Create a kind cluster with a local Docker repository. - ./cluster-api/hack/kind-install-for-capd.sh +KIND_CLUSTER_NAME := $(shell cat ./hack/tilt-settings.json | grep kind_cluster_name | cut -d: -f2 | xargs) + +.PHONY: create-kind-cluster +create-kind-cluster: cluster-api cluster-api/tilt-settings.json ## Create a kind cluster with a local Docker repository. + @if [ -z "$$(kind get clusters | grep $(KIND_CLUSTER_NAME))" ]; then \ + CAPI_KIND_CLUSTER_NAME=$(KIND_CLUSTER_NAME) ./cluster-api/hack/kind-install-for-capd.sh; \ + fi; + +.PHONY: delete-kind-cluster +delete-kind-cluster: + kind delete cluster --name $(KIND_CLUSTER_NAME) cluster-api: ## Clone cluster-api repository for tilt use. - git clone --branch v1.2.12 --depth 1 https://github.com/kubernetes-sigs/cluster-api.git + git clone --branch v1.9.6 --depth 1 https://github.com/kubernetes-sigs/cluster-api.git cluster-api/tilt-settings.json: hack/tilt-settings.json cluster-api cp ./hack/tilt-settings.json cluster-api @@ -264,10 +271,10 @@ config/.flag-test.mk: $(CONTROLLER_GEN) $(MANIFEST_GEN_INPUTS_TEST) .PHONY: test test: ## Run tests. -test: generate-deepcopy-test generate-manifest-test generate-mocks lint $(GINKGO_V2) $(KUBECTL) $(API_SERVER) $(ETCD) +test: generate-deepcopy-test generate-manifest-test generate-mocks lint $(GINKGO) $(KUBECTL) $(API_SERVER) $(ETCD) @./hack/testing_ginkgo_recover_statements.sh --add # Add ginkgo.GinkgoRecover() statements to controllers. @# The following is a slightly funky way to make sure the ginkgo statements are removed regardless the test results. - @$(GINKGO_V2) --label-filter="!integ" --cover -coverprofile cover.out --covermode=atomic -v ./api/... ./controllers/... ./pkg/...; EXIT_STATUS=$$?;\ + @$(GINKGO) --label-filter="!integ" --cover -coverprofile cover.out --covermode=atomic -v ./api/... ./controllers/... ./pkg/...; EXIT_STATUS=$$?;\ ./hack/testing_ginkgo_recover_statements.sh --remove; exit $$EXIT_STATUS CLUSTER_TEMPLATES_INPUT_FILES=$(shell find test/e2e/data/infrastructure-cloudstack/v1beta*/cluster-template* test/e2e/data/infrastructure-cloudstack/*/bases/* -type f) @@ -277,15 +284,16 @@ e2e-cluster-templates: $(CLUSTER_TEMPLATES_OUTPUT_FILES) ## Generate cluster tem cluster-template%yaml: $(KUSTOMIZE) $(CLUSTER_TEMPLATES_INPUT_FILES) $(KUSTOMIZE) build --load-restrictor LoadRestrictionsNone $(basename $@) > $@ -e2e-essentials: $(GINKGO_V1) $(KUBECTL) e2e-cluster-templates kind-cluster ## Fulfill essential tasks for e2e testing. +e2e-essentials: $(GINKGO) $(KUBECTL) e2e-cluster-templates create-kind-cluster ## Fulfill essential tasks for e2e testing. IMG=$(IMG_LOCAL) make generate-manifests docker-build docker-push JOB ?= .* E2E_CONFIG ?= ${REPO_ROOT}/test/e2e/config/cloudstack.yaml +E2E_TIMEOUT ?= 3h run-e2e: e2e-essentials ## Run e2e testing. JOB is an optional REGEXP to select certainn test cases to run. e.g. JOB=PR-Blocking, JOB=Conformance $(KUBECTL) apply -f cloud-config.yaml && \ cd test/e2e && \ - $(GINKGO_V1) -v -trace -tags=e2e -focus=$(JOB) -skip=Conformance -skipPackage=kubeconfig_helper -nodes=1 -noColor=false ./... -- \ + $(GINKGO) -v --trace --tags=e2e --focus=$(JOB) --timeout=$(E2E_TIMEOUT) --skip=Conformance --skip-package=kubeconfig_helper --nodes=1 --no-color=false ./... -- \ -e2e.artifacts-folder=${REPO_ROOT}/_artifacts \ -e2e.config=${E2E_CONFIG} \ -e2e.skip-resource-cleanup=false -e2e.use-existing-cluster=true @@ -310,6 +318,7 @@ clean: ## Cleans up everything. rm -rf $(TOOLS_BIN_DIR) rm -rf cluster-api rm -rf test/e2e/data/infrastructure-cloudstack/*/*yaml + rm -rf config/.flag.mk config/.flag-test.mk .dockerflag.mk ##@ Release ## -------------------------------------- @@ -331,20 +340,20 @@ release-manifests-metrics-port: make release-manifests RELEASE_MANIFEST_SOURCE_BASE=config/default-with-metrics-port .PHONY: release-staging -release-staging: ## Builds and push container images and manifests to the staging bucket. - $(MAKE) docker-build - $(MAKE) docker-push - $(MAKE) release-alias-tag +release-staging: ## Builds and uploads manifests to the staging bucket and creates new tag $(MAKE) release-templates $(MAKE) release-manifests TAG=$(RELEASE_ALIAS_TAG) $(MAKE) upload-staging-artifacts + git tag $(RELEASE_ALIAS_TAG) + git push upstream $(RELEASE_ALIAS_TAG) .PHONY: release-alias-tag release-alias-tag: # Adds the tag to the last build tag. gcloud container images add-tag -q $(CONTROLLER_IMG):$(TAG) $(CONTROLLER_IMG):$(RELEASE_ALIAS_TAG) .PHONY: release-templates -release-templates: $(RELEASE_DIR) ## Generate release templates +release-templates: ## Generate release templates + @mkdir -p $(RELEASE_DIR) cp templates/cluster-template*.yaml $(RELEASE_DIR)/ .PHONY: upload-staging-artifacts diff --git a/OWNERS b/OWNERS index 3321c864..472766d9 100644 --- a/OWNERS +++ b/OWNERS @@ -6,3 +6,4 @@ approvers: - g-gaston - chrisdoherty4 - weizhouapache + - vishesh92 diff --git a/PROJECT b/PROJECT index 5c9c5703..ea4068e3 100644 --- a/PROJECT +++ b/PROJECT @@ -2,6 +2,7 @@ domain: cluster.x-k8s.io projectName: cluster-api-provider-capc repo: sigs.k8s.io/cluster-api-provider-cloudstack resources: +# v1beta3 types - api: crdVersion: v1 namespaced: true @@ -9,8 +10,8 @@ resources: domain: cluster.x-k8s.io group: infrastructure kind: CloudStackCluster - path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 - version: v1beta1 + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 webhooks: defaulting: true validation: true @@ -22,8 +23,8 @@ resources: domain: cluster.x-k8s.io group: infrastructure kind: CloudStackMachine - path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 - version: v1beta1 + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 webhooks: defaulting: true validation: true @@ -35,8 +36,8 @@ resources: domain: cluster.x-k8s.io group: infrastructure kind: CloudStackMachineTemplate - path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 - version: v1beta1 + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 webhooks: defaulting: true validation: true @@ -48,6 +49,105 @@ resources: domain: cluster.x-k8s.io group: infrastructure kind: CloudStackIsolatedNetwork + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackAffinityGroup + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackMachineStateChecker + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackFailureDomain + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 + version: v1beta3 +# v1beta2 types +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackCluster + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackMachine + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackMachineTemplate + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackIsolatedNetwork + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackAffinityGroup + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackMachineStateChecker + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackFailureDomain + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 + version: v1beta2 +# v1beta1 types +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackCluster path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 version: v1beta1 - api: @@ -56,7 +156,7 @@ resources: controller: true domain: cluster.x-k8s.io group: infrastructure - kind: CloudStackZone + kind: CloudStackMachine path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 version: v1beta1 - api: @@ -65,7 +165,7 @@ resources: controller: true domain: cluster.x-k8s.io group: infrastructure - kind: CloudStackAffinityGroup + kind: CloudStackMachineTemplate path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 version: v1beta1 - api: @@ -74,15 +174,34 @@ resources: controller: true domain: cluster.x-k8s.io group: infrastructure - kind: CloudStackMachineStateChecker + kind: CloudStackIsolatedNetwork path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 version: v1beta1 - api: crdVersion: v1 namespaced: true + controller: true domain: cluster.x-k8s.io group: infrastructure - kind: CloudStackFailureDomain - path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 - version: v1beta2 + kind: CloudStackZone + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackAffinityGroup + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 + version: v1beta1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: cluster.x-k8s.io + group: infrastructure + kind: CloudStackMachineStateChecker + path: sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1 + version: v1beta1 version: "3" diff --git a/README.md b/README.md index 7a9349f3..7ec125c6 100644 --- a/README.md +++ b/README.md @@ -51,20 +51,24 @@ Check out the [Getting Started Guide][getting_started] to create your first Kube This provider's versions are able to install and manage the following versions of Kubernetes: -| Kubernetes Version | v1.22 | v1.23 | v1.24 | -| --------------------------- | ----- | ----- | ----- | -| CloudStack Provider (v0.4) | ✓ | ✓ | ✓ | +| Kubernetes Version | v1.22 | v1.23 | v1.24 | v1.25 | v1.26 | v1.27 | v1.28 | v1.29 | v1.30 | v1.31 | v1.32 | +| --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | +| CloudStack Provider (v0.4) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | | | | +| CloudStack Provider (v0.6) | | | | | | | ✓ | ✓ | ✓ | ✓ | ✓ | + +Note: The above matrix is based on what has been tested. Provider could work with older and newer versions of Kubernetes but it has not been tested. ## Compatibility with Apache CloudStack Versions This provider's versions are able to work on the following versions of Apache CloudStack: -| CloudStack Version | 4.14 | 4.15 | 4.16 | 4.17 | -| --------------------------- | ---- | ---- | ---- | ---- | -| CloudStack Provider (v0.4) | ✓ | ✓ | ✓ | ✓ | +| CloudStack Version | 4.14 | 4.15 | 4.16 | 4.17 | 4.18 | 4.19 | 4.20 | +| --------------------------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | +| CloudStack Provider (v0.4) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | +| CloudStack Provider (v0.6) | | | | | | ✓ | ✓ | ------- +Note: The above matrix is based on what has been tested. Provider could work with older and newer versions of Apache CloudStack but it has not been tested. ## Operating system images @@ -74,19 +78,50 @@ Reference images can be found in [kubernetes-sigs/image-builder][image-builder]. Prebuilt images can be found below : -| Hypervisor | Kubernetes Version | Rocky Linux 8 | Ubuntu 20.04 | +| Hypervisor | Kubernetes Version | Rocky Linux 9 | Ubuntu 22.04 | Ubuntu 24.04 | +| ---------- | ------------------ | ---------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | +| KVM | v1.28 | [qcow2][k1.28-rl9-qcow2], [md5][k1.28-rl9-qcow2-md5] | [qcow2][k1.28-u22-qcow2], [md5][k1.28-u22-qcow2-md5] | [qcow2][k1.28-u24-qcow2], [md5][k1.28-u24-qcow2-md5] | +| | v1.29 | [qcow2][k1.29-rl9-qcow2], [md5][k1.29-rl9-qcow2-md5] | [qcow2][k1.29-u22-qcow2], [md5][k1.29-u22-qcow2-md5] | [qcow2][k1.29-u24-qcow2], [md5][k1.29-u24-qcow2-md5] | +| | v1.30 | [qcow2][k1.30-rl9-qcow2], [md5][k1.30-rl9-qcow2-md5] | [qcow2][k1.30-u22-qcow2], [md5][k1.30-u22-qcow2-md5] | [qcow2][k1.30-u24-qcow2], [md5][k1.30-u24-qcow2-md5] | +| | v1.31 | [qcow2][k1.31-rl9-qcow2], [md5][k1.31-rl9-qcow2-md5] | [qcow2][k1.31-u22-qcow2], [md5][k1.31-u22-qcow2-md5] | [qcow2][k1.31-u24-qcow2], [md5][k1.31-u24-qcow2-md5] | +| | v1.32 | [qcow2][k1.32-rl9-qcow2], [md5][k1.32-rl9-qcow2-md5] | [qcow2][k1.32-u22-qcow2], [md5][k1.32-u22-qcow2-md5] | [qcow2][k1.32-u24-qcow2], [md5][k1.32-u24-qcow2-md5] | +| VMware | v1.28 | [ova][k1.28-rl9-ova], [md5][k1.28-rl9-ova-md5] | [ova][k1.28-u22-ova], [md5][k1.28-u22-ova-md5] | [ova][k1.28-u24-ova], [md5][k1.28-u24-ova-md5] | +| | v1.29 | [ova][k1.29-rl9-ova], [md5][k1.29-rl9-ova-md5] | [ova][k1.29-u22-ova], [md5][k1.29-u22-ova-md5] | [ova][k1.29-u24-ova], [md5][k1.29-u24-ova-md5] | +| | v1.30 | [ova][k1.30-rl9-ova], [md5][k1.30-rl9-ova-md5] | [ova][k1.30-u22-ova], [md5][k1.30-u22-ova-md5] | [ova][k1.30-u24-ova], [md5][k1.30-u24-ova-md5] | +| | v1.31 | [ova][k1.31-rl9-ova], [md5][k1.31-rl9-ova-md5] | [ova][k1.31-u22-ova], [md5][k1.31-u22-ova-md5] | [ova][k1.31-u24-ova], [md5][k1.31-u24-ova-md5] | +| | v1.32 | [ova][k1.32-rl9-ova], [md5][k1.32-rl9-ova-md5] | [ova][k1.32-u22-ova], [md5][k1.32-u22-ova-md5] | [ova][k1.32-u24-ova], [md5][k1.32-u24-ova-md5] | +| XenServer | v1.28 | [vhd][k1.28-rl9-vhd], [md5][k1.28-rl9-vhd-md5] | [vhd][k1.28-u22-vhd], [md5][k1.28-u22-vhd-md5] | [vhd][k1.28-u24-vhd], [md5][k1.28-u24-vhd-md5] | +| | v1.29 | [vhd][k1.29-rl9-vhd], [md5][k1.29-rl9-vhd-md5] | [vhd][k1.29-u22-vhd], [md5][k1.29-u22-vhd-md5] | [vhd][k1.29-u24-vhd], [md5][k1.29-u24-vhd-md5] | +| | v1.30 | [vhd][k1.30-rl9-vhd], [md5][k1.30-rl9-vhd-md5] | [vhd][k1.30-u22-vhd], [md5][k1.30-u22-vhd-md5] | [vhd][k1.30-u24-vhd], [md5][k1.30-u24-vhd-md5] | +| | v1.31 | [vhd][k1.31-rl9-vhd], [md5][k1.31-rl9-vhd-md5] | [vhd][k1.31-u22-vhd], [md5][k1.31-u22-vhd-md5] | [vhd][k1.31-u24-vhd], [md5][k1.31-u24-vhd-md5] | +| | v1.32 | [vhd][k1.32-rl9-vhd], [md5][k1.32-rl9-vhd-md5] | [vhd][k1.32-u22-vhd], [md5][k1.32-u22-vhd-md5] | [vhd][k1.32-u24-vhd], [md5][k1.32-u24-vhd-md5] | + +
+Past images + +| Hypervisor | Kubernetes Version | Rocky Linux 8 | Ubuntu 20.04 | | ---------- | ------------------ | ---------------------------------------------------- | ---------------------------------------------------- | | KVM | v1.22 | [qcow2][k1.22-rl8-qcow2], [md5][k1.22-rl8-qcow2-md5] | [qcow2][k1.22-u20-qcow2], [md5][k1.22-u20-qcow2-md5] | | | v1.23 | [qcow2][k1.23-rl8-qcow2], [md5][k1.23-rl8-qcow2-md5] | [qcow2][k1.23-u20-qcow2], [md5][k1.23-u20-qcow2-md5] | | | v1.24 | [qcow2][k1.24-rl8-qcow2], [md5][k1.24-rl8-qcow2-md5] | [qcow2][k1.24-u20-qcow2], [md5][k1.24-u20-qcow2-md5] | +| | v1.25 | [qcow2][k1.25-rl8-qcow2], [md5][k1.25-rl8-qcow2-md5] | [qcow2][k1.25-u20-qcow2], [md5][k1.25-u20-qcow2-md5] | +| | v1.26 | [qcow2][k1.26-rl8-qcow2], [md5][k1.26-rl8-qcow2-md5] | [qcow2][k1.26-u20-qcow2], [md5][k1.26-u20-qcow2-md5] | +| | v1.27 | [qcow2][k1.27-rl8-qcow2], [md5][k1.27-rl8-qcow2-md5] | [qcow2][k1.27-u20-qcow2], [md5][k1.27-u20-qcow2-md5] | | VMware | v1.22 | [ova][k1.22-rl8-ova], [md5][k1.22-rl8-ova-md5] | [ova][k1.22-u20-ova], [md5][k1.22-u20-ova-md5] | | | v1.23 | [ova][k1.23-rl8-ova], [md5][k1.23-rl8-ova-md5] | [ova][k1.23-u20-ova], [md5][k1.23-u20-ova-md5] | | | v1.24 | [ova][k1.24-rl8-ova], [md5][k1.24-rl8-ova-md5] | [ova][k1.24-u20-ova], [md5][k1.24-u20-ova-md5] | +| | v1.25 | [ova][k1.25-rl8-ova], [md5][k1.25-rl8-ova-md5] | [ova][k1.25-u20-ova], [md5][k1.25-u20-ova-md5] | +| | v1.26 | [ova][k1.26-rl8-ova], [md5][k1.26-rl8-ova-md5] | [ova][k1.26-u20-ova], [md5][k1.26-u20-ova-md5] | +| | v1.27 | [ova][k1.27-rl8-ova], [md5][k1.27-rl8-ova-md5] | [ova][k1.27-u20-ova], [md5][k1.27-u20-ova-md5] | | XenServer | v1.22 | [vhd][k1.22-rl8-vhd], [md5][k1.22-rl8-vhd-md5] | [vhd][k1.22-u20-vhd], [md5][k1.22-u20-vhd-md5] | | | v1.23 | [vhd][k1.23-rl8-vhd], [md5][k1.23-rl8-vhd-md5] | [vhd][k1.23-u20-vhd], [md5][k1.23-u20-vhd-md5] | | | v1.24 | [vhd][k1.24-rl8-vhd], [md5][k1.24-rl8-vhd-md5] | [vhd][k1.24-u20-vhd], [md5][k1.24-u20-vhd-md5] | - +| | v1.25 | [vhd][k1.25-rl8-vhd], [md5][k1.25-rl8-vhd-md5] | [vhd][k1.25-u20-vhd], [md5][k1.25-u20-vhd-md5] | +| | v1.26 | [vhd][k1.26-rl8-vhd], [md5][k1.26-rl8-vhd-md5] | [vhd][k1.26-u20-vhd], [md5][k1.26-u20-vhd-md5] | +| | v1.27 | [vhd][k1.27-rl8-vhd], [md5][k1.27-rl8-vhd-md5] | [vhd][k1.27-u20-vhd], [md5][k1.27-u20-vhd-md5] | ------ +
+ ## Getting involved and contributing Are you interested in contributing to cluster-api-provider-cloudstack? We, the @@ -131,16 +166,18 @@ We also use the issue tracker to track features. If you have an idea for a featu Thank you to all contributors and a special thanks to our current maintainers & reviewers: -| Maintainers | Reviewers | -| --------------------------------------------------------- | ------------------------------------------------------ | -| [@rohityadavcloud](https://github.com/rohityadavcloud) | [@rohityadavcloud](https://github.com/rohityadavcloud) | -| [@davidjumani](https://github.com/davidjumani) | [@davidjumani](https://github.com/davidjumani) | -| [@jweite-amazon](https://github.com/jweite-amazon) | [@jweite-amazon](https://github.com/jweite-amazon) | +| Maintainers | Reviewers | +| ------------------------------------------------------ | ------------------------------------------------------ | +| [@rohityadavcloud](https://github.com/rohityadavcloud) | [@rohityadavcloud](https://github.com/rohityadavcloud) | +| [@weizhouapache](https://github.com/weizhouapache) | [@weizhouapache](https://github.com/weizhouapache) | +| [@vishesh92](https://github.com/vishesh92) | [@vishesh92](https://github.com/vishesh92) | +| [@davidjumani](https://github.com/davidjumani) | [@davidjumani](https://github.com/davidjumani) | +| [@jweite-amazon](https://github.com/jweite-amazon) | [@jweite-amazon](https://github.com/jweite-amazon) | All the CAPC contributors:

- +

@@ -163,39 +200,165 @@ All the CAPC contributors: [k1.22-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.22.6-kvm.qcow2.bz2.md5 [k1.23-rl8-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.23.3-kvm.qcow2.bz2 [k1.23-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.23.3-kvm.qcow2.bz2.md5 -[k1.24-rl8-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.24.7-kvm.qcow2.bz2 -[k1.24-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.24.7-kvm.qcow2.bz2.md5 +[k1.24-rl8-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.24.14-kvm.qcow2.bz2 +[k1.24-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.24.14-kvm.qcow2.bz2.md5 +[k1.25-rl8-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.25.10-kvm.qcow2.bz2 +[k1.25-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.25.10-kvm.qcow2.bz2.md5 +[k1.26-rl8-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.26.5-kvm.qcow2.bz2 +[k1.26-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.26.5-kvm.qcow2.bz2.md5 +[k1.27-rl8-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.27.2-kvm.qcow2.bz2 +[k1.27-rl8-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-8-kube-v1.27.2-kvm.qcow2.bz2.md5 +[k1.28-rl9-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.28.15-kvm.qcow2.bz2 +[k1.28-rl9-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.28.15-kvm.qcow2.bz2.md5 +[k1.29-rl9-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.29.15-kvm.qcow2.bz2 +[k1.29-rl9-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.29.15-kvm.qcow2.bz2.md5 +[k1.30-rl9-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.30.11-kvm.qcow2.bz2 +[k1.30-rl9-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.30.11-kvm.qcow2.bz2.md5 +[k1.31-rl9-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.31.7-kvm.qcow2.bz2 +[k1.31-rl9-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.31.7-kvm.qcow2.bz2.md5 +[k1.32-rl9-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.32.3-kvm.qcow2.bz2 +[k1.32-rl9-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/rockylinux-9-kube-v1.32.3-kvm.qcow2.bz2.md5 [k1.22-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.22.6-kvm.qcow2.bz2 [k1.22-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.22.6-kvm.qcow2.bz2.md5 [k1.23-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.23.3-kvm.qcow2.bz2 [k1.23-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.23.3-kvm.qcow2.bz2.md5 -[k1.24-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.24.7-kvm.qcow2.bz2 -[k1.24-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.24.7-kvm.qcow2.bz2.md5 +[k1.24-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.24.14-kvm.qcow2.bz2 +[k1.24-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.24.14-kvm.qcow2.bz2.md5 +[k1.25-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.25.10-kvm.qcow2.bz2 +[k1.25-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.25.10-kvm.qcow2.bz2.md5 +[k1.26-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.26.5-kvm.qcow2.bz2 +[k1.26-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.26.5-kvm.qcow2.bz2.md5 +[k1.27-u20-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.27.2-kvm.qcow2.bz2 +[k1.27-u20-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2004-kube-v1.27.2-kvm.qcow2.bz2.md5 +[k1.28-u22-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.28.15-kvm.qcow2.bz2 +[k1.28-u22-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.28.15-kvm.qcow2.bz2.md5 +[k1.29-u22-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.29.15-kvm.qcow2.bz2 +[k1.29-u22-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.29.15-kvm.qcow2.bz2.md5 +[k1.30-u22-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.30.11-kvm.qcow2.bz2 +[k1.30-u22-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.30.11-kvm.qcow2.bz2.md5 +[k1.31-u22-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.31.7-kvm.qcow2.bz2 +[k1.31-u22-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.31.7-kvm.qcow2.bz2.md5 +[k1.32-u22-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.32.3-kvm.qcow2.bz2 +[k1.32-u22-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2204-kube-v1.32.3-kvm.qcow2.bz2.md5 +[k1.28-u24-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.28.15-kvm.qcow2.bz2 +[k1.28-u24-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.28.15-kvm.qcow2.bz2.md5 +[k1.29-u24-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.29.15-kvm.qcow2.bz2 +[k1.29-u24-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.29.15-kvm.qcow2.bz2.md5 +[k1.30-u24-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.30.11-kvm.qcow2.bz2 +[k1.30-u24-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.30.11-kvm.qcow2.bz2.md5 +[k1.31-u24-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.31.7-kvm.qcow2.bz2 +[k1.31-u24-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.31.7-kvm.qcow2.bz2.md5 +[k1.32-u24-qcow2]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.32.3-kvm.qcow2.bz2 +[k1.32-u24-qcow2-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/kvm/ubuntu-2404-kube-v1.32.3-kvm.qcow2.bz2.md5 [k1.22-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.22.6-vmware.ova [k1.22-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.22.6-vmware.ova.md5 [k1.23-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.23.3-vmware.ova [k1.23-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.23.3-vmware.ova.md5 -[k1.24-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.24.7-vmware.ova -[k1.24-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.24.7-vmware.ova.md5 +[k1.24-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.24.14-vmware.ova +[k1.24-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.24.14-vmware.ova.md5 +[k1.25-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.25.10-vmware.ova +[k1.25-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.25.10-vmware.ova.md5 +[k1.26-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.26.5-vmware.ova +[k1.26-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.26.5-vmware.ova.md5 +[k1.27-rl8-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.27.2-vmware.ova +[k1.27-rl8-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-8-kube-v1.27.2-vmware.ova.md5 +[k1.28-rl9-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.28.15-vmware.ova +[k1.28-rl9-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.28.15-vmware.ova.md5 +[k1.29-rl9-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.29.15-vmware.ova +[k1.29-rl9-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.29.15-vmware.ova.md5 +[k1.30-rl9-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.30.11-vmware.ova +[k1.30-rl9-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.30.11-vmware.ova.md5 +[k1.31-rl9-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.31.7-vmware.ova +[k1.31-rl9-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.31.7-vmware.ova.md5 +[k1.32-rl9-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.32.3-vmware.ova +[k1.32-rl9-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/rockylinux-9-kube-v1.32.3-vmware.ova.md5 [k1.22-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.22.6-vmware.ova [k1.22-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.22.6-vmware.ova.md5 [k1.23-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.23.3-vmware.ova [k1.23-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.23.3-vmware.ova.md5 -[k1.24-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.24.7-vmware.ova -[k1.24-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.24.7-vmware.ova.md5 +[k1.24-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.24.14-vmware.ova +[k1.24-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.24.14-vmware.ova.md5 +[k1.25-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.25.10-vmware.ova +[k1.25-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.25.10-vmware.ova.md5 +[k1.26-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.26.5-vmware.ova +[k1.26-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.26.5-vmware.ova.md5 +[k1.27-u20-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.27.2-vmware.ova +[k1.27-u20-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2004-kube-v1.27.2-vmware.ova.md5 +[k1.28-u22-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.28.15-vmware.ova +[k1.28-u22-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.28.15-vmware.ova.md5 +[k1.29-u22-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.29.15-vmware.ova +[k1.29-u22-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.29.15-vmware.ova.md5 +[k1.30-u22-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.30.11-vmware.ova +[k1.30-u22-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.30.11-vmware.ova.md5 +[k1.31-u22-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.31.7-vmware.ova +[k1.31-u22-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.31.7-vmware.ova.md5 +[k1.32-u22-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.32.3-vmware.ova +[k1.32-u22-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2204-kube-v1.32.3-vmware.ova.md5 +[k1.28-u24-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.28.15-vmware.ova +[k1.28-u24-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.28.15-vmware.ova.md5 +[k1.29-u24-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.29.15-vmware.ova +[k1.29-u24-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.29.15-vmware.ova.md5 +[k1.30-u24-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.30.11-vmware.ova +[k1.30-u24-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.30.11-vmware.ova.md5 +[k1.31-u24-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.31.7-vmware.ova +[k1.31-u24-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.31.7-vmware.ova.md5 +[k1.32-u24-ova]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.32.3-vmware.ova +[k1.32-u24-ova-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/vmware/ubuntu-2404-kube-v1.32.3-vmware.ova.md5 [k1.22-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.22.6-xen.vhd.bz2 [k1.22-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.22.6-xen.vhd.bz2.md5 [k1.23-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.23.3-xen.vhd.bz2 [k1.23-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.23.3-xen.vhd.bz2.md5 -[k1.24-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.24.7-xen.vhd.bz2 -[k1.24-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.24.7-xen.vhd.bz2.md5 +[k1.24-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.24.14-xen.vhd.bz2 +[k1.24-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.24.14-xen.vhd.bz2.md5 +[k1.25-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.25.10-xen.vhd.bz2 +[k1.25-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.25.10-xen.vhd.bz2.md5 +[k1.26-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.26.5-xen.vhd.bz2 +[k1.26-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.26.5-xen.vhd.bz2.md5 +[k1.27-rl8-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.27.2-xen.vhd.bz2 +[k1.27-rl8-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-8-kube-v1.27.2-xen.vhd.bz2.md5 +[k1.28-rl9-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.28.15-xen.vhd.bz2 +[k1.28-rl9-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.28.15-xen.vhd.bz2.md5 +[k1.29-rl9-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.29.15-xen.vhd.bz2 +[k1.29-rl9-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.29.15-xen.vhd.bz2.md5 +[k1.30-rl9-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.30.11-xen.vhd.bz2 +[k1.30-rl9-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.30.11-xen.vhd.bz2.md5 +[k1.31-rl9-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.31.7-xen.vhd.bz2 +[k1.31-rl9-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.31.7-xen.vhd.bz2.md5 +[k1.32-rl9-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.32.3-xen.vhd.bz2 +[k1.32-rl9-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/rockylinux-9-kube-v1.32.3-xen.vhd.bz2.md5 [k1.22-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.22.6-xen.vhd.bz2 [k1.22-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.22.6-xen.vhd.bz2.md5 [k1.23-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.23.3-xen.vhd.bz2 [k1.23-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.23.3-xen.vhd.bz2.md5 -[k1.24-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.24.7-xen.vhd.bz2 -[k1.24-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.24.7-xen.vhd.bz2.md5 +[k1.24-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.24.14-xen.vhd.bz2 +[k1.24-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.24.14-xen.vhd.bz2.md5 +[k1.25-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.25.10-xen.vhd.bz2 +[k1.25-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.25.10-xen.vhd.bz2.md5 +[k1.26-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.26.5-xen.vhd.bz2 +[k1.26-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.26.5-xen.vhd.bz2.md5 +[k1.27-u20-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.27.2-xen.vhd.bz2 +[k1.27-u20-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2004-kube-v1.27.2-xen.vhd.bz2.md5 +[k1.28-u22-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.28.15-xen.vhd.bz2 +[k1.28-u22-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.28.15-xen.vhd.bz2.md5 +[k1.29-u22-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.29.15-xen.vhd.bz2 +[k1.29-u22-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.29.15-xen.vhd.bz2.md5 +[k1.30-u22-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.30.11-xen.vhd.bz2 +[k1.30-u22-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.30.11-xen.vhd.bz2.md5 +[k1.31-u22-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.31.7-xen.vhd.bz2 +[k1.31-u22-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.31.7-xen.vhd.bz2.md5 +[k1.32-u22-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.32.3-xen.vhd.bz2 +[k1.32-u22-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2204-kube-v1.32.3-xen.vhd.bz2.md5 +[k1.28-u24-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.28.15-xen.vhd.bz2 +[k1.28-u24-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.28.15-xen.vhd.bz2.md5 +[k1.29-u24-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.29.15-xen.vhd.bz2 +[k1.29-u24-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.29.15-xen.vhd.bz2.md5 +[k1.30-u24-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.30.11-xen.vhd.bz2 +[k1.30-u24-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.30.11-xen.vhd.bz2.md5 +[k1.31-u24-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.31.7-xen.vhd.bz2 +[k1.31-u24-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.31.7-xen.vhd.bz2.md5 +[k1.32-u24-vhd]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.32.3-xen.vhd.bz2 +[k1.32-u24-vhd-md5]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/xen/ubuntu-2404-kube-v1.32.3-xen.vhd.bz2.md5 diff --git a/api/v1beta1/cloudstackaffinitygroup_conversion.go b/api/v1beta1/cloudstackaffinitygroup_conversion.go index 44fb55e6..cee3df48 100644 --- a/api/v1beta1/cloudstackaffinitygroup_conversion.go +++ b/api/v1beta1/cloudstackaffinitygroup_conversion.go @@ -17,16 +17,40 @@ limitations under the License. package v1beta1 import ( - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *CloudStackAffinityGroup) ConvertTo(dstRaw conversion.Hub) error { // nolint - dst := dstRaw.(*v1beta2.CloudStackAffinityGroup) - return Convert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(src, dst, nil) + dst := dstRaw.(*v1beta3.CloudStackAffinityGroup) + if err := Convert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(src, dst, nil); err != nil { + return err + } + + // Manually restore data + restored := &v1beta3.CloudStackAffinityGroup{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + if restored.Spec.FailureDomainName != "" { + dst.Spec.FailureDomainName = restored.Spec.FailureDomainName + } + return nil } func (dst *CloudStackAffinityGroup) ConvertFrom(srcRaw conversion.Hub) error { // nolint - src := srcRaw.(*v1beta2.CloudStackAffinityGroup) - return Convert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(src, dst, nil) + src := srcRaw.(*v1beta3.CloudStackAffinityGroup) + if err := Convert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion + err := utilconversion.MarshalData(src, dst) + return err +} + +func Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(in *v1beta3.CloudStackAffinityGroupSpec, out *CloudStackAffinityGroupSpec, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(in, out, s) } diff --git a/api/v1beta1/cloudstackaffinitygroup_types.go b/api/v1beta1/cloudstackaffinitygroup_types.go index 0fdf06fa..b83530f2 100644 --- a/api/v1beta1/cloudstackaffinitygroup_types.go +++ b/api/v1beta1/cloudstackaffinitygroup_types.go @@ -66,5 +66,5 @@ type CloudStackAffinityGroupList struct { } func init() { - SchemeBuilder.Register(&CloudStackAffinityGroup{}, &CloudStackAffinityGroupList{}) + objectTypes = append(objectTypes, &CloudStackAffinityGroup{}, &CloudStackAffinityGroupList{}) } diff --git a/api/v1beta1/cloudstackcluster_conversion.go b/api/v1beta1/cloudstackcluster_conversion.go index d5773936..2e383000 100644 --- a/api/v1beta1/cloudstackcluster_conversion.go +++ b/api/v1beta1/cloudstackcluster_conversion.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Kubernetes Authors. +Copyright 2023 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,18 +17,16 @@ limitations under the License. package v1beta1 import ( - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *CloudStackCluster) ConvertTo(dstRaw conversion.Hub) error { // nolint - dst := dstRaw.(*v1beta2.CloudStackCluster) - - return Convert_v1beta1_CloudStackCluster_To_v1beta2_CloudStackCluster(src, dst, nil) + dst := dstRaw.(*v1beta3.CloudStackCluster) + return Convert_v1beta1_CloudStackCluster_To_v1beta3_CloudStackCluster(src, dst, nil) } func (dst *CloudStackCluster) ConvertFrom(srcRaw conversion.Hub) error { // nolint - src := srcRaw.(*v1beta2.CloudStackCluster) - - return Convert_v1beta2_CloudStackCluster_To_v1beta1_CloudStackCluster(src, dst, nil) + src := srcRaw.(*v1beta3.CloudStackCluster) + return Convert_v1beta3_CloudStackCluster_To_v1beta1_CloudStackCluster(src, dst, nil) } diff --git a/api/v1beta1/cloudstackcluster_types.go b/api/v1beta1/cloudstackcluster_types.go index 9fe31fc9..db23ffe3 100644 --- a/api/v1beta1/cloudstackcluster_types.go +++ b/api/v1beta1/cloudstackcluster_types.go @@ -154,5 +154,5 @@ type CloudStackClusterList struct { } func init() { - SchemeBuilder.Register(&CloudStackCluster{}, &CloudStackClusterList{}) + objectTypes = append(objectTypes, &CloudStackCluster{}, &CloudStackClusterList{}) } diff --git a/api/v1beta1/cloudstackisolatednetwork_conversion.go b/api/v1beta1/cloudstackisolatednetwork_conversion.go index 36430afd..8694a127 100644 --- a/api/v1beta1/cloudstackisolatednetwork_conversion.go +++ b/api/v1beta1/cloudstackisolatednetwork_conversion.go @@ -17,16 +17,40 @@ limitations under the License. package v1beta1 import ( - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *CloudStackIsolatedNetwork) ConvertTo(dstRaw conversion.Hub) error { // nolint - dst := dstRaw.(*v1beta2.CloudStackIsolatedNetwork) - return Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(src, dst, nil) + dst := dstRaw.(*v1beta3.CloudStackIsolatedNetwork) + if err := Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(src, dst, nil); err != nil { + return err + } + + // Manually restore data + restored := &v1beta3.CloudStackIsolatedNetwork{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + if restored.Spec.FailureDomainName != "" { + dst.Spec.FailureDomainName = restored.Spec.FailureDomainName + } + return nil } func (dst *CloudStackIsolatedNetwork) ConvertFrom(srcRaw conversion.Hub) error { // nolint - src := srcRaw.(*v1beta2.CloudStackIsolatedNetwork) - return Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(src, dst, nil) + src := srcRaw.(*v1beta3.CloudStackIsolatedNetwork) + if err := Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion + err := utilconversion.MarshalData(src, dst) + return err +} + +func Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(in *v1beta3.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(in, out, s) } diff --git a/api/v1beta1/cloudstackisolatednetwork_types.go b/api/v1beta1/cloudstackisolatednetwork_types.go index 8033322a..83698fed 100644 --- a/api/v1beta1/cloudstackisolatednetwork_types.go +++ b/api/v1beta1/cloudstackisolatednetwork_types.go @@ -81,5 +81,5 @@ type CloudStackIsolatedNetworkList struct { } func init() { - SchemeBuilder.Register(&CloudStackIsolatedNetwork{}, &CloudStackIsolatedNetworkList{}) + objectTypes = append(objectTypes, &CloudStackIsolatedNetwork{}, &CloudStackIsolatedNetworkList{}) } diff --git a/api/v1beta1/cloudstackmachine_conversion.go b/api/v1beta1/cloudstackmachine_conversion.go index bdf7235d..d59ab1b9 100644 --- a/api/v1beta1/cloudstackmachine_conversion.go +++ b/api/v1beta1/cloudstackmachine_conversion.go @@ -17,16 +17,53 @@ limitations under the License. package v1beta1 import ( - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *CloudStackMachine) ConvertTo(dstRaw conversion.Hub) error { // nolint - dst := dstRaw.(*v1beta2.CloudStackMachine) - return Convert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine(src, dst, nil) + dst := dstRaw.(*v1beta3.CloudStackMachine) + if err := Convert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine(src, dst, nil); err != nil { + return err + } + + // Manually restore data + restored := &v1beta3.CloudStackMachine{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + if restored.Spec.FailureDomainName != "" { + dst.Spec.FailureDomainName = restored.Spec.FailureDomainName + } + if restored.Spec.UncompressedUserData != nil { + dst.Spec.UncompressedUserData = restored.Spec.UncompressedUserData + } + if restored.Status.Status != nil { + dst.Status.Status = restored.Status.Status + } + if restored.Status.Reason != nil { + dst.Status.Reason = restored.Status.Reason + } + return nil } func (dst *CloudStackMachine) ConvertFrom(srcRaw conversion.Hub) error { // nolint - src := srcRaw.(*v1beta2.CloudStackMachine) - return Convert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine(src, dst, nil) + src := srcRaw.(*v1beta3.CloudStackMachine) + if err := Convert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion + err := utilconversion.MarshalData(src, dst) + return err +} + +func Convert_v1beta3_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(in *v1beta3.CloudStackMachineSpec, out *CloudStackMachineSpec, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(in, out, s) +} + +func Convert_v1beta3_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(in *v1beta3.CloudStackMachineStatus, out *CloudStackMachineStatus, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(in, out, s) } diff --git a/api/v1beta1/cloudstackmachine_types.go b/api/v1beta1/cloudstackmachine_types.go index b86ab530..1329c9f0 100644 --- a/api/v1beta1/cloudstackmachine_types.go +++ b/api/v1beta1/cloudstackmachine_types.go @@ -180,5 +180,5 @@ type CloudStackMachineList struct { } func init() { - SchemeBuilder.Register(&CloudStackMachine{}, &CloudStackMachineList{}) + objectTypes = append(objectTypes, &CloudStackMachine{}, &CloudStackMachineList{}) } diff --git a/api/v1beta1/cloudstackmachine_types_test.go b/api/v1beta1/cloudstackmachine_types_test.go index 4344db25..9cedf970 100644 --- a/api/v1beta1/cloudstackmachine_types_test.go +++ b/api/v1beta1/cloudstackmachine_types_test.go @@ -17,30 +17,31 @@ limitations under the License. package v1beta1_test import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1" - "time" ) -var _ = Describe("CloudStackMachine types", func() { +var _ = ginkgo.Describe("CloudStackMachine types", func() { var cloudStackMachine infrav1.CloudStackMachine - BeforeEach(func() { // Reset test vars to initial state. + ginkgo.BeforeEach(func() { // Reset test vars to initial state. cloudStackMachine = infrav1.CloudStackMachine{} }) - Context("When calculating time since state change", func() { - It("Return the correct value when the last state update time is known", func() { + ginkgo.Context("When calculating time since state change", func() { + ginkgo.It("Return the correct value when the last state update time is known", func() { delta := time.Duration(10 * time.Minute) lastUpdated := time.Now().Add(-delta) cloudStackMachine.Status.InstanceStateLastUpdated = metav1.NewTime(lastUpdated) - Ω(cloudStackMachine.Status.TimeSinceLastStateChange()).Should(BeNumerically("~", delta, time.Second)) + gomega.Expect(cloudStackMachine.Status.TimeSinceLastStateChange()).Should(gomega.BeNumerically("~", delta, time.Second)) }) - It("Return a negative value when the last state update time is unknown", func() { - Ω(cloudStackMachine.Status.TimeSinceLastStateChange()).Should(BeNumerically("<", 0)) + ginkgo.It("Return a negative value when the last state update time is unknown", func() { + gomega.Expect(cloudStackMachine.Status.TimeSinceLastStateChange()).Should(gomega.BeNumerically("<", 0)) }) }) }) diff --git a/api/v1beta1/cloudstackmachinestatechecker_conversion.go b/api/v1beta1/cloudstackmachinestatechecker_conversion.go index 3e3c9396..fd075c17 100644 --- a/api/v1beta1/cloudstackmachinestatechecker_conversion.go +++ b/api/v1beta1/cloudstackmachinestatechecker_conversion.go @@ -17,16 +17,16 @@ limitations under the License. package v1beta1 import ( - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/controller-runtime/pkg/conversion" ) func (src *CloudStackMachineStateChecker) ConvertTo(dstRaw conversion.Hub) error { // nolint - dst := dstRaw.(*v1beta2.CloudStackMachineStateChecker) - return Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(src, dst, nil) + dst := dstRaw.(*v1beta3.CloudStackMachineStateChecker) + return Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(src, dst, nil) } func (dst *CloudStackMachineStateChecker) ConvertFrom(srcRaw conversion.Hub) error { // nolint - src := srcRaw.(*v1beta2.CloudStackMachineStateChecker) - return Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(src, dst, nil) + src := srcRaw.(*v1beta3.CloudStackMachineStateChecker) + return Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(src, dst, nil) } diff --git a/api/v1beta1/cloudstackmachinestatechecker_types.go b/api/v1beta1/cloudstackmachinestatechecker_types.go index 61a6a9b4..492a6d8e 100644 --- a/api/v1beta1/cloudstackmachinestatechecker_types.go +++ b/api/v1beta1/cloudstackmachinestatechecker_types.go @@ -54,5 +54,5 @@ type CloudStackMachineStateCheckerList struct { } func init() { - SchemeBuilder.Register(&CloudStackMachineStateChecker{}, &CloudStackMachineStateCheckerList{}) + objectTypes = append(objectTypes, &CloudStackMachineStateChecker{}, &CloudStackMachineStateCheckerList{}) } diff --git a/api/v1beta1/cloudstackmachinetemplate_conversion.go b/api/v1beta1/cloudstackmachinetemplate_conversion.go index 065a3d6a..992b6747 100644 --- a/api/v1beta1/cloudstackmachinetemplate_conversion.go +++ b/api/v1beta1/cloudstackmachinetemplate_conversion.go @@ -17,16 +17,70 @@ limitations under the License. package v1beta1 import ( - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func (src *CloudStackMachineTemplate) ConvertTo(dstRaw conversion.Hub) error { // nolint - dst := dstRaw.(*v1beta2.CloudStackMachineTemplate) - return Convert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(src, dst, nil) + dst := dstRaw.(*v1beta3.CloudStackMachineTemplate) + if err := Convert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(src, dst, nil); err != nil { + return err + } + + // Manually restore data + restored := &v1beta3.CloudStackMachineTemplate{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + if restored.Spec.Template.Spec.FailureDomainName != "" { + dst.Spec.Template.Spec.FailureDomainName = restored.Spec.Template.Spec.FailureDomainName + } + if restored.Spec.Template.Spec.UncompressedUserData != nil { + dst.Spec.Template.Spec.UncompressedUserData = restored.Spec.Template.Spec.UncompressedUserData + } + return nil } func (dst *CloudStackMachineTemplate) ConvertFrom(srcRaw conversion.Hub) error { // nolint - src := srcRaw.(*v1beta2.CloudStackMachineTemplate) - return Convert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(src, dst, nil) + src := srcRaw.(*v1beta3.CloudStackMachineTemplate) + if err := Convert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion + err := utilconversion.MarshalData(src, dst) + return err +} + +func Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(in *CloudStackMachineTemplateSpec, out *v1beta3.CloudStackMachineTemplateSpec, s machineryconversion.Scope) error { // nolint + return Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(&in.Spec, &out.Template, s) +} + +func Convert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(in *v1beta3.CloudStackMachineTemplateSpec, out *CloudStackMachineTemplateSpec, s machineryconversion.Scope) error { // nolint + return Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(&in.Template, &out.Spec, s) +} + +func Convert_v1beta1_ObjectMeta_To_v1_ObjectMeta(in *clusterv1.ObjectMeta, out *metav1.ObjectMeta, s machineryconversion.Scope) error { // nolint + if in.Annotations != nil { + out.Annotations = in.Annotations + } + if in.Labels != nil { + out.Labels = in.Labels + } + return nil +} + +func Convert_v1_ObjectMeta_To_v1beta1_ObjectMeta(in *metav1.ObjectMeta, out *clusterv1.ObjectMeta, s machineryconversion.Scope) error { // nolint + if in.Annotations != nil { + out.Annotations = in.Annotations + } + if in.Labels != nil { + out.Labels = in.Labels + } + return nil } diff --git a/api/v1beta1/cloudstackmachinetemplate_types.go b/api/v1beta1/cloudstackmachinetemplate_types.go index d8b332df..66f6a1d6 100644 --- a/api/v1beta1/cloudstackmachinetemplate_types.go +++ b/api/v1beta1/cloudstackmachinetemplate_types.go @@ -54,5 +54,5 @@ type CloudStackMachineTemplateList struct { } func init() { - SchemeBuilder.Register(&CloudStackMachineTemplate{}, &CloudStackMachineTemplateList{}) + objectTypes = append(objectTypes, &CloudStackMachineTemplate{}, &CloudStackMachineTemplateList{}) } diff --git a/api/v1beta1/cloudstackzone_types.go b/api/v1beta1/cloudstackzone_types.go index 4f192fde..b6d52546 100644 --- a/api/v1beta1/cloudstackzone_types.go +++ b/api/v1beta1/cloudstackzone_types.go @@ -66,5 +66,5 @@ type CloudStackZoneList struct { } func init() { - SchemeBuilder.Register(&CloudStackZone{}, &CloudStackZoneList{}) + objectTypes = append(objectTypes, &CloudStackZone{}, &CloudStackZoneList{}) } diff --git a/api/v1beta1/conversion.go b/api/v1beta1/conversion.go index a04e8fab..e4ef38f6 100644 --- a/api/v1beta1/conversion.go +++ b/api/v1beta1/conversion.go @@ -22,7 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" conv "k8s.io/apimachinery/pkg/conversion" - "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -30,18 +30,18 @@ import ( const DefaultEndpointCredential = "global" //nolint:golint,revive,stylecheck -func Convert_v1beta1_CloudStackCluster_To_v1beta2_CloudStackCluster(in *CloudStackCluster, out *v1beta2.CloudStackCluster, s conv.Scope) error { +func Convert_v1beta1_CloudStackCluster_To_v1beta3_CloudStackCluster(in *CloudStackCluster, out *v1beta3.CloudStackCluster, s conv.Scope) error { out.ObjectMeta = in.ObjectMeta failureDomains, err := GetFailureDomains(in) if err != nil { return err } - out.Spec = v1beta2.CloudStackClusterSpec{ + out.Spec = v1beta3.CloudStackClusterSpec{ ControlPlaneEndpoint: in.Spec.ControlPlaneEndpoint, FailureDomains: failureDomains, } - out.Status = v1beta2.CloudStackClusterStatus{ + out.Status = v1beta3.CloudStackClusterStatus{ FailureDomains: in.Status.FailureDomains, Ready: in.Status.Ready, } @@ -49,9 +49,9 @@ func Convert_v1beta1_CloudStackCluster_To_v1beta2_CloudStackCluster(in *CloudSta } //nolint:golint,revive,stylecheck -func Convert_v1beta2_CloudStackCluster_To_v1beta1_CloudStackCluster(in *v1beta2.CloudStackCluster, out *CloudStackCluster, scope conv.Scope) error { +func Convert_v1beta3_CloudStackCluster_To_v1beta1_CloudStackCluster(in *v1beta3.CloudStackCluster, out *CloudStackCluster, scope conv.Scope) error { if len(in.Spec.FailureDomains) < 1 { - return fmt.Errorf("v1beta2 to v1beta1 conversion not supported when < 1 failure domain is provided. Input CloudStackCluster spec %s", in.Spec) + return fmt.Errorf("v1beta3 to v1beta1 conversion not supported when < 1 failure domain is provided. Input CloudStackCluster spec %v", in.Spec) } out.ObjectMeta = in.ObjectMeta out.Spec = CloudStackClusterSpec{ @@ -69,7 +69,7 @@ func Convert_v1beta2_CloudStackCluster_To_v1beta1_CloudStackCluster(in *v1beta2. } // getZones maps failure domains to zones -func getZones(csCluster *v1beta2.CloudStackCluster) []Zone { +func getZones(csCluster *v1beta3.CloudStackCluster) []Zone { var zones []Zone for _, failureDomain := range csCluster.Spec.FailureDomains { zone := failureDomain.Zone @@ -86,21 +86,21 @@ func getZones(csCluster *v1beta2.CloudStackCluster) []Zone { return zones } -// GetFailureDomains maps v1beta1 zones to v1beta2 failure domains. -func GetFailureDomains(csCluster *CloudStackCluster) ([]v1beta2.CloudStackFailureDomainSpec, error) { - var failureDomains []v1beta2.CloudStackFailureDomainSpec +// GetFailureDomains maps v1beta1 zones to v1beta3 failure domains. +func GetFailureDomains(csCluster *CloudStackCluster) ([]v1beta3.CloudStackFailureDomainSpec, error) { + var failureDomains []v1beta3.CloudStackFailureDomainSpec namespace := csCluster.Namespace for _, zone := range csCluster.Spec.Zones { name, err := GetDefaultFailureDomainName(namespace, csCluster.Name, zone.ID, zone.Name) if err != nil { return nil, err } - failureDomains = append(failureDomains, v1beta2.CloudStackFailureDomainSpec{ + failureDomains = append(failureDomains, v1beta3.CloudStackFailureDomainSpec{ Name: name, - Zone: v1beta2.CloudStackZoneSpec{ + Zone: v1beta3.CloudStackZoneSpec{ ID: zone.ID, Name: zone.Name, - Network: v1beta2.Network{ + Network: v1beta3.Network{ ID: zone.Network.ID, Name: zone.Network.Name, Type: zone.Network.Type, @@ -123,7 +123,7 @@ func GetFailureDomains(csCluster *CloudStackCluster) ([]v1beta2.CloudStackFailur // When upgrading cluster using eks-a, a secret named global will be created by eks-a, and it is used by following // method to get zoneID by calling cloudstack API. // When upgrading cluster using clusterctl directly, zoneID is fetched directly from kubernetes cluster in cloudstackzones. -func GetDefaultFailureDomainName(namespace string, clusterName string, zoneID string, zoneName string) (string, error) { +func GetDefaultFailureDomainName(namespace string, _ string, zoneID string, zoneName string) (string, error) { if len(zoneID) > 0 { return zoneID, nil } @@ -149,7 +149,7 @@ func GetDefaultFailureDomainName(namespace string, clusterName string, zoneID st func fetchZoneIDUsingK8s(namespace string, zoneName string) (string, error) { zone := &CloudStackZone{} key := client.ObjectKey{Name: zoneName, Namespace: namespace} - if err := v1beta2.K8sClient.Get(context.TODO(), key, zone); err != nil { + if err := v1beta3.K8sClient.Get(context.TODO(), key, zone); err != nil { return "", err } @@ -157,11 +157,11 @@ func fetchZoneIDUsingK8s(namespace string, zoneName string) (string, error) { } func fetchZoneIDUsingCloudStack(secret *corev1.Secret, zoneName string) (string, error) { - client, err := cloud.NewClientFromK8sSecret(secret, nil) + client, err := cloud.NewClientFromK8sSecret(secret, nil, "") if err != nil { return "", err } - zone := &v1beta2.CloudStackZoneSpec{Name: zoneName} + zone := &v1beta3.CloudStackZoneSpec{Name: zoneName} err = client.ResolveZone(zone) return zone.ID, err } @@ -169,8 +169,30 @@ func fetchZoneIDUsingCloudStack(secret *corev1.Secret, zoneName string) (string, func GetK8sSecret(name, namespace string) (*corev1.Secret, error) { endpointCredentials := &corev1.Secret{} key := client.ObjectKey{Name: name, Namespace: namespace} - if err := v1beta2.K8sClient.Get(context.TODO(), key, endpointCredentials); err != nil { + if err := v1beta3.K8sClient.Get(context.TODO(), key, endpointCredentials); err != nil { return nil, err } return endpointCredentials, nil } + +// Convert_v1beta3_Network_To_v1beta1_Network converts from v1beta3.Network to v1beta1.Network +// +//nolint:golint,revive,stylecheck +func Convert_v1beta3_Network_To_v1beta1_Network(in *v1beta3.Network, out *Network, _ conv.Scope) error { + out.ID = in.ID + out.Type = in.Type + out.Name = in.Name + // Skip Gateway, Netmask, and VPC fields as they do not exist in v1beta1.Network + return nil +} + +// Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus handles manual conversion of CloudStackIsolatedNetworkStatus from v1beta3 to v1beta1 +// +//nolint:golint,revive,stylecheck +func Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(in *v1beta3.CloudStackIsolatedNetworkStatus, out *CloudStackIsolatedNetworkStatus, _ conv.Scope) error { + out.PublicIPID = in.PublicIPID + out.LBRuleID = in.LBRuleID + out.Ready = in.Ready + // RoutingMode field doesn't exist in v1beta1, so we ignore it during conversion + return nil +} diff --git a/api/v1beta1/conversion_test.go b/api/v1beta1/conversion_test.go index 8c83b7ef..3e1c2169 100644 --- a/api/v1beta1/conversion_test.go +++ b/api/v1beta1/conversion_test.go @@ -17,21 +17,21 @@ limitations under the License. package v1beta1_test import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1beta1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1" - v1beta2 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - capiv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) -var _ = Describe("Conversion", func() { - BeforeEach(func() { // Reset test vars to initial state. +var _ = ginkgo.Describe("Conversion", func() { + ginkgo.BeforeEach(func() { // Reset test vars to initial state. }) - Context("GetFailureDomains function", func() { - It("Converts v1beta1 cluster spec to v1beta2 failure domains", func() { + ginkgo.Context("GetFailureDomains function", func() { + ginkgo.It("Converts v1beta1 cluster spec to v1beta3 failure domains", func() { csCluster := &v1beta1.CloudStackCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster1", @@ -46,7 +46,7 @@ var _ = Describe("Conversion", func() { }, }, }, - ControlPlaneEndpoint: capiv1.APIEndpoint{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: "endpoint1", Port: 443, }, @@ -56,12 +56,12 @@ var _ = Describe("Conversion", func() { Status: v1beta1.CloudStackClusterStatus{}, } failureDomains, err := v1beta1.GetFailureDomains(csCluster) - expectedResult := []v1beta2.CloudStackFailureDomainSpec{ + expectedResult := []v1beta3.CloudStackFailureDomainSpec{ { Name: "76472a84-d23f-4e97-b154-ee1b975ed936", - Zone: v1beta2.CloudStackZoneSpec{ + Zone: v1beta3.CloudStackZoneSpec{ ID: "76472a84-d23f-4e97-b154-ee1b975ed936", - Network: v1beta2.Network{Name: "network1"}, + Network: v1beta3.Network{Name: "network1"}, }, Account: "account1", Domain: "domain1", @@ -71,25 +71,25 @@ var _ = Describe("Conversion", func() { }, }, } - Ω(err).ShouldNot(HaveOccurred()) - Ω(failureDomains).Should(Equal(expectedResult)) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(failureDomains).Should(gomega.Equal(expectedResult)) }) }) - Context("v1beta2 to v1beta1 function", func() { - It("Converts v1beta2 cluster spec to v1beta1 zone based cluster spec", func() { - csCluster := &v1beta2.CloudStackCluster{ + ginkgo.Context("v1beta3 to v1beta1 function", func() { + ginkgo.It("Converts v1beta3 cluster spec to v1beta1 zone based cluster spec", func() { + csCluster := &v1beta3.CloudStackCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster1", Namespace: "namespace1", }, - Spec: v1beta2.CloudStackClusterSpec{ - FailureDomains: []v1beta2.CloudStackFailureDomainSpec{ + Spec: v1beta3.CloudStackClusterSpec{ + FailureDomains: []v1beta3.CloudStackFailureDomainSpec{ { Name: "76472a84-d23f-4e97-b154-ee1b975ed936", - Zone: v1beta2.CloudStackZoneSpec{ + Zone: v1beta3.CloudStackZoneSpec{ ID: "76472a84-d23f-4e97-b154-ee1b975ed936", - Network: v1beta2.Network{Name: "network1"}, + Network: v1beta3.Network{Name: "network1"}, }, Account: "account1", Domain: "domain1", @@ -99,15 +99,15 @@ var _ = Describe("Conversion", func() { }, }, }, - ControlPlaneEndpoint: capiv1.APIEndpoint{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: "endpoint1", Port: 443, }, }, - Status: v1beta2.CloudStackClusterStatus{}, + Status: v1beta3.CloudStackClusterStatus{}, } converted := &v1beta1.CloudStackCluster{} - err := v1beta1.Convert_v1beta2_CloudStackCluster_To_v1beta1_CloudStackCluster(csCluster, converted, nil) + err := v1beta1.Convert_v1beta3_CloudStackCluster_To_v1beta1_CloudStackCluster(csCluster, converted, nil) expectedResult := &v1beta1.CloudStackCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster1", @@ -122,7 +122,7 @@ var _ = Describe("Conversion", func() { }, }, }, - ControlPlaneEndpoint: capiv1.APIEndpoint{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: "endpoint1", Port: 443, }, @@ -132,26 +132,26 @@ var _ = Describe("Conversion", func() { Status: v1beta1.CloudStackClusterStatus{}, } - Ω(err).ShouldNot(HaveOccurred()) - Ω(converted).Should(Equal(expectedResult)) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(converted).Should(gomega.Equal(expectedResult)) }) - It("Returns error when len(failureDomains) < 1", func() { - csCluster := &v1beta2.CloudStackCluster{ + ginkgo.It("Returns error when len(failureDomains) < 1", func() { + csCluster := &v1beta3.CloudStackCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster1", Namespace: "namespace1", }, - Spec: v1beta2.CloudStackClusterSpec{ - ControlPlaneEndpoint: capiv1.APIEndpoint{ + Spec: v1beta3.CloudStackClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{ Host: "endpoint1", Port: 443, }, }, - Status: v1beta2.CloudStackClusterStatus{}, + Status: v1beta3.CloudStackClusterStatus{}, } - err := v1beta1.Convert_v1beta2_CloudStackCluster_To_v1beta1_CloudStackCluster(csCluster, nil, nil) - Ω(err).Should(HaveOccurred()) + err := v1beta1.Convert_v1beta3_CloudStackCluster_To_v1beta1_CloudStackCluster(csCluster, nil, nil) + gomega.Expect(err).Should(gomega.HaveOccurred()) }) }) }) diff --git a/api/v1beta1/doc.go b/api/v1beta1/doc.go index 262e4e2e..2de7e2a8 100644 --- a/api/v1beta1/doc.go +++ b/api/v1beta1/doc.go @@ -17,5 +17,5 @@ limitations under the License. // Package v1beta1 contains API Schema definitions for the infrastructure v1beta1 API group // +kubebuilder:object:generate=true // +groupName=infrastructure.cluster.x-k8s.io -// +k8s:conversion-gen=sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2 +// +k8s:conversion-gen=sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 package v1beta1 diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta1/groupversion_info.go index 3729b567..936d8997 100644 --- a/api/v1beta1/groupversion_info.go +++ b/api/v1beta1/groupversion_info.go @@ -20,8 +20,9 @@ limitations under the License. package v1beta1 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" ) var ( @@ -29,11 +30,19 @@ var ( GroupVersion = schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme + AddToScheme = schemeBuilder.AddToScheme // So the auto-generated code can compile - localSchemeBuilder = &SchemeBuilder.SchemeBuilder + objectTypes = []runtime.Object{} + + localSchemeBuilder = &schemeBuilder ) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, objectTypes...) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/api/v1beta1/webhook_suite_test.go b/api/v1beta1/webhook_suite_test.go deleted file mode 100644 index dce4a281..00000000 --- a/api/v1beta1/webhook_suite_test.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2022 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1beta1_test - -import ( - "context" - "path/filepath" - "testing" - "time" - - "k8s.io/client-go/rest" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - admissionv1beta1 "k8s.io/api/admission/v1beta1" - //+kubebuilder:scaffold:imports - "k8s.io/apimachinery/pkg/runtime" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var k8sClient client.Client -var testEnv *envtest.Environment -var ctx context.Context -var cancel context.CancelFunc - -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Webhook Suite") -} - -var _ = BeforeSuite(func() { - ctx, cancel = context.WithCancel(context.TODO()) - - 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")}, - }, - } - - var cfg *rest.Config - var err error - done := make(chan interface{}) - go func() { - defer GinkgoRecover() - cfg, err = testEnv.Start() - close(done) - }() - Eventually(done).WithTimeout(time.Minute).Should(BeClosed()) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - scheme := runtime.NewScheme() - err = infrav1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = admissionv1beta1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{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, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - LeaderElection: false, - MetricsBindAddress: "0", - }) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:webhook - - go func() { - defer GinkgoRecover() - err = mgr.Start(ctrl.SetupSignalHandler()) - Expect(err).NotTo(HaveOccurred()) - }() -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index 4b8835e5..6fd363e2 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -21,9 +21,14 @@ limitations under the License. package v1beta1 import ( + unsafe "unsafe" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" - v1beta2 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + v1beta3 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" ) func init() { @@ -33,298 +38,308 @@ func init() { // RegisterConversions adds conversion functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterConversions(s *runtime.Scheme) error { - if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroup)(nil), (*v1beta2.CloudStackAffinityGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(a.(*CloudStackAffinityGroup), b.(*v1beta2.CloudStackAffinityGroup), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroup)(nil), (*v1beta3.CloudStackAffinityGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(a.(*CloudStackAffinityGroup), b.(*v1beta3.CloudStackAffinityGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroup)(nil), (*CloudStackAffinityGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(a.(*v1beta3.CloudStackAffinityGroup), b.(*CloudStackAffinityGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupList)(nil), (*v1beta3.CloudStackAffinityGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(a.(*CloudStackAffinityGroupList), b.(*v1beta3.CloudStackAffinityGroupList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackAffinityGroup)(nil), (*CloudStackAffinityGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(a.(*v1beta2.CloudStackAffinityGroup), b.(*CloudStackAffinityGroup), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroupList)(nil), (*CloudStackAffinityGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(a.(*v1beta3.CloudStackAffinityGroupList), b.(*CloudStackAffinityGroupList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupList)(nil), (*v1beta2.CloudStackAffinityGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(a.(*CloudStackAffinityGroupList), b.(*v1beta2.CloudStackAffinityGroupList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupSpec)(nil), (*v1beta3.CloudStackAffinityGroupSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(a.(*CloudStackAffinityGroupSpec), b.(*v1beta3.CloudStackAffinityGroupSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackAffinityGroupList)(nil), (*CloudStackAffinityGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(a.(*v1beta2.CloudStackAffinityGroupList), b.(*CloudStackAffinityGroupList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupStatus)(nil), (*v1beta3.CloudStackAffinityGroupStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(a.(*CloudStackAffinityGroupStatus), b.(*v1beta3.CloudStackAffinityGroupStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupSpec)(nil), (*v1beta2.CloudStackAffinityGroupSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(a.(*CloudStackAffinityGroupSpec), b.(*v1beta2.CloudStackAffinityGroupSpec), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroupStatus)(nil), (*CloudStackAffinityGroupStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(a.(*v1beta3.CloudStackAffinityGroupStatus), b.(*CloudStackAffinityGroupStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackAffinityGroupSpec)(nil), (*CloudStackAffinityGroupSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(a.(*v1beta2.CloudStackAffinityGroupSpec), b.(*CloudStackAffinityGroupSpec), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetwork)(nil), (*v1beta3.CloudStackIsolatedNetwork)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(a.(*CloudStackIsolatedNetwork), b.(*v1beta3.CloudStackIsolatedNetwork), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupStatus)(nil), (*v1beta2.CloudStackAffinityGroupStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(a.(*CloudStackAffinityGroupStatus), b.(*v1beta2.CloudStackAffinityGroupStatus), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackIsolatedNetwork)(nil), (*CloudStackIsolatedNetwork)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(a.(*v1beta3.CloudStackIsolatedNetwork), b.(*CloudStackIsolatedNetwork), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackAffinityGroupStatus)(nil), (*CloudStackAffinityGroupStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(a.(*v1beta2.CloudStackAffinityGroupStatus), b.(*CloudStackAffinityGroupStatus), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkList)(nil), (*v1beta3.CloudStackIsolatedNetworkList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(a.(*CloudStackIsolatedNetworkList), b.(*v1beta3.CloudStackIsolatedNetworkList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetwork)(nil), (*v1beta2.CloudStackIsolatedNetwork)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(a.(*CloudStackIsolatedNetwork), b.(*v1beta2.CloudStackIsolatedNetwork), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackIsolatedNetworkList)(nil), (*CloudStackIsolatedNetworkList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(a.(*v1beta3.CloudStackIsolatedNetworkList), b.(*CloudStackIsolatedNetworkList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackIsolatedNetwork)(nil), (*CloudStackIsolatedNetwork)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(a.(*v1beta2.CloudStackIsolatedNetwork), b.(*CloudStackIsolatedNetwork), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkSpec)(nil), (*v1beta3.CloudStackIsolatedNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(a.(*CloudStackIsolatedNetworkSpec), b.(*v1beta3.CloudStackIsolatedNetworkSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkList)(nil), (*v1beta2.CloudStackIsolatedNetworkList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(a.(*CloudStackIsolatedNetworkList), b.(*v1beta2.CloudStackIsolatedNetworkList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkStatus)(nil), (*v1beta3.CloudStackIsolatedNetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(a.(*CloudStackIsolatedNetworkStatus), b.(*v1beta3.CloudStackIsolatedNetworkStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackIsolatedNetworkList)(nil), (*CloudStackIsolatedNetworkList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(a.(*v1beta2.CloudStackIsolatedNetworkList), b.(*CloudStackIsolatedNetworkList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachine)(nil), (*v1beta3.CloudStackMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine(a.(*CloudStackMachine), b.(*v1beta3.CloudStackMachine), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkSpec)(nil), (*v1beta2.CloudStackIsolatedNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(a.(*CloudStackIsolatedNetworkSpec), b.(*v1beta2.CloudStackIsolatedNetworkSpec), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachine)(nil), (*CloudStackMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine(a.(*v1beta3.CloudStackMachine), b.(*CloudStackMachine), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackIsolatedNetworkSpec)(nil), (*CloudStackIsolatedNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(a.(*v1beta2.CloudStackIsolatedNetworkSpec), b.(*CloudStackIsolatedNetworkSpec), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineList)(nil), (*v1beta3.CloudStackMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineList_To_v1beta3_CloudStackMachineList(a.(*CloudStackMachineList), b.(*v1beta3.CloudStackMachineList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkStatus)(nil), (*v1beta2.CloudStackIsolatedNetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(a.(*CloudStackIsolatedNetworkStatus), b.(*v1beta2.CloudStackIsolatedNetworkStatus), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineList)(nil), (*CloudStackMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineList_To_v1beta1_CloudStackMachineList(a.(*v1beta3.CloudStackMachineList), b.(*CloudStackMachineList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackIsolatedNetworkStatus)(nil), (*CloudStackIsolatedNetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(a.(*v1beta2.CloudStackIsolatedNetworkStatus), b.(*CloudStackIsolatedNetworkStatus), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineSpec)(nil), (*v1beta3.CloudStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(a.(*CloudStackMachineSpec), b.(*v1beta3.CloudStackMachineSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachine)(nil), (*v1beta2.CloudStackMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine(a.(*CloudStackMachine), b.(*v1beta2.CloudStackMachine), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateChecker)(nil), (*v1beta3.CloudStackMachineStateChecker)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(a.(*CloudStackMachineStateChecker), b.(*v1beta3.CloudStackMachineStateChecker), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachine)(nil), (*CloudStackMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine(a.(*v1beta2.CloudStackMachine), b.(*CloudStackMachine), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateChecker)(nil), (*CloudStackMachineStateChecker)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(a.(*v1beta3.CloudStackMachineStateChecker), b.(*CloudStackMachineStateChecker), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineList)(nil), (*v1beta2.CloudStackMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineList_To_v1beta2_CloudStackMachineList(a.(*CloudStackMachineList), b.(*v1beta2.CloudStackMachineList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerList)(nil), (*v1beta3.CloudStackMachineStateCheckerList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(a.(*CloudStackMachineStateCheckerList), b.(*v1beta3.CloudStackMachineStateCheckerList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineList)(nil), (*CloudStackMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineList_To_v1beta1_CloudStackMachineList(a.(*v1beta2.CloudStackMachineList), b.(*CloudStackMachineList), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateCheckerList)(nil), (*CloudStackMachineStateCheckerList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(a.(*v1beta3.CloudStackMachineStateCheckerList), b.(*CloudStackMachineStateCheckerList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineSpec)(nil), (*v1beta2.CloudStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(a.(*CloudStackMachineSpec), b.(*v1beta2.CloudStackMachineSpec), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerSpec)(nil), (*v1beta3.CloudStackMachineStateCheckerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(a.(*CloudStackMachineStateCheckerSpec), b.(*v1beta3.CloudStackMachineStateCheckerSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineSpec)(nil), (*CloudStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(a.(*v1beta2.CloudStackMachineSpec), b.(*CloudStackMachineSpec), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateCheckerSpec)(nil), (*CloudStackMachineStateCheckerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(a.(*v1beta3.CloudStackMachineStateCheckerSpec), b.(*CloudStackMachineStateCheckerSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateChecker)(nil), (*v1beta2.CloudStackMachineStateChecker)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(a.(*CloudStackMachineStateChecker), b.(*v1beta2.CloudStackMachineStateChecker), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerStatus)(nil), (*v1beta3.CloudStackMachineStateCheckerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(a.(*CloudStackMachineStateCheckerStatus), b.(*v1beta3.CloudStackMachineStateCheckerStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineStateChecker)(nil), (*CloudStackMachineStateChecker)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(a.(*v1beta2.CloudStackMachineStateChecker), b.(*CloudStackMachineStateChecker), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateCheckerStatus)(nil), (*CloudStackMachineStateCheckerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(a.(*v1beta3.CloudStackMachineStateCheckerStatus), b.(*CloudStackMachineStateCheckerStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerList)(nil), (*v1beta2.CloudStackMachineStateCheckerList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(a.(*CloudStackMachineStateCheckerList), b.(*v1beta2.CloudStackMachineStateCheckerList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStatus)(nil), (*v1beta3.CloudStackMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(a.(*CloudStackMachineStatus), b.(*v1beta3.CloudStackMachineStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineStateCheckerList)(nil), (*CloudStackMachineStateCheckerList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(a.(*v1beta2.CloudStackMachineStateCheckerList), b.(*CloudStackMachineStateCheckerList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplate)(nil), (*v1beta3.CloudStackMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(a.(*CloudStackMachineTemplate), b.(*v1beta3.CloudStackMachineTemplate), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerSpec)(nil), (*v1beta2.CloudStackMachineStateCheckerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(a.(*CloudStackMachineStateCheckerSpec), b.(*v1beta2.CloudStackMachineStateCheckerSpec), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineTemplate)(nil), (*CloudStackMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(a.(*v1beta3.CloudStackMachineTemplate), b.(*CloudStackMachineTemplate), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineStateCheckerSpec)(nil), (*CloudStackMachineStateCheckerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(a.(*v1beta2.CloudStackMachineStateCheckerSpec), b.(*CloudStackMachineStateCheckerSpec), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateList)(nil), (*v1beta3.CloudStackMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(a.(*CloudStackMachineTemplateList), b.(*v1beta3.CloudStackMachineTemplateList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerStatus)(nil), (*v1beta2.CloudStackMachineStateCheckerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(a.(*CloudStackMachineStateCheckerStatus), b.(*v1beta2.CloudStackMachineStateCheckerStatus), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineTemplateList)(nil), (*CloudStackMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(a.(*v1beta3.CloudStackMachineTemplateList), b.(*CloudStackMachineTemplateList), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineStateCheckerStatus)(nil), (*CloudStackMachineStateCheckerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(a.(*v1beta2.CloudStackMachineStateCheckerStatus), b.(*CloudStackMachineStateCheckerStatus), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateResource)(nil), (*v1beta3.CloudStackMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(a.(*CloudStackMachineTemplateResource), b.(*v1beta3.CloudStackMachineTemplateResource), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineStatus)(nil), (*v1beta2.CloudStackMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(a.(*CloudStackMachineStatus), b.(*v1beta2.CloudStackMachineStatus), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineTemplateResource)(nil), (*CloudStackMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(a.(*v1beta3.CloudStackMachineTemplateResource), b.(*CloudStackMachineTemplateResource), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineStatus)(nil), (*CloudStackMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(a.(*v1beta2.CloudStackMachineStatus), b.(*CloudStackMachineStatus), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackResourceDiskOffering)(nil), (*v1beta3.CloudStackResourceDiskOffering)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(a.(*CloudStackResourceDiskOffering), b.(*v1beta3.CloudStackResourceDiskOffering), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplate)(nil), (*v1beta2.CloudStackMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(a.(*CloudStackMachineTemplate), b.(*v1beta2.CloudStackMachineTemplate), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackResourceDiskOffering)(nil), (*CloudStackResourceDiskOffering)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(a.(*v1beta3.CloudStackResourceDiskOffering), b.(*CloudStackResourceDiskOffering), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineTemplate)(nil), (*CloudStackMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(a.(*v1beta2.CloudStackMachineTemplate), b.(*CloudStackMachineTemplate), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackResourceIdentifier)(nil), (*v1beta3.CloudStackResourceIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(a.(*CloudStackResourceIdentifier), b.(*v1beta3.CloudStackResourceIdentifier), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateList)(nil), (*v1beta2.CloudStackMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(a.(*CloudStackMachineTemplateList), b.(*v1beta2.CloudStackMachineTemplateList), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackResourceIdentifier)(nil), (*CloudStackResourceIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(a.(*v1beta3.CloudStackResourceIdentifier), b.(*CloudStackResourceIdentifier), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineTemplateList)(nil), (*CloudStackMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(a.(*v1beta2.CloudStackMachineTemplateList), b.(*CloudStackMachineTemplateList), scope) + if err := s.AddGeneratedConversionFunc((*CloudStackZoneSpec)(nil), (*v1beta3.CloudStackZoneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(a.(*CloudStackZoneSpec), b.(*v1beta3.CloudStackZoneSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateResource)(nil), (*v1beta2.CloudStackMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(a.(*CloudStackMachineTemplateResource), b.(*v1beta2.CloudStackMachineTemplateResource), scope) + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackZoneSpec)(nil), (*CloudStackZoneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(a.(*v1beta3.CloudStackZoneSpec), b.(*CloudStackZoneSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineTemplateResource)(nil), (*CloudStackMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(a.(*v1beta2.CloudStackMachineTemplateResource), b.(*CloudStackMachineTemplateResource), scope) + if err := s.AddGeneratedConversionFunc((*Network)(nil), (*v1beta3.Network)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_Network_To_v1beta3_Network(a.(*Network), b.(*v1beta3.Network), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateSpec)(nil), (*v1beta2.CloudStackMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(a.(*CloudStackMachineTemplateSpec), b.(*v1beta2.CloudStackMachineTemplateSpec), scope) + if err := s.AddConversionFunc((*v1.ObjectMeta)(nil), (*apiv1beta1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_ObjectMeta_To_v1beta1_ObjectMeta(a.(*v1.ObjectMeta), b.(*apiv1beta1.ObjectMeta), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackMachineTemplateSpec)(nil), (*CloudStackMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(a.(*v1beta2.CloudStackMachineTemplateSpec), b.(*CloudStackMachineTemplateSpec), scope) + if err := s.AddConversionFunc((*CloudStackCluster)(nil), (*v1beta3.CloudStackCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackCluster_To_v1beta3_CloudStackCluster(a.(*CloudStackCluster), b.(*v1beta3.CloudStackCluster), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackResourceDiskOffering)(nil), (*v1beta2.CloudStackResourceDiskOffering)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(a.(*CloudStackResourceDiskOffering), b.(*v1beta2.CloudStackResourceDiskOffering), scope) + if err := s.AddConversionFunc((*CloudStackMachineTemplateSpec)(nil), (*v1beta3.CloudStackMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(a.(*CloudStackMachineTemplateSpec), b.(*v1beta3.CloudStackMachineTemplateSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackResourceDiskOffering)(nil), (*CloudStackResourceDiskOffering)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(a.(*v1beta2.CloudStackResourceDiskOffering), b.(*CloudStackResourceDiskOffering), scope) + if err := s.AddConversionFunc((*apiv1beta1.ObjectMeta)(nil), (*v1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ObjectMeta_To_v1_ObjectMeta(a.(*apiv1beta1.ObjectMeta), b.(*v1.ObjectMeta), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackResourceIdentifier)(nil), (*v1beta2.CloudStackResourceIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(a.(*CloudStackResourceIdentifier), b.(*v1beta2.CloudStackResourceIdentifier), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackAffinityGroupSpec)(nil), (*CloudStackAffinityGroupSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(a.(*v1beta3.CloudStackAffinityGroupSpec), b.(*CloudStackAffinityGroupSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackResourceIdentifier)(nil), (*CloudStackResourceIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(a.(*v1beta2.CloudStackResourceIdentifier), b.(*CloudStackResourceIdentifier), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackCluster)(nil), (*CloudStackCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackCluster_To_v1beta1_CloudStackCluster(a.(*v1beta3.CloudStackCluster), b.(*CloudStackCluster), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*CloudStackZoneSpec)(nil), (*v1beta2.CloudStackZoneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(a.(*CloudStackZoneSpec), b.(*v1beta2.CloudStackZoneSpec), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackIsolatedNetworkSpec)(nil), (*CloudStackIsolatedNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(a.(*v1beta3.CloudStackIsolatedNetworkSpec), b.(*CloudStackIsolatedNetworkSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.CloudStackZoneSpec)(nil), (*CloudStackZoneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(a.(*v1beta2.CloudStackZoneSpec), b.(*CloudStackZoneSpec), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackIsolatedNetworkStatus)(nil), (*CloudStackIsolatedNetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(a.(*v1beta3.CloudStackIsolatedNetworkStatus), b.(*CloudStackIsolatedNetworkStatus), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*Network)(nil), (*v1beta2.Network)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_Network_To_v1beta2_Network(a.(*Network), b.(*v1beta2.Network), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackMachineSpec)(nil), (*CloudStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(a.(*v1beta3.CloudStackMachineSpec), b.(*CloudStackMachineSpec), scope) }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.Network)(nil), (*Network)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_Network_To_v1beta1_Network(a.(*v1beta2.Network), b.(*Network), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackMachineStatus)(nil), (*CloudStackMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(a.(*v1beta3.CloudStackMachineStatus), b.(*CloudStackMachineStatus), scope) }); err != nil { return err } - if err := s.AddConversionFunc((*CloudStackCluster)(nil), (*v1beta2.CloudStackCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_CloudStackCluster_To_v1beta2_CloudStackCluster(a.(*CloudStackCluster), b.(*v1beta2.CloudStackCluster), scope) + if err := s.AddConversionFunc((*v1beta3.CloudStackMachineTemplateSpec)(nil), (*CloudStackMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(a.(*v1beta3.CloudStackMachineTemplateSpec), b.(*CloudStackMachineTemplateSpec), scope) }); err != nil { return err } - if err := s.AddConversionFunc((*v1beta2.CloudStackCluster)(nil), (*CloudStackCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_CloudStackCluster_To_v1beta1_CloudStackCluster(a.(*v1beta2.CloudStackCluster), b.(*CloudStackCluster), scope) + if err := s.AddConversionFunc((*v1beta3.Network)(nil), (*Network)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_Network_To_v1beta1_Network(a.(*v1beta3.Network), b.(*Network), scope) }); err != nil { return err } return nil } -func autoConvert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(in *CloudStackAffinityGroup, out *v1beta2.CloudStackAffinityGroup, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(in *CloudStackAffinityGroup, out *v1beta3.CloudStackAffinityGroup, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup is an autogenerated conversion function. -func Convert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(in *CloudStackAffinityGroup, out *v1beta2.CloudStackAffinityGroup, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(in, out, s) +// Convert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup is an autogenerated conversion function. +func Convert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(in *CloudStackAffinityGroup, out *v1beta3.CloudStackAffinityGroup, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(in, out, s) } -func autoConvert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(in *v1beta2.CloudStackAffinityGroup, out *CloudStackAffinityGroup, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(in *v1beta3.CloudStackAffinityGroup, out *CloudStackAffinityGroup, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup is an autogenerated conversion function. -func Convert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(in *v1beta2.CloudStackAffinityGroup, out *CloudStackAffinityGroup, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(in, out, s) +// Convert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(in *v1beta3.CloudStackAffinityGroup, out *CloudStackAffinityGroup, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(in, out, s) } -func autoConvert_v1beta1_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(in *CloudStackAffinityGroupList, out *v1beta2.CloudStackAffinityGroupList, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(in *CloudStackAffinityGroupList, out *v1beta3.CloudStackAffinityGroupList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]v1beta2.CloudStackAffinityGroup, len(*in)) + *out = make([]v1beta3.CloudStackAffinityGroup, len(*in)) for i := range *in { - if err := Convert_v1beta1_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta1_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -334,18 +349,18 @@ func autoConvert_v1beta1_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffini return nil } -// Convert_v1beta1_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList is an autogenerated conversion function. -func Convert_v1beta1_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(in *CloudStackAffinityGroupList, out *v1beta2.CloudStackAffinityGroupList, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(in, out, s) +// Convert_v1beta1_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList is an autogenerated conversion function. +func Convert_v1beta1_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(in *CloudStackAffinityGroupList, out *v1beta3.CloudStackAffinityGroupList, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(in, out, s) } -func autoConvert_v1beta2_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(in *v1beta2.CloudStackAffinityGroupList, out *CloudStackAffinityGroupList, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(in *v1beta3.CloudStackAffinityGroupList, out *CloudStackAffinityGroupList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]CloudStackAffinityGroup, len(*in)) for i := range *in { - if err := Convert_v1beta2_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta3_CloudStackAffinityGroup_To_v1beta1_CloudStackAffinityGroup(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -355,95 +370,90 @@ func autoConvert_v1beta2_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffini return nil } -// Convert_v1beta2_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList is an autogenerated conversion function. -func Convert_v1beta2_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(in *v1beta2.CloudStackAffinityGroupList, out *CloudStackAffinityGroupList, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(in, out, s) +// Convert_v1beta3_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(in *v1beta3.CloudStackAffinityGroupList, out *CloudStackAffinityGroupList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroupList_To_v1beta1_CloudStackAffinityGroupList(in, out, s) } -func autoConvert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(in *CloudStackAffinityGroupSpec, out *v1beta2.CloudStackAffinityGroupSpec, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(in *CloudStackAffinityGroupSpec, out *v1beta3.CloudStackAffinityGroupSpec, s conversion.Scope) error { out.Type = in.Type out.Name = in.Name out.ID = in.ID return nil } -// Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec is an autogenerated conversion function. -func Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(in *CloudStackAffinityGroupSpec, out *v1beta2.CloudStackAffinityGroupSpec, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(in, out, s) +// Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec is an autogenerated conversion function. +func Convert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(in *CloudStackAffinityGroupSpec, out *v1beta3.CloudStackAffinityGroupSpec, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(in, out, s) } -func autoConvert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(in *v1beta2.CloudStackAffinityGroupSpec, out *CloudStackAffinityGroupSpec, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(in *v1beta3.CloudStackAffinityGroupSpec, out *CloudStackAffinityGroupSpec, s conversion.Scope) error { out.Type = in.Type out.Name = in.Name out.ID = in.ID - // INFO: in.FailureDomainName opted out of conversion generation + // WARNING: in.FailureDomainName requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec is an autogenerated conversion function. -func Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(in *v1beta2.CloudStackAffinityGroupSpec, out *CloudStackAffinityGroupSpec, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta1_CloudStackAffinityGroupSpec(in, out, s) -} - -func autoConvert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(in *CloudStackAffinityGroupStatus, out *v1beta2.CloudStackAffinityGroupStatus, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(in *CloudStackAffinityGroupStatus, out *v1beta3.CloudStackAffinityGroupStatus, s conversion.Scope) error { out.Ready = in.Ready return nil } -// Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus is an autogenerated conversion function. -func Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(in *CloudStackAffinityGroupStatus, out *v1beta2.CloudStackAffinityGroupStatus, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(in, out, s) +// Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus is an autogenerated conversion function. +func Convert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(in *CloudStackAffinityGroupStatus, out *v1beta3.CloudStackAffinityGroupStatus, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(in, out, s) } -func autoConvert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(in *v1beta2.CloudStackAffinityGroupStatus, out *CloudStackAffinityGroupStatus, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(in *v1beta3.CloudStackAffinityGroupStatus, out *CloudStackAffinityGroupStatus, s conversion.Scope) error { out.Ready = in.Ready return nil } -// Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus is an autogenerated conversion function. -func Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(in *v1beta2.CloudStackAffinityGroupStatus, out *CloudStackAffinityGroupStatus, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(in, out, s) +// Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(in *v1beta3.CloudStackAffinityGroupStatus, out *CloudStackAffinityGroupStatus, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta1_CloudStackAffinityGroupStatus(in, out, s) } -func autoConvert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(in *CloudStackIsolatedNetwork, out *v1beta2.CloudStackIsolatedNetwork, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(in *CloudStackIsolatedNetwork, out *v1beta3.CloudStackIsolatedNetwork, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork is an autogenerated conversion function. -func Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(in *CloudStackIsolatedNetwork, out *v1beta2.CloudStackIsolatedNetwork, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(in, out, s) +// Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork is an autogenerated conversion function. +func Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(in *CloudStackIsolatedNetwork, out *v1beta3.CloudStackIsolatedNetwork, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(in, out, s) } -func autoConvert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(in *v1beta2.CloudStackIsolatedNetwork, out *CloudStackIsolatedNetwork, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(in *v1beta3.CloudStackIsolatedNetwork, out *CloudStackIsolatedNetwork, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork is an autogenerated conversion function. -func Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(in *v1beta2.CloudStackIsolatedNetwork, out *CloudStackIsolatedNetwork, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(in, out, s) +// Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork is an autogenerated conversion function. +func Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(in *v1beta3.CloudStackIsolatedNetwork, out *CloudStackIsolatedNetwork, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(in, out, s) } -func autoConvert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(in *CloudStackIsolatedNetworkList, out *v1beta2.CloudStackIsolatedNetworkList, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(in *CloudStackIsolatedNetworkList, out *v1beta3.CloudStackIsolatedNetworkList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]v1beta2.CloudStackIsolatedNetwork, len(*in)) + *out = make([]v1beta3.CloudStackIsolatedNetwork, len(*in)) for i := range *in { - if err := Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta1_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -453,18 +463,18 @@ func autoConvert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsol return nil } -// Convert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList is an autogenerated conversion function. -func Convert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(in *CloudStackIsolatedNetworkList, out *v1beta2.CloudStackIsolatedNetworkList, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(in, out, s) +// Convert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList is an autogenerated conversion function. +func Convert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(in *CloudStackIsolatedNetworkList, out *v1beta3.CloudStackIsolatedNetworkList, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(in, out, s) } -func autoConvert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(in *v1beta2.CloudStackIsolatedNetworkList, out *CloudStackIsolatedNetworkList, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(in *v1beta3.CloudStackIsolatedNetworkList, out *CloudStackIsolatedNetworkList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]CloudStackIsolatedNetwork, len(*in)) for i := range *in { - if err := Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta1_CloudStackIsolatedNetwork(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -474,99 +484,95 @@ func autoConvert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsol return nil } -// Convert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList is an autogenerated conversion function. -func Convert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(in *v1beta2.CloudStackIsolatedNetworkList, out *CloudStackIsolatedNetworkList, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(in, out, s) +// Convert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(in *v1beta3.CloudStackIsolatedNetworkList, out *CloudStackIsolatedNetworkList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta1_CloudStackIsolatedNetworkList(in, out, s) } -func autoConvert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(in *CloudStackIsolatedNetworkSpec, out *v1beta2.CloudStackIsolatedNetworkSpec, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(in *CloudStackIsolatedNetworkSpec, out *v1beta3.CloudStackIsolatedNetworkSpec, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID out.ControlPlaneEndpoint = in.ControlPlaneEndpoint return nil } -// Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec is an autogenerated conversion function. -func Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(in *CloudStackIsolatedNetworkSpec, out *v1beta2.CloudStackIsolatedNetworkSpec, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(in, out, s) +// Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec is an autogenerated conversion function. +func Convert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(in *CloudStackIsolatedNetworkSpec, out *v1beta3.CloudStackIsolatedNetworkSpec, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(in, out, s) } -func autoConvert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(in *v1beta2.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(in *v1beta3.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID out.ControlPlaneEndpoint = in.ControlPlaneEndpoint - // INFO: in.FailureDomainName opted out of conversion generation + // WARNING: in.FailureDomainName requires manual conversion: does not exist in peer-type + // WARNING: in.Gateway requires manual conversion: does not exist in peer-type + // WARNING: in.Netmask requires manual conversion: does not exist in peer-type + // WARNING: in.Offering requires manual conversion: does not exist in peer-type + // WARNING: in.VPC requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec is an autogenerated conversion function. -func Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(in *v1beta2.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta1_CloudStackIsolatedNetworkSpec(in, out, s) -} - -func autoConvert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(in *CloudStackIsolatedNetworkStatus, out *v1beta2.CloudStackIsolatedNetworkStatus, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(in *CloudStackIsolatedNetworkStatus, out *v1beta3.CloudStackIsolatedNetworkStatus, s conversion.Scope) error { out.PublicIPID = in.PublicIPID out.LBRuleID = in.LBRuleID out.Ready = in.Ready return nil } -// Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus is an autogenerated conversion function. -func Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(in *CloudStackIsolatedNetworkStatus, out *v1beta2.CloudStackIsolatedNetworkStatus, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(in, out, s) +// Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus is an autogenerated conversion function. +func Convert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(in *CloudStackIsolatedNetworkStatus, out *v1beta3.CloudStackIsolatedNetworkStatus, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(in, out, s) } -func autoConvert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(in *v1beta2.CloudStackIsolatedNetworkStatus, out *CloudStackIsolatedNetworkStatus, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(in *v1beta3.CloudStackIsolatedNetworkStatus, out *CloudStackIsolatedNetworkStatus, s conversion.Scope) error { out.PublicIPID = in.PublicIPID out.LBRuleID = in.LBRuleID + // WARNING: in.RoutingMode requires manual conversion: does not exist in peer-type + // WARNING: in.FirewallRulesOpened requires manual conversion: does not exist in peer-type out.Ready = in.Ready return nil } -// Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus is an autogenerated conversion function. -func Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(in *v1beta2.CloudStackIsolatedNetworkStatus, out *CloudStackIsolatedNetworkStatus, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta1_CloudStackIsolatedNetworkStatus(in, out, s) -} - -func autoConvert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine(in *CloudStackMachine, out *v1beta2.CloudStackMachine, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine(in *CloudStackMachine, out *v1beta3.CloudStackMachine, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta1_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta1_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine(in *CloudStackMachine, out *v1beta2.CloudStackMachine, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine(in, out, s) +// Convert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine(in *CloudStackMachine, out *v1beta3.CloudStackMachine, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine(in, out, s) } -func autoConvert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine(in *v1beta2.CloudStackMachine, out *CloudStackMachine, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine(in *v1beta3.CloudStackMachine, out *CloudStackMachine, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta3_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta2_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta3_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine(in *v1beta2.CloudStackMachine, out *CloudStackMachine, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine(in, out, s) +// Convert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine(in *v1beta3.CloudStackMachine, out *CloudStackMachine, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineList_To_v1beta2_CloudStackMachineList(in *CloudStackMachineList, out *v1beta2.CloudStackMachineList, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineList_To_v1beta3_CloudStackMachineList(in *CloudStackMachineList, out *v1beta3.CloudStackMachineList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]v1beta2.CloudStackMachine, len(*in)) + *out = make([]v1beta3.CloudStackMachine, len(*in)) for i := range *in { - if err := Convert_v1beta1_CloudStackMachine_To_v1beta2_CloudStackMachine(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta1_CloudStackMachine_To_v1beta3_CloudStackMachine(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -576,18 +582,18 @@ func autoConvert_v1beta1_CloudStackMachineList_To_v1beta2_CloudStackMachineList( return nil } -// Convert_v1beta1_CloudStackMachineList_To_v1beta2_CloudStackMachineList is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineList_To_v1beta2_CloudStackMachineList(in *CloudStackMachineList, out *v1beta2.CloudStackMachineList, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineList_To_v1beta2_CloudStackMachineList(in, out, s) +// Convert_v1beta1_CloudStackMachineList_To_v1beta3_CloudStackMachineList is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineList_To_v1beta3_CloudStackMachineList(in *CloudStackMachineList, out *v1beta3.CloudStackMachineList, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineList_To_v1beta3_CloudStackMachineList(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineList_To_v1beta1_CloudStackMachineList(in *v1beta2.CloudStackMachineList, out *CloudStackMachineList, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineList_To_v1beta1_CloudStackMachineList(in *v1beta3.CloudStackMachineList, out *CloudStackMachineList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]CloudStackMachine, len(*in)) for i := range *in { - if err := Convert_v1beta2_CloudStackMachine_To_v1beta1_CloudStackMachine(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta3_CloudStackMachine_To_v1beta1_CloudStackMachine(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -597,243 +603,217 @@ func autoConvert_v1beta2_CloudStackMachineList_To_v1beta1_CloudStackMachineList( return nil } -// Convert_v1beta2_CloudStackMachineList_To_v1beta1_CloudStackMachineList is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineList_To_v1beta1_CloudStackMachineList(in *v1beta2.CloudStackMachineList, out *CloudStackMachineList, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineList_To_v1beta1_CloudStackMachineList(in, out, s) +// Convert_v1beta3_CloudStackMachineList_To_v1beta1_CloudStackMachineList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineList_To_v1beta1_CloudStackMachineList(in *v1beta3.CloudStackMachineList, out *CloudStackMachineList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineList_To_v1beta1_CloudStackMachineList(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in *CloudStackMachineSpec, out *v1beta2.CloudStackMachineSpec, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(in *CloudStackMachineSpec, out *v1beta3.CloudStackMachineSpec, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.InstanceID = in.InstanceID - if err := Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(&in.Offering, &out.Offering, s); err != nil { + out.InstanceID = (*string)(unsafe.Pointer(in.InstanceID)) + if err := Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(&in.Offering, &out.Offering, s); err != nil { return err } - if err := Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(&in.Template, &out.Template, s); err != nil { + if err := Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(&in.Template, &out.Template, s); err != nil { return err } - if err := Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(&in.DiskOffering, &out.DiskOffering, s); err != nil { + if err := Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(&in.DiskOffering, &out.DiskOffering, s); err != nil { return err } out.SSHKey = in.SSHKey - out.Details = in.Details - out.AffinityGroupIDs = in.AffinityGroupIDs + out.Details = *(*map[string]string)(unsafe.Pointer(&in.Details)) + out.AffinityGroupIDs = *(*[]string)(unsafe.Pointer(&in.AffinityGroupIDs)) out.Affinity = in.Affinity - out.AffinityGroupRef = in.AffinityGroupRef - out.ProviderID = in.ProviderID + out.AffinityGroupRef = (*corev1.ObjectReference)(unsafe.Pointer(in.AffinityGroupRef)) + out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) // INFO: in.ZoneID opted out of conversion generation // INFO: in.ZoneName opted out of conversion generation // INFO: in.IdentityRef opted out of conversion generation return nil } -// Convert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in *CloudStackMachineSpec, out *v1beta2.CloudStackMachineSpec, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in, out, s) +// Convert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(in *CloudStackMachineSpec, out *v1beta3.CloudStackMachineSpec, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(in *v1beta2.CloudStackMachineSpec, out *CloudStackMachineSpec, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(in *v1beta3.CloudStackMachineSpec, out *CloudStackMachineSpec, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - out.InstanceID = in.InstanceID - if err := Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(&in.Offering, &out.Offering, s); err != nil { + out.InstanceID = (*string)(unsafe.Pointer(in.InstanceID)) + if err := Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(&in.Offering, &out.Offering, s); err != nil { return err } - if err := Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(&in.Template, &out.Template, s); err != nil { + if err := Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(&in.Template, &out.Template, s); err != nil { return err } - if err := Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(&in.DiskOffering, &out.DiskOffering, s); err != nil { + if err := Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(&in.DiskOffering, &out.DiskOffering, s); err != nil { return err } + // WARNING: in.Networks requires manual conversion: does not exist in peer-type out.SSHKey = in.SSHKey - out.Details = in.Details - out.AffinityGroupIDs = in.AffinityGroupIDs + out.Details = *(*map[string]string)(unsafe.Pointer(&in.Details)) + out.AffinityGroupIDs = *(*[]string)(unsafe.Pointer(&in.AffinityGroupIDs)) out.Affinity = in.Affinity - out.AffinityGroupRef = in.AffinityGroupRef - out.ProviderID = in.ProviderID - // INFO: in.FailureDomainName opted out of conversion generation + out.AffinityGroupRef = (*corev1.ObjectReference)(unsafe.Pointer(in.AffinityGroupRef)) + out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) + // WARNING: in.FailureDomainName requires manual conversion: does not exist in peer-type + // WARNING: in.UncompressedUserData requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(in *v1beta2.CloudStackMachineSpec, out *CloudStackMachineSpec, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(in, out, s) -} - -func autoConvert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(in *CloudStackMachineStateChecker, out *v1beta2.CloudStackMachineStateChecker, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(in *CloudStackMachineStateChecker, out *v1beta3.CloudStackMachineStateChecker, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(in *CloudStackMachineStateChecker, out *v1beta2.CloudStackMachineStateChecker, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(in, out, s) +// Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(in *CloudStackMachineStateChecker, out *v1beta3.CloudStackMachineStateChecker, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(in *v1beta2.CloudStackMachineStateChecker, out *CloudStackMachineStateChecker, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(in *v1beta3.CloudStackMachineStateChecker, out *CloudStackMachineStateChecker, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(&in.Spec, &out.Spec, s); err != nil { return err } - if err := Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(&in.Status, &out.Status, s); err != nil { + if err := Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(&in.Status, &out.Status, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(in *v1beta2.CloudStackMachineStateChecker, out *CloudStackMachineStateChecker, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(in, out, s) +// Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(in *v1beta3.CloudStackMachineStateChecker, out *CloudStackMachineStateChecker, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(in *CloudStackMachineStateCheckerList, out *v1beta2.CloudStackMachineStateCheckerList, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(in *CloudStackMachineStateCheckerList, out *v1beta3.CloudStackMachineStateCheckerList, s conversion.Scope) error { out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]v1beta2.CloudStackMachineStateChecker, len(*in)) - for i := range *in { - if err := Convert_v1beta1_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.Items = nil - } + out.Items = *(*[]v1beta3.CloudStackMachineStateChecker)(unsafe.Pointer(&in.Items)) return nil } -// Convert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(in *CloudStackMachineStateCheckerList, out *v1beta2.CloudStackMachineStateCheckerList, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(in, out, s) +// Convert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(in *CloudStackMachineStateCheckerList, out *v1beta3.CloudStackMachineStateCheckerList, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(in *v1beta2.CloudStackMachineStateCheckerList, out *CloudStackMachineStateCheckerList, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(in *v1beta3.CloudStackMachineStateCheckerList, out *CloudStackMachineStateCheckerList, s conversion.Scope) error { out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]CloudStackMachineStateChecker, len(*in)) - for i := range *in { - if err := Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta1_CloudStackMachineStateChecker(&(*in)[i], &(*out)[i], s); err != nil { - return err - } - } - } else { - out.Items = nil - } + out.Items = *(*[]CloudStackMachineStateChecker)(unsafe.Pointer(&in.Items)) return nil } -// Convert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(in *v1beta2.CloudStackMachineStateCheckerList, out *CloudStackMachineStateCheckerList, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(in, out, s) +// Convert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(in *v1beta3.CloudStackMachineStateCheckerList, out *CloudStackMachineStateCheckerList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta1_CloudStackMachineStateCheckerList(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(in *CloudStackMachineStateCheckerSpec, out *v1beta2.CloudStackMachineStateCheckerSpec, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(in *CloudStackMachineStateCheckerSpec, out *v1beta3.CloudStackMachineStateCheckerSpec, s conversion.Scope) error { out.InstanceID = in.InstanceID return nil } -// Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(in *CloudStackMachineStateCheckerSpec, out *v1beta2.CloudStackMachineStateCheckerSpec, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(in, out, s) +// Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(in *CloudStackMachineStateCheckerSpec, out *v1beta3.CloudStackMachineStateCheckerSpec, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(in *v1beta2.CloudStackMachineStateCheckerSpec, out *CloudStackMachineStateCheckerSpec, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(in *v1beta3.CloudStackMachineStateCheckerSpec, out *CloudStackMachineStateCheckerSpec, s conversion.Scope) error { out.InstanceID = in.InstanceID return nil } -// Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(in *v1beta2.CloudStackMachineStateCheckerSpec, out *CloudStackMachineStateCheckerSpec, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(in, out, s) +// Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(in *v1beta3.CloudStackMachineStateCheckerSpec, out *CloudStackMachineStateCheckerSpec, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta1_CloudStackMachineStateCheckerSpec(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(in *CloudStackMachineStateCheckerStatus, out *v1beta2.CloudStackMachineStateCheckerStatus, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(in *CloudStackMachineStateCheckerStatus, out *v1beta3.CloudStackMachineStateCheckerStatus, s conversion.Scope) error { out.Ready = in.Ready return nil } -// Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(in *CloudStackMachineStateCheckerStatus, out *v1beta2.CloudStackMachineStateCheckerStatus, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(in, out, s) +// Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(in *CloudStackMachineStateCheckerStatus, out *v1beta3.CloudStackMachineStateCheckerStatus, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(in *v1beta2.CloudStackMachineStateCheckerStatus, out *CloudStackMachineStateCheckerStatus, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(in *v1beta3.CloudStackMachineStateCheckerStatus, out *CloudStackMachineStateCheckerStatus, s conversion.Scope) error { out.Ready = in.Ready return nil } -// Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(in *v1beta2.CloudStackMachineStateCheckerStatus, out *CloudStackMachineStateCheckerStatus, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(in, out, s) +// Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(in *v1beta3.CloudStackMachineStateCheckerStatus, out *CloudStackMachineStateCheckerStatus, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta1_CloudStackMachineStateCheckerStatus(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(in *CloudStackMachineStatus, out *v1beta2.CloudStackMachineStatus, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(in *CloudStackMachineStatus, out *v1beta3.CloudStackMachineStatus, s conversion.Scope) error { // INFO: in.ZoneID opted out of conversion generation - out.Addresses = in.Addresses + out.Addresses = *(*[]corev1.NodeAddress)(unsafe.Pointer(&in.Addresses)) out.InstanceState = string(in.InstanceState) out.InstanceStateLastUpdated = in.InstanceStateLastUpdated out.Ready = in.Ready return nil } -// Convert_v1beta1_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(in *CloudStackMachineStatus, out *v1beta2.CloudStackMachineStatus, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(in, out, s) +// Convert_v1beta1_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(in *CloudStackMachineStatus, out *v1beta3.CloudStackMachineStatus, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(in *v1beta2.CloudStackMachineStatus, out *CloudStackMachineStatus, s conversion.Scope) error { - out.Addresses = in.Addresses +func autoConvert_v1beta3_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(in *v1beta3.CloudStackMachineStatus, out *CloudStackMachineStatus, s conversion.Scope) error { + out.Addresses = *(*[]corev1.NodeAddress)(unsafe.Pointer(&in.Addresses)) out.InstanceState = InstanceState(in.InstanceState) out.InstanceStateLastUpdated = in.InstanceStateLastUpdated out.Ready = in.Ready + // WARNING: in.Status requires manual conversion: does not exist in peer-type + // WARNING: in.Reason requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(in *v1beta2.CloudStackMachineStatus, out *CloudStackMachineStatus, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineStatus_To_v1beta1_CloudStackMachineStatus(in, out, s) -} - -func autoConvert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(in *CloudStackMachineTemplate, out *v1beta2.CloudStackMachineTemplate, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(in *CloudStackMachineTemplate, out *v1beta3.CloudStackMachineTemplate, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(in *CloudStackMachineTemplate, out *v1beta2.CloudStackMachineTemplate, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(in, out, s) +// Convert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(in *CloudStackMachineTemplate, out *v1beta3.CloudStackMachineTemplate, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(in *v1beta2.CloudStackMachineTemplate, out *CloudStackMachineTemplate, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(in *v1beta3.CloudStackMachineTemplate, out *CloudStackMachineTemplate, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(in *v1beta2.CloudStackMachineTemplate, out *CloudStackMachineTemplate, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(in, out, s) +// Convert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(in *v1beta3.CloudStackMachineTemplate, out *CloudStackMachineTemplate, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(in *CloudStackMachineTemplateList, out *v1beta2.CloudStackMachineTemplateList, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(in *CloudStackMachineTemplateList, out *v1beta3.CloudStackMachineTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]v1beta2.CloudStackMachineTemplate, len(*in)) + *out = make([]v1beta3.CloudStackMachineTemplate, len(*in)) for i := range *in { - if err := Convert_v1beta1_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta1_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -843,18 +823,18 @@ func autoConvert_v1beta1_CloudStackMachineTemplateList_To_v1beta2_CloudStackMach return nil } -// Convert_v1beta1_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(in *CloudStackMachineTemplateList, out *v1beta2.CloudStackMachineTemplateList, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(in, out, s) +// Convert_v1beta1_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(in *CloudStackMachineTemplateList, out *v1beta3.CloudStackMachineTemplateList, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(in *v1beta2.CloudStackMachineTemplateList, out *CloudStackMachineTemplateList, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(in *v1beta3.CloudStackMachineTemplateList, out *CloudStackMachineTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]CloudStackMachineTemplate, len(*in)) for i := range *in { - if err := Convert_v1beta2_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta3_CloudStackMachineTemplate_To_v1beta1_CloudStackMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -864,63 +844,53 @@ func autoConvert_v1beta2_CloudStackMachineTemplateList_To_v1beta1_CloudStackMach return nil } -// Convert_v1beta2_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(in *v1beta2.CloudStackMachineTemplateList, out *CloudStackMachineTemplateList, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(in, out, s) +// Convert_v1beta3_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(in *v1beta3.CloudStackMachineTemplateList, out *CloudStackMachineTemplateList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineTemplateList_To_v1beta1_CloudStackMachineTemplateList(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(in *CloudStackMachineTemplateResource, out *v1beta2.CloudStackMachineTemplateResource, s conversion.Scope) error { - out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta1_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { +func autoConvert_v1beta1_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(in *CloudStackMachineTemplateResource, out *v1beta3.CloudStackMachineTemplateResource, s conversion.Scope) error { + if err := Convert_v1_ObjectMeta_To_v1beta1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } - return nil -} - -// Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(in *CloudStackMachineTemplateResource, out *v1beta2.CloudStackMachineTemplateResource, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(in, out, s) -} - -func autoConvert_v1beta2_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(in *v1beta2.CloudStackMachineTemplateResource, out *CloudStackMachineTemplateResource, s conversion.Scope) error { - out.ObjectMeta = in.ObjectMeta - if err := Convert_v1beta2_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + if err := Convert_v1beta1_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(in *v1beta2.CloudStackMachineTemplateResource, out *CloudStackMachineTemplateResource, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(in, out, s) +// Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource is an autogenerated conversion function. +func Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(in *CloudStackMachineTemplateResource, out *v1beta3.CloudStackMachineTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(in, out, s) } -func autoConvert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(in *CloudStackMachineTemplateSpec, out *v1beta2.CloudStackMachineTemplateSpec, s conversion.Scope) error { - if err := Convert_v1beta1_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(&in.Spec, &out.Spec, s); err != nil { +func autoConvert_v1beta3_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(in *v1beta3.CloudStackMachineTemplateResource, out *CloudStackMachineTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta1_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackMachineSpec_To_v1beta1_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec is an autogenerated conversion function. -func Convert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(in *CloudStackMachineTemplateSpec, out *v1beta2.CloudStackMachineTemplateSpec, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(in, out, s) +// Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(in *v1beta3.CloudStackMachineTemplateResource, out *CloudStackMachineTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(in, out, s) } -func autoConvert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(in *v1beta2.CloudStackMachineTemplateSpec, out *CloudStackMachineTemplateSpec, s conversion.Scope) error { - if err := Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta1_CloudStackMachineTemplateResource(&in.Spec, &out.Spec, s); err != nil { - return err - } +func autoConvert_v1beta1_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(in *CloudStackMachineTemplateSpec, out *v1beta3.CloudStackMachineTemplateSpec, s conversion.Scope) error { + // WARNING: in.Spec requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec is an autogenerated conversion function. -func Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(in *v1beta2.CloudStackMachineTemplateSpec, out *CloudStackMachineTemplateSpec, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(in, out, s) +func autoConvert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta1_CloudStackMachineTemplateSpec(in *v1beta3.CloudStackMachineTemplateSpec, out *CloudStackMachineTemplateSpec, s conversion.Scope) error { + // WARNING: in.Template requires manual conversion: does not exist in peer-type + return nil } -func autoConvert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(in *CloudStackResourceDiskOffering, out *v1beta2.CloudStackResourceDiskOffering, s conversion.Scope) error { - if err := Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(&in.CloudStackResourceIdentifier, &out.CloudStackResourceIdentifier, s); err != nil { +func autoConvert_v1beta1_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(in *CloudStackResourceDiskOffering, out *v1beta3.CloudStackResourceDiskOffering, s conversion.Scope) error { + if err := Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(&in.CloudStackResourceIdentifier, &out.CloudStackResourceIdentifier, s); err != nil { return err } out.CustomSize = in.CustomSize @@ -931,13 +901,13 @@ func autoConvert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackRes return nil } -// Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering is an autogenerated conversion function. -func Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(in *CloudStackResourceDiskOffering, out *v1beta2.CloudStackResourceDiskOffering, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(in, out, s) +// Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering is an autogenerated conversion function. +func Convert_v1beta1_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(in *CloudStackResourceDiskOffering, out *v1beta3.CloudStackResourceDiskOffering, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(in, out, s) } -func autoConvert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(in *v1beta2.CloudStackResourceDiskOffering, out *CloudStackResourceDiskOffering, s conversion.Scope) error { - if err := Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(&in.CloudStackResourceIdentifier, &out.CloudStackResourceIdentifier, s); err != nil { +func autoConvert_v1beta3_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(in *v1beta3.CloudStackResourceDiskOffering, out *CloudStackResourceDiskOffering, s conversion.Scope) error { + if err := Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(&in.CloudStackResourceIdentifier, &out.CloudStackResourceIdentifier, s); err != nil { return err } out.CustomSize = in.CustomSize @@ -948,81 +918,81 @@ func autoConvert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackRes return nil } -// Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering is an autogenerated conversion function. -func Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(in *v1beta2.CloudStackResourceDiskOffering, out *CloudStackResourceDiskOffering, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(in, out, s) +// Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering is an autogenerated conversion function. +func Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(in *v1beta3.CloudStackResourceDiskOffering, out *CloudStackResourceDiskOffering, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackResourceDiskOffering_To_v1beta1_CloudStackResourceDiskOffering(in, out, s) } -func autoConvert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(in *CloudStackResourceIdentifier, out *v1beta2.CloudStackResourceIdentifier, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(in *CloudStackResourceIdentifier, out *v1beta3.CloudStackResourceIdentifier, s conversion.Scope) error { out.ID = in.ID out.Name = in.Name return nil } -// Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier is an autogenerated conversion function. -func Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(in *CloudStackResourceIdentifier, out *v1beta2.CloudStackResourceIdentifier, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(in, out, s) +// Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier is an autogenerated conversion function. +func Convert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(in *CloudStackResourceIdentifier, out *v1beta3.CloudStackResourceIdentifier, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(in, out, s) } -func autoConvert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(in *v1beta2.CloudStackResourceIdentifier, out *CloudStackResourceIdentifier, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(in *v1beta3.CloudStackResourceIdentifier, out *CloudStackResourceIdentifier, s conversion.Scope) error { out.ID = in.ID out.Name = in.Name return nil } -// Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier is an autogenerated conversion function. -func Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(in *v1beta2.CloudStackResourceIdentifier, out *CloudStackResourceIdentifier, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(in, out, s) +// Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier is an autogenerated conversion function. +func Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(in *v1beta3.CloudStackResourceIdentifier, out *CloudStackResourceIdentifier, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackResourceIdentifier_To_v1beta1_CloudStackResourceIdentifier(in, out, s) } -func autoConvert_v1beta1_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(in *CloudStackZoneSpec, out *v1beta2.CloudStackZoneSpec, s conversion.Scope) error { +func autoConvert_v1beta1_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(in *CloudStackZoneSpec, out *v1beta3.CloudStackZoneSpec, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - if err := Convert_v1beta1_Network_To_v1beta2_Network(&in.Network, &out.Network, s); err != nil { + if err := Convert_v1beta1_Network_To_v1beta3_Network(&in.Network, &out.Network, s); err != nil { return err } return nil } -// Convert_v1beta1_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec is an autogenerated conversion function. -func Convert_v1beta1_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(in *CloudStackZoneSpec, out *v1beta2.CloudStackZoneSpec, s conversion.Scope) error { - return autoConvert_v1beta1_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(in, out, s) +// Convert_v1beta1_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec is an autogenerated conversion function. +func Convert_v1beta1_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(in *CloudStackZoneSpec, out *v1beta3.CloudStackZoneSpec, s conversion.Scope) error { + return autoConvert_v1beta1_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(in, out, s) } -func autoConvert_v1beta2_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(in *v1beta2.CloudStackZoneSpec, out *CloudStackZoneSpec, s conversion.Scope) error { +func autoConvert_v1beta3_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(in *v1beta3.CloudStackZoneSpec, out *CloudStackZoneSpec, s conversion.Scope) error { out.Name = in.Name out.ID = in.ID - if err := Convert_v1beta2_Network_To_v1beta1_Network(&in.Network, &out.Network, s); err != nil { + if err := Convert_v1beta3_Network_To_v1beta1_Network(&in.Network, &out.Network, s); err != nil { return err } return nil } -// Convert_v1beta2_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec is an autogenerated conversion function. -func Convert_v1beta2_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(in *v1beta2.CloudStackZoneSpec, out *CloudStackZoneSpec, s conversion.Scope) error { - return autoConvert_v1beta2_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(in, out, s) +// Convert_v1beta3_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec is an autogenerated conversion function. +func Convert_v1beta3_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(in *v1beta3.CloudStackZoneSpec, out *CloudStackZoneSpec, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackZoneSpec_To_v1beta1_CloudStackZoneSpec(in, out, s) } -func autoConvert_v1beta1_Network_To_v1beta2_Network(in *Network, out *v1beta2.Network, s conversion.Scope) error { +func autoConvert_v1beta1_Network_To_v1beta3_Network(in *Network, out *v1beta3.Network, s conversion.Scope) error { out.ID = in.ID out.Type = in.Type out.Name = in.Name return nil } -// Convert_v1beta1_Network_To_v1beta2_Network is an autogenerated conversion function. -func Convert_v1beta1_Network_To_v1beta2_Network(in *Network, out *v1beta2.Network, s conversion.Scope) error { - return autoConvert_v1beta1_Network_To_v1beta2_Network(in, out, s) +// Convert_v1beta1_Network_To_v1beta3_Network is an autogenerated conversion function. +func Convert_v1beta1_Network_To_v1beta3_Network(in *Network, out *v1beta3.Network, s conversion.Scope) error { + return autoConvert_v1beta1_Network_To_v1beta3_Network(in, out, s) } -func autoConvert_v1beta2_Network_To_v1beta1_Network(in *v1beta2.Network, out *Network, s conversion.Scope) error { +func autoConvert_v1beta3_Network_To_v1beta1_Network(in *v1beta3.Network, out *Network, s conversion.Scope) error { out.ID = in.ID out.Type = in.Type out.Name = in.Name + // WARNING: in.Gateway requires manual conversion: does not exist in peer-type + // WARNING: in.Netmask requires manual conversion: does not exist in peer-type + // WARNING: in.Offering requires manual conversion: does not exist in peer-type + // WARNING: in.VPC requires manual conversion: does not exist in peer-type + // WARNING: in.RoutingMode requires manual conversion: does not exist in peer-type return nil } - -// Convert_v1beta2_Network_To_v1beta1_Network is an autogenerated conversion function. -func Convert_v1beta2_Network_To_v1beta1_Network(in *v1beta2.Network, out *Network, s conversion.Scope) error { - return autoConvert_v1beta2_Network_To_v1beta1_Network(in, out, s) -} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 0d2167fb..1ebfffe1 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* Copyright 2022. @@ -23,7 +22,7 @@ package v1beta1 import ( "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" apiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" ) diff --git a/api/v1beta2/cloudstackaffinitygroup_conversion.go b/api/v1beta2/cloudstackaffinitygroup_conversion.go new file mode 100644 index 00000000..6eaa382c --- /dev/null +++ b/api/v1beta2/cloudstackaffinitygroup_conversion.go @@ -0,0 +1,32 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackAffinityGroup) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackAffinityGroup) + return Convert_v1beta2_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(src, dst, nil) +} + +func (dst *CloudStackAffinityGroup) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackAffinityGroup) + return Convert_v1beta3_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(src, dst, nil) +} diff --git a/api/v1beta2/cloudstackaffinitygroup_types.go b/api/v1beta2/cloudstackaffinitygroup_types.go index 72674c29..0f2a20e2 100644 --- a/api/v1beta2/cloudstackaffinitygroup_types.go +++ b/api/v1beta2/cloudstackaffinitygroup_types.go @@ -37,7 +37,6 @@ type CloudStackAffinityGroupSpec struct { // FailureDomainName -- the name of the FailureDomain the machine is placed in. // +optional - // +k8s:conversion-gen=false FailureDomainName string `json:"failureDomainName,omitempty"` } @@ -49,7 +48,6 @@ type CloudStackAffinityGroupStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion // CloudStackAffinityGroup is the Schema for the cloudstackaffinitygroups API type CloudStackAffinityGroup struct { @@ -70,5 +68,5 @@ type CloudStackAffinityGroupList struct { } func init() { - SchemeBuilder.Register(&CloudStackAffinityGroup{}, &CloudStackAffinityGroupList{}) + objectTypes = append(objectTypes, &CloudStackAffinityGroup{}, &CloudStackAffinityGroupList{}) } diff --git a/api/v1beta2/cloudstackcluster_conversion.go b/api/v1beta2/cloudstackcluster_conversion.go new file mode 100644 index 00000000..dbd8a21a --- /dev/null +++ b/api/v1beta2/cloudstackcluster_conversion.go @@ -0,0 +1,41 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackCluster) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackCluster) + return Convert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster(src, dst, nil) +} + +func (dst *CloudStackCluster) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackCluster) + return Convert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster(src, dst, nil) +} + +func Convert_v1beta3_CloudStackClusterSpec_To_v1beta2_CloudStackClusterSpec(in *v1beta3.CloudStackClusterSpec, out *CloudStackClusterSpec, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackClusterSpec_To_v1beta2_CloudStackClusterSpec(in, out, s) +} + +func Convert_v1beta3_CloudStackClusterStatus_To_v1beta2_CloudStackClusterStatus(in *v1beta3.CloudStackClusterStatus, out *CloudStackClusterStatus, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackClusterStatus_To_v1beta2_CloudStackClusterStatus(in, out, s) +} diff --git a/api/v1beta2/cloudstackcluster_types.go b/api/v1beta2/cloudstackcluster_types.go index fb7752dc..354fe8f9 100644 --- a/api/v1beta2/cloudstackcluster_types.go +++ b/api/v1beta2/cloudstackcluster_types.go @@ -49,7 +49,6 @@ type CloudStackClusterStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion // CloudStackCluster is the Schema for the cloudstackclusters API type CloudStackCluster struct { @@ -72,5 +71,5 @@ type CloudStackClusterList struct { } func init() { - SchemeBuilder.Register(&CloudStackCluster{}, &CloudStackClusterList{}) + objectTypes = append(objectTypes, &CloudStackCluster{}, &CloudStackClusterList{}) } diff --git a/api/v1beta2/cloudstackcluster_webhook_test.go b/api/v1beta2/cloudstackcluster_webhook_test.go deleted file mode 100644 index ed6f77fe..00000000 --- a/api/v1beta2/cloudstackcluster_webhook_test.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2022 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1beta2_test - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" -) - -var _ = Describe("CloudStackCluster webhooks", func() { - var ctx context.Context - forbiddenRegex := "admission webhook.*denied the request.*Forbidden\\: %s" - requiredRegex := "admission webhook.*denied the request.*Required value\\: %s" - - BeforeEach(func() { // Reset test vars to initial state. - ctx = context.Background() - dummies.SetDummyVars() // Reset cluster var. - _ = k8sClient.Delete(ctx, dummies.CSCluster) // Delete any remnants. - dummies.SetDummyVars() // Reset again since the k8s client can set this on delete. - }) - - Context("When creating a CloudStackCluster", func() { - It("Should accept a CloudStackCluster with all attributes present", func() { - Ω(k8sClient.Create(ctx, dummies.CSCluster)).Should(Succeed()) - }) - - It("Should reject a CloudStackCluster with missing Zones.Network attribute", func() { - dummies.CSCluster.Spec.FailureDomains = []infrav1.CloudStackFailureDomainSpec{{}} - dummies.CSCluster.Spec.FailureDomains[0].Zone.Name = "ZoneWNoNetwork" - Ω(k8sClient.Create(ctx, dummies.CSCluster)).Should( - MatchError(MatchRegexp(requiredRegex, "each Zone requires a Network specification"))) - }) - - It("Should reject a CloudStackCluster with missing Zone attribute", func() { - dummies.CSCluster.Spec.FailureDomains[0].Zone = infrav1.CloudStackZoneSpec{} - Ω(k8sClient.Create(ctx, dummies.CSCluster)).Should(MatchError(MatchRegexp(requiredRegex, - "each Zone requires a Network specification"))) - }) - }) - - Context("When updating a CloudStackCluster", func() { - BeforeEach(func() { - Ω(k8sClient.Create(ctx, dummies.CSCluster)).Should(Succeed()) - }) - - It("Should reject updates to CloudStackCluster FailureDomains", func() { - dummies.CSCluster.Spec.FailureDomains[0].Zone.Name = "SomeRandomUpdate" - Ω(k8sClient.Update(ctx, dummies.CSCluster)).Should(MatchError(MatchRegexp(forbiddenRegex, "Cannot change FailureDomain"))) - }) - It("Should reject updates to Networks specified in CloudStackCluster Zones", func() { - dummies.CSCluster.Spec.FailureDomains[0].Zone.Network.Name = "ArbitraryUpdateNetworkName" - Ω(k8sClient.Update(ctx, dummies.CSCluster)).Should(MatchError(MatchRegexp(forbiddenRegex, "Cannot change FailureDomain"))) - }) - It("Should reject updates to CloudStackCluster controlplaneendpoint.host", func() { - dummies.CSCluster.Spec.ControlPlaneEndpoint.Host = "1.1.1.1" - Ω(k8sClient.Update(ctx, dummies.CSCluster)). - Should(MatchError(MatchRegexp(forbiddenRegex, "controlplaneendpoint\\.host"))) - }) - - It("Should reject updates to CloudStackCluster controlplaneendpoint.port", func() { - dummies.CSCluster.Spec.ControlPlaneEndpoint.Port = int32(1234) - Ω(k8sClient.Update(ctx, dummies.CSCluster)). - Should(MatchError(MatchRegexp(forbiddenRegex, "controlplaneendpoint\\.port"))) - }) - }) -}) diff --git a/api/v1beta2/cloudstackfailuredomain_conversion.go b/api/v1beta2/cloudstackfailuredomain_conversion.go new file mode 100644 index 00000000..eee1a492 --- /dev/null +++ b/api/v1beta2/cloudstackfailuredomain_conversion.go @@ -0,0 +1,37 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackFailureDomain) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackFailureDomain) + return Convert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain(src, dst, nil) +} + +func (dst *CloudStackFailureDomain) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackFailureDomain) + return Convert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain(src, dst, nil) +} + +func Convert_v1beta3_CloudStackFailureDomainSpec_To_v1beta2_CloudStackFailureDomainSpec(in *v1beta3.CloudStackFailureDomainSpec, out *CloudStackFailureDomainSpec, s machineryconversion.Scope) error { // nolint + return autoConvert_v1beta3_CloudStackFailureDomainSpec_To_v1beta2_CloudStackFailureDomainSpec(in, out, s) +} diff --git a/api/v1beta2/cloudstackfailuredomain_types.go b/api/v1beta2/cloudstackfailuredomain_types.go index 5b1ddaa7..9205b25b 100644 --- a/api/v1beta2/cloudstackfailuredomain_types.go +++ b/api/v1beta2/cloudstackfailuredomain_types.go @@ -97,8 +97,6 @@ type CloudStackFailureDomainStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion -//+ks8:conversion-gen=false // CloudStackFailureDomain is the Schema for the cloudstackfailuredomains API type CloudStackFailureDomain struct { @@ -119,5 +117,5 @@ type CloudStackFailureDomainList struct { } func init() { - SchemeBuilder.Register(&CloudStackFailureDomain{}, &CloudStackFailureDomainList{}) + objectTypes = append(objectTypes, &CloudStackFailureDomain{}, &CloudStackFailureDomainList{}) } diff --git a/api/v1beta2/cloudstackisolatednetwork_conversion.go b/api/v1beta2/cloudstackisolatednetwork_conversion.go new file mode 100644 index 00000000..44e824ff --- /dev/null +++ b/api/v1beta2/cloudstackisolatednetwork_conversion.go @@ -0,0 +1,32 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackIsolatedNetwork) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackIsolatedNetwork) + return Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(src, dst, nil) +} + +func (dst *CloudStackIsolatedNetwork) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackIsolatedNetwork) + return Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(src, dst, nil) +} diff --git a/api/v1beta2/cloudstackisolatednetwork_types.go b/api/v1beta2/cloudstackisolatednetwork_types.go index fca6e71f..90399489 100644 --- a/api/v1beta2/cloudstackisolatednetwork_types.go +++ b/api/v1beta2/cloudstackisolatednetwork_types.go @@ -37,7 +37,6 @@ type CloudStackIsolatedNetworkSpec struct { // The kubernetes control plane endpoint. ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"` - //+k8s:conversion-gen=false // FailureDomainName -- the FailureDomain the network is placed in. FailureDomainName string `json:"failureDomainName"` } @@ -63,7 +62,6 @@ func (n *CloudStackIsolatedNetwork) Network() *Network { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion // CloudStackIsolatedNetwork is the Schema for the cloudstackisolatednetworks API type CloudStackIsolatedNetwork struct { @@ -84,5 +82,5 @@ type CloudStackIsolatedNetworkList struct { } func init() { - SchemeBuilder.Register(&CloudStackIsolatedNetwork{}, &CloudStackIsolatedNetworkList{}) + objectTypes = append(objectTypes, &CloudStackIsolatedNetwork{}, &CloudStackIsolatedNetworkList{}) } diff --git a/api/v1beta2/cloudstackmachine_conversion.go b/api/v1beta2/cloudstackmachine_conversion.go new file mode 100644 index 00000000..48ec39a9 --- /dev/null +++ b/api/v1beta2/cloudstackmachine_conversion.go @@ -0,0 +1,61 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackMachine) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackMachine) + if err := Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(src, dst, nil); err != nil { + return err + } + + // Manually restore data + restored := &v1beta3.CloudStackMachine{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + if len(restored.Spec.Networks) > 0 { + dst.Spec.Networks = restored.Spec.Networks + } + + return nil +} + +func (dst *CloudStackMachine) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackMachine) + if err := Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion, including Networks field + err := utilconversion.MarshalData(src, dst) + return err +} + +// Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec handles the conversion from v1beta3 to v1beta2, +// ignoring the Networks field that doesn't exist in v1beta2 +func Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in *v1beta3.CloudStackMachineSpec, out *CloudStackMachineSpec, s machineryconversion.Scope) error { // nolint + // Use the auto-generated conversion function, which will handle all fields except Networks + return autoConvert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in, out, s) +} diff --git a/api/v1beta2/cloudstackmachine_types.go b/api/v1beta2/cloudstackmachine_types.go index 5fe16442..efc5d702 100644 --- a/api/v1beta2/cloudstackmachine_types.go +++ b/api/v1beta2/cloudstackmachine_types.go @@ -82,7 +82,6 @@ type CloudStackMachineSpec struct { // FailureDomainName -- the name of the FailureDomain the machine is placed in. // +optional - // +k8s:conversion-gen=false FailureDomainName string `json:"failureDomainName,omitempty"` // UncompressedUserData specifies whether the user data is gzip-compressed. @@ -157,7 +156,6 @@ func (s *CloudStackMachineStatus) TimeSinceLastStateChange() time.Duration { // +kubebuilder:object:root=true // +kubebuilder:resource:path=cloudstackmachines,scope=Namespaced,categories=cluster-api,shortName=csm -// +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this CloudStackMachine belongs" // +kubebuilder:printcolumn:name="InstanceState",type="string",JSONPath=".status.instanceState",description="CloudStack instance state" @@ -184,5 +182,5 @@ type CloudStackMachineList struct { } func init() { - SchemeBuilder.Register(&CloudStackMachine{}, &CloudStackMachineList{}) + objectTypes = append(objectTypes, &CloudStackMachine{}, &CloudStackMachineList{}) } diff --git a/api/v1beta2/cloudstackmachine_types_test.go b/api/v1beta2/cloudstackmachine_types_test.go index 3d3750db..bd585587 100644 --- a/api/v1beta2/cloudstackmachine_types_test.go +++ b/api/v1beta2/cloudstackmachine_types_test.go @@ -17,14 +17,14 @@ limitations under the License. package v1beta2_test import ( - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" capcv1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" ) -var _ = Describe("CloudStackMachineConfig_CompressUserdata", func() { +var _ = ginkgo.Describe("CloudStackMachineConfig_CompressUserdata", func() { for _, tc := range []struct { Name string Machine capcv1.CloudStackMachine @@ -43,7 +43,7 @@ var _ = Describe("CloudStackMachineConfig_CompressUserdata", func() { Name: "is false when uncompressed user data is true", Machine: capcv1.CloudStackMachine{ Spec: capcv1.CloudStackMachineSpec{ - UncompressedUserData: pointer.Bool(true), + UncompressedUserData: ptr.To(true), }, }, Expect: false, @@ -52,16 +52,16 @@ var _ = Describe("CloudStackMachineConfig_CompressUserdata", func() { Name: "Is false when uncompressed user data is false", Machine: capcv1.CloudStackMachine{ Spec: capcv1.CloudStackMachineSpec{ - UncompressedUserData: pointer.Bool(false), + UncompressedUserData: ptr.To(false), }, }, Expect: true, }, } { tc := tc - It(tc.Name, func() { + ginkgo.It(tc.Name, func() { result := tc.Machine.CompressUserdata() - Expect(result).To(Equal(tc.Expect)) + gomega.Expect(result).To(gomega.Equal(tc.Expect)) }) } }) diff --git a/api/v1beta2/cloudstackmachine_webhook_test.go b/api/v1beta2/cloudstackmachine_webhook_test.go deleted file mode 100644 index 9ee44592..00000000 --- a/api/v1beta2/cloudstackmachine_webhook_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2022 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1beta2_test - -import ( - "context" - - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("CloudStackMachine webhook", func() { - var ctx context.Context - forbiddenRegex := "admission webhook.*denied the request.*Forbidden\\: %s" - requiredRegex := "admission webhook.*denied the request.*Required value\\: %s" - - BeforeEach(func() { // Reset test vars to initial state. - dummies.SetDummyVars() - ctx = context.Background() - _ = k8sClient.Delete(ctx, dummies.CSMachine1) // Clear out any remaining machines. Ignore errors. - }) - - Context("When creating a CloudStackMachine", func() { - It("should accept CloudStackMachine with all attributes", func() { - Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) - }) - - It("should accept a CloudStackMachine with disk Offering attribute", func() { - dummies.CSMachine1.Spec.DiskOffering = dummies.DiskOffering - Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) - }) - - It("should accept a CloudStackMachine with positive disk Offering size attribute", func() { - dummies.CSMachine1.Spec.DiskOffering = dummies.DiskOffering - dummies.CSMachine1.Spec.DiskOffering.CustomSize = 1 - Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) - }) - - It("should not accept a CloudStackMachine with negative disk Offering size attribute", func() { - dummies.CSMachine1.Spec.DiskOffering = dummies.DiskOffering - dummies.CSMachine1.Spec.DiskOffering.CustomSize = -1 - Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(MatchError(MatchRegexp(forbiddenRegex, "customSizeInGB"))) - }) - - It("should reject a CloudStackMachine with missing Offering attribute", func() { - dummies.CSMachine1.Spec.Offering = infrav1.CloudStackResourceIdentifier{ID: "", Name: ""} - Expect(k8sClient.Create(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(requiredRegex, "Offering"))) - }) - - It("should reject a CloudStackMachine with missing Template attribute", func() { - dummies.CSMachine1.Spec.Template = infrav1.CloudStackResourceIdentifier{ID: "", Name: ""} - Expect(k8sClient.Create(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(requiredRegex, "Template"))) - }) - }) - - Context("When updating a CloudStackMachine", func() { - BeforeEach(func() { - Ω(k8sClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) - }) - - It("should reject VM offering updates to the CloudStackMachine", func() { - dummies.CSMachine1.Spec.Offering = infrav1.CloudStackResourceIdentifier{Name: "ArbitraryUpdateOffering"} - Ω(k8sClient.Update(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "offering"))) - }) - - It("should reject VM template updates to the CloudStackMachine", func() { - dummies.CSMachine1.Spec.Template = infrav1.CloudStackResourceIdentifier{Name: "ArbitraryUpdateTemplate"} - Ω(k8sClient.Update(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "template"))) - }) - - It("should reject VM disk offering updates to the CloudStackMachine", func() { - dummies.CSMachine1.Spec.DiskOffering.Name = "medium" - Ω(k8sClient.Update(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "diskOffering"))) - }) - - It("should reject updates to VM details of the CloudStackMachine", func() { - dummies.CSMachine1.Spec.Details = map[string]string{"memoryOvercommitRatio": "1.5"} - Ω(k8sClient.Update(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "details"))) - }) - - It("should reject updates to the list of affinty groups of the CloudStackMachine", func() { - dummies.CSMachine1.Spec.AffinityGroupIDs = []string{"28b907b8-75a7-4214-bd3d-6c61961fc2af"} - Ω(k8sClient.Update(ctx, dummies.CSMachine1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "AffinityGroupIDs"))) - }) - }) -}) diff --git a/api/v1beta2/cloudstackmachinestatechecker_conversion.go b/api/v1beta2/cloudstackmachinestatechecker_conversion.go new file mode 100644 index 00000000..182f45d9 --- /dev/null +++ b/api/v1beta2/cloudstackmachinestatechecker_conversion.go @@ -0,0 +1,32 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackMachineStateChecker) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackMachineStateChecker) + return Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(src, dst, nil) +} + +func (dst *CloudStackMachineStateChecker) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackMachineStateChecker) + return Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(src, dst, nil) +} diff --git a/api/v1beta2/cloudstackmachinestatechecker_types.go b/api/v1beta2/cloudstackmachinestatechecker_types.go index 085fc680..fd558eaf 100644 --- a/api/v1beta2/cloudstackmachinestatechecker_types.go +++ b/api/v1beta2/cloudstackmachinestatechecker_types.go @@ -32,7 +32,6 @@ type CloudStackMachineStateCheckerStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion // CloudStackMachineStateChecker is the Schema for the cloudstackmachinestatecheckers API type CloudStackMachineStateChecker struct { @@ -53,5 +52,5 @@ type CloudStackMachineStateCheckerList struct { } func init() { - SchemeBuilder.Register(&CloudStackMachineStateChecker{}, &CloudStackMachineStateCheckerList{}) + objectTypes = append(objectTypes, &CloudStackMachineStateChecker{}, &CloudStackMachineStateCheckerList{}) } diff --git a/api/v1beta2/cloudstackmachinetemplate_conversion.go b/api/v1beta2/cloudstackmachinetemplate_conversion.go new file mode 100644 index 00000000..721d8bd0 --- /dev/null +++ b/api/v1beta2/cloudstackmachinetemplate_conversion.go @@ -0,0 +1,79 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + machineryconversion "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +func (src *CloudStackMachineTemplate) ConvertTo(dstRaw conversion.Hub) error { // nolint + dst := dstRaw.(*v1beta3.CloudStackMachineTemplate) + if err := Convert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(src, dst, nil); err != nil { + return err + } + + // Manually restore data + restored := &v1beta3.CloudStackMachineTemplate{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + if restored.Spec.Template.Spec.FailureDomainName != "" { + dst.Spec.Template.Spec.FailureDomainName = restored.Spec.Template.Spec.FailureDomainName + } + if restored.Spec.Template.Spec.UncompressedUserData != nil { + dst.Spec.Template.Spec.UncompressedUserData = restored.Spec.Template.Spec.UncompressedUserData + } + return nil +} + +func (dst *CloudStackMachineTemplate) ConvertFrom(srcRaw conversion.Hub) error { // nolint + src := srcRaw.(*v1beta3.CloudStackMachineTemplate) + return Convert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(src, dst, nil) +} + +func Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(in *CloudStackMachineTemplateSpec, out *v1beta3.CloudStackMachineTemplateSpec, s machineryconversion.Scope) error { // nolint + return Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(&in.Spec, &out.Template, s) +} + +func Convert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(in *v1beta3.CloudStackMachineTemplateSpec, out *CloudStackMachineTemplateSpec, s machineryconversion.Scope) error { // nolint + return Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(&in.Template, &out.Spec, s) +} + +func Convert_v1beta1_ObjectMeta_To_v1_ObjectMeta(in *clusterv1.ObjectMeta, out *metav1.ObjectMeta, s machineryconversion.Scope) error { // nolint + if in.Annotations != nil { + out.Annotations = in.Annotations + } + if in.Labels != nil { + out.Labels = in.Labels + } + return nil +} + +func Convert_v1_ObjectMeta_To_v1beta1_ObjectMeta(in *metav1.ObjectMeta, out *clusterv1.ObjectMeta, s machineryconversion.Scope) error { // nolint + if in.Annotations != nil { + out.Annotations = in.Annotations + } + if in.Labels != nil { + out.Labels = in.Labels + } + return nil +} diff --git a/api/v1beta2/cloudstackmachinetemplate_types.go b/api/v1beta2/cloudstackmachinetemplate_types.go index 5c6ffff1..b8ae5621 100644 --- a/api/v1beta2/cloudstackmachinetemplate_types.go +++ b/api/v1beta2/cloudstackmachinetemplate_types.go @@ -34,7 +34,6 @@ type CloudStackMachineTemplateSpec struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion // CloudStackMachineTemplate is the Schema for the cloudstackmachinetemplates API type CloudStackMachineTemplate struct { @@ -54,5 +53,5 @@ type CloudStackMachineTemplateList struct { } func init() { - SchemeBuilder.Register(&CloudStackMachineTemplate{}, &CloudStackMachineTemplateList{}) + objectTypes = append(objectTypes, &CloudStackMachineTemplate{}, &CloudStackMachineTemplateList{}) } diff --git a/api/v1beta2/cloudstackmachinetemplate_webhook_test.go b/api/v1beta2/cloudstackmachinetemplate_webhook_test.go deleted file mode 100644 index d9bd80ff..00000000 --- a/api/v1beta2/cloudstackmachinetemplate_webhook_test.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright 2022 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1beta2_test - -import ( - "context" - - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("CloudStackMachineTemplate webhook", func() { - var ctx context.Context - forbiddenRegex := "admission webhook.*denied the request.*Forbidden\\: %s" - requiredRegex := "admission webhook.*denied the request.*Required value\\: %s" - - BeforeEach(func() { // Reset test vars to initial state. - dummies.SetDummyVars() - ctx = context.Background() - _ = k8sClient.Delete(ctx, dummies.CSMachineTemplate1) // Delete any remnants. - }) - - Context("When creating a CloudStackMachineTemplate", func() { - It("Should accept a CloudStackMachineTemplate with all attributes present", func() { - Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)).Should(Succeed()) - }) - - It("Should accept a CloudStackMachineTemplate when missing the VM Disk Offering attribute", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.DiskOffering = infrav1.CloudStackResourceDiskOffering{ - CloudStackResourceIdentifier: infrav1.CloudStackResourceIdentifier{Name: "", ID: ""}, - } - Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)).Should(Succeed()) - }) - - It("Should reject a CloudStackMachineTemplate when missing the VM Offering attribute", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.Offering = infrav1.CloudStackResourceIdentifier{Name: "", ID: ""} - Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)). - Should(MatchError(MatchRegexp(requiredRegex, "Offering"))) - }) - - It("Should reject a CloudStackMachineTemplate when missing the VM Template attribute", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.Template = infrav1.CloudStackResourceIdentifier{Name: "", ID: ""} - Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)). - Should(MatchError(MatchRegexp(requiredRegex, "Template"))) - }) - }) - - Context("When updating a CloudStackMachineTemplate", func() { - BeforeEach(func() { // Reset test vars to initial state. - Ω(k8sClient.Create(ctx, dummies.CSMachineTemplate1)).Should(Succeed()) - }) - - It("should reject VM template updates to the CloudStackMachineTemplate", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.Template = infrav1.CloudStackResourceIdentifier{Name: "ArbitraryUpdateTemplate"} - Ω(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "template"))) - }) - - It("should reject VM disk offering updates to the CloudStackMachineTemplate", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.DiskOffering = infrav1.CloudStackResourceDiskOffering{ - CloudStackResourceIdentifier: infrav1.CloudStackResourceIdentifier{Name: "DiskOffering2"}} - Ω(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "diskOffering"))) - }) - - It("should reject VM offering updates to the CloudStackMachineTemplate", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.Offering = infrav1.CloudStackResourceIdentifier{Name: "Offering2"} - Ω(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "offering"))) - }) - - It("should reject updates to VM details of the CloudStackMachineTemplate", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.Details = map[string]string{"memoryOvercommitRatio": "1.5"} - Ω(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). - Should(MatchError(MatchRegexp(forbiddenRegex, "details"))) - }) - - It("should reject updates to the list of AffinityGroupIDs of the CloudStackMachineTemplate", func() { - dummies.CSMachineTemplate1.Spec.Spec.Spec.AffinityGroupIDs = []string{"28b907b8-75a7-4214-bd3d-6c61961fc2ag"} - Ω(k8sClient.Update(ctx, dummies.CSMachineTemplate1)).ShouldNot(Succeed()) - }) - }) -}) diff --git a/api/v1beta2/conversion.go b/api/v1beta2/conversion.go index b0c655f9..7f810154 100644 --- a/api/v1beta2/conversion.go +++ b/api/v1beta2/conversion.go @@ -16,44 +16,41 @@ limitations under the License. package v1beta2 -// Hub marks CloudStackCluster as a conversion hub. -func (*CloudStackCluster) Hub() {} - -// Hub marks CloudStackClusterList as a conversion hub. -func (*CloudStackClusterList) Hub() {} - -// Hub marks CloudStackMachine as a conversion hub. -func (*CloudStackMachine) Hub() {} - -// Hub marks CloudStackMachineList as a conversion hub. -func (*CloudStackMachineList) Hub() {} - -// Hub marks CloudStackMachineTemplate as a conversion hub. -func (*CloudStackMachineTemplate) Hub() {} - -// Hub marks CloudStackMachineTemplateList as a conversion hub. -func (*CloudStackMachineTemplateList) Hub() {} - -// Hub marks CloudStackIsolatedNetwork as a conversion hub. -func (*CloudStackIsolatedNetwork) Hub() {} - -// Hub marks CloudStackIsolatedNetworkList as a conversion hub. -func (*CloudStackIsolatedNetworkList) Hub() {} - -// Hub marks CloudStackFailureDomain as a conversion hub. -func (*CloudStackFailureDomain) Hub() {} - -// Hub marks CloudStackFailureDomainList as a conversion hub. -func (*CloudStackFailureDomainList) Hub() {} - -// Hub marks CloudStackAffinityGroup as a conversion hub. -func (*CloudStackAffinityGroup) Hub() {} - -// Hub marks CloudStackAffinityGroupList as a conversion hub. -func (*CloudStackAffinityGroupList) Hub() {} - -// Hub marks CloudStackMachineStateChecker as a conversion hub. -func (*CloudStackMachineStateChecker) Hub() {} - -// Hub marks CloudStackMachineStateCheckerList as a conversion hub. -func (*CloudStackMachineStateCheckerList) Hub() {} +import ( + conv "k8s.io/apimachinery/pkg/conversion" + "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" +) + +// Convert_v1beta3_Network_To_v1beta2_Network converts from v1beta3.Network to v1beta2.Network +// +//nolint:golint,revive,stylecheck +func Convert_v1beta3_Network_To_v1beta2_Network(in *v1beta3.Network, out *Network, _ conv.Scope) error { + out.ID = in.ID + out.Type = in.Type + out.Name = in.Name + // Skip Gateway, Netmask, and VPC fields as they do not exist in v1beta2.Network + return nil +} + +// Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec converts from v1beta3.CloudStackIsolatedNetworkSpec to v1beta2.CloudStackIsolatedNetworkSpec +// +//nolint:golint,revive,stylecheck +func Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(in *v1beta3.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, _ conv.Scope) error { + out.Name = in.Name + out.ID = in.ID + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + out.FailureDomainName = in.FailureDomainName + // Skip Gateway, Netmask, and VPC fields as they do not exist in v1beta2.CloudStackIsolatedNetworkSpec + return nil +} + +// Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus handles manual conversion of CloudStackIsolatedNetworkStatus from v1beta3 to v1beta2 +// +//nolint:golint,revive,stylecheck +func Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(in *v1beta3.CloudStackIsolatedNetworkStatus, out *CloudStackIsolatedNetworkStatus, _ conv.Scope) error { + out.PublicIPID = in.PublicIPID + out.LBRuleID = in.LBRuleID + out.Ready = in.Ready + // RoutingMode field doesn't exist in v1beta2, so we ignore it during conversion + return nil +} diff --git a/api/v1beta2/doc.go b/api/v1beta2/doc.go index e7da4a9e..9e24cab0 100644 --- a/api/v1beta2/doc.go +++ b/api/v1beta2/doc.go @@ -17,4 +17,5 @@ limitations under the License. // Package v1beta2 contains API Schema definitions for the infrastructure v1beta2 API group // +kubebuilder:object:generate=true // +groupName=infrastructure.cluster.x-k8s.io +// +k8s:conversion-gen=sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3 package v1beta2 diff --git a/api/v1beta2/groupversion_info.go b/api/v1beta2/groupversion_info.go index 5d8d6c9a..146666fe 100644 --- a/api/v1beta2/groupversion_info.go +++ b/api/v1beta2/groupversion_info.go @@ -20,8 +20,9 @@ limitations under the License. package v1beta2 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" ) var ( @@ -29,8 +30,18 @@ var ( GroupVersion = schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta2"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + 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 = &schemeBuilder ) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, objectTypes...) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/api/v1beta2/zz_generated.conversion.go b/api/v1beta2/zz_generated.conversion.go new file mode 100644 index 00000000..0d32f9bc --- /dev/null +++ b/api/v1beta2/zz_generated.conversion.go @@ -0,0 +1,1312 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by conversion-gen. DO NOT EDIT. + +package v1beta2 + +import ( + unsafe "unsafe" + + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + v1beta3 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + v1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +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((*CloudStackAffinityGroup)(nil), (*v1beta3.CloudStackAffinityGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(a.(*CloudStackAffinityGroup), b.(*v1beta3.CloudStackAffinityGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroup)(nil), (*CloudStackAffinityGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(a.(*v1beta3.CloudStackAffinityGroup), b.(*CloudStackAffinityGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupList)(nil), (*v1beta3.CloudStackAffinityGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(a.(*CloudStackAffinityGroupList), b.(*v1beta3.CloudStackAffinityGroupList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroupList)(nil), (*CloudStackAffinityGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(a.(*v1beta3.CloudStackAffinityGroupList), b.(*CloudStackAffinityGroupList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupSpec)(nil), (*v1beta3.CloudStackAffinityGroupSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(a.(*CloudStackAffinityGroupSpec), b.(*v1beta3.CloudStackAffinityGroupSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroupSpec)(nil), (*CloudStackAffinityGroupSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(a.(*v1beta3.CloudStackAffinityGroupSpec), b.(*CloudStackAffinityGroupSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackAffinityGroupStatus)(nil), (*v1beta3.CloudStackAffinityGroupStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(a.(*CloudStackAffinityGroupStatus), b.(*v1beta3.CloudStackAffinityGroupStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackAffinityGroupStatus)(nil), (*CloudStackAffinityGroupStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(a.(*v1beta3.CloudStackAffinityGroupStatus), b.(*CloudStackAffinityGroupStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackCluster)(nil), (*v1beta3.CloudStackCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster(a.(*CloudStackCluster), b.(*v1beta3.CloudStackCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackCluster)(nil), (*CloudStackCluster)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster(a.(*v1beta3.CloudStackCluster), b.(*CloudStackCluster), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackClusterList)(nil), (*v1beta3.CloudStackClusterList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackClusterList_To_v1beta3_CloudStackClusterList(a.(*CloudStackClusterList), b.(*v1beta3.CloudStackClusterList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackClusterList)(nil), (*CloudStackClusterList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackClusterList_To_v1beta2_CloudStackClusterList(a.(*v1beta3.CloudStackClusterList), b.(*CloudStackClusterList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackClusterSpec)(nil), (*v1beta3.CloudStackClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackClusterSpec_To_v1beta3_CloudStackClusterSpec(a.(*CloudStackClusterSpec), b.(*v1beta3.CloudStackClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackClusterStatus)(nil), (*v1beta3.CloudStackClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackClusterStatus_To_v1beta3_CloudStackClusterStatus(a.(*CloudStackClusterStatus), b.(*v1beta3.CloudStackClusterStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackFailureDomain)(nil), (*v1beta3.CloudStackFailureDomain)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain(a.(*CloudStackFailureDomain), b.(*v1beta3.CloudStackFailureDomain), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackFailureDomain)(nil), (*CloudStackFailureDomain)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain(a.(*v1beta3.CloudStackFailureDomain), b.(*CloudStackFailureDomain), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackFailureDomainList)(nil), (*v1beta3.CloudStackFailureDomainList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackFailureDomainList_To_v1beta3_CloudStackFailureDomainList(a.(*CloudStackFailureDomainList), b.(*v1beta3.CloudStackFailureDomainList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackFailureDomainList)(nil), (*CloudStackFailureDomainList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackFailureDomainList_To_v1beta2_CloudStackFailureDomainList(a.(*v1beta3.CloudStackFailureDomainList), b.(*CloudStackFailureDomainList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackFailureDomainSpec)(nil), (*v1beta3.CloudStackFailureDomainSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec(a.(*CloudStackFailureDomainSpec), b.(*v1beta3.CloudStackFailureDomainSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackFailureDomainStatus)(nil), (*v1beta3.CloudStackFailureDomainStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackFailureDomainStatus_To_v1beta3_CloudStackFailureDomainStatus(a.(*CloudStackFailureDomainStatus), b.(*v1beta3.CloudStackFailureDomainStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackFailureDomainStatus)(nil), (*CloudStackFailureDomainStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackFailureDomainStatus_To_v1beta2_CloudStackFailureDomainStatus(a.(*v1beta3.CloudStackFailureDomainStatus), b.(*CloudStackFailureDomainStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetwork)(nil), (*v1beta3.CloudStackIsolatedNetwork)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(a.(*CloudStackIsolatedNetwork), b.(*v1beta3.CloudStackIsolatedNetwork), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackIsolatedNetwork)(nil), (*CloudStackIsolatedNetwork)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(a.(*v1beta3.CloudStackIsolatedNetwork), b.(*CloudStackIsolatedNetwork), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkList)(nil), (*v1beta3.CloudStackIsolatedNetworkList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(a.(*CloudStackIsolatedNetworkList), b.(*v1beta3.CloudStackIsolatedNetworkList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackIsolatedNetworkList)(nil), (*CloudStackIsolatedNetworkList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(a.(*v1beta3.CloudStackIsolatedNetworkList), b.(*CloudStackIsolatedNetworkList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkSpec)(nil), (*v1beta3.CloudStackIsolatedNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(a.(*CloudStackIsolatedNetworkSpec), b.(*v1beta3.CloudStackIsolatedNetworkSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackIsolatedNetworkStatus)(nil), (*v1beta3.CloudStackIsolatedNetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(a.(*CloudStackIsolatedNetworkStatus), b.(*v1beta3.CloudStackIsolatedNetworkStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachine)(nil), (*v1beta3.CloudStackMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(a.(*CloudStackMachine), b.(*v1beta3.CloudStackMachine), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachine)(nil), (*CloudStackMachine)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(a.(*v1beta3.CloudStackMachine), b.(*CloudStackMachine), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineList)(nil), (*v1beta3.CloudStackMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineList_To_v1beta3_CloudStackMachineList(a.(*CloudStackMachineList), b.(*v1beta3.CloudStackMachineList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineList)(nil), (*CloudStackMachineList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineList_To_v1beta2_CloudStackMachineList(a.(*v1beta3.CloudStackMachineList), b.(*CloudStackMachineList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineSpec)(nil), (*v1beta3.CloudStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(a.(*CloudStackMachineSpec), b.(*v1beta3.CloudStackMachineSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateChecker)(nil), (*v1beta3.CloudStackMachineStateChecker)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(a.(*CloudStackMachineStateChecker), b.(*v1beta3.CloudStackMachineStateChecker), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateChecker)(nil), (*CloudStackMachineStateChecker)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(a.(*v1beta3.CloudStackMachineStateChecker), b.(*CloudStackMachineStateChecker), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerList)(nil), (*v1beta3.CloudStackMachineStateCheckerList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(a.(*CloudStackMachineStateCheckerList), b.(*v1beta3.CloudStackMachineStateCheckerList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateCheckerList)(nil), (*CloudStackMachineStateCheckerList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(a.(*v1beta3.CloudStackMachineStateCheckerList), b.(*CloudStackMachineStateCheckerList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerSpec)(nil), (*v1beta3.CloudStackMachineStateCheckerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(a.(*CloudStackMachineStateCheckerSpec), b.(*v1beta3.CloudStackMachineStateCheckerSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateCheckerSpec)(nil), (*CloudStackMachineStateCheckerSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(a.(*v1beta3.CloudStackMachineStateCheckerSpec), b.(*CloudStackMachineStateCheckerSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStateCheckerStatus)(nil), (*v1beta3.CloudStackMachineStateCheckerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(a.(*CloudStackMachineStateCheckerStatus), b.(*v1beta3.CloudStackMachineStateCheckerStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStateCheckerStatus)(nil), (*CloudStackMachineStateCheckerStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(a.(*v1beta3.CloudStackMachineStateCheckerStatus), b.(*CloudStackMachineStateCheckerStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineStatus)(nil), (*v1beta3.CloudStackMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(a.(*CloudStackMachineStatus), b.(*v1beta3.CloudStackMachineStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineStatus)(nil), (*CloudStackMachineStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(a.(*v1beta3.CloudStackMachineStatus), b.(*CloudStackMachineStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplate)(nil), (*v1beta3.CloudStackMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(a.(*CloudStackMachineTemplate), b.(*v1beta3.CloudStackMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineTemplate)(nil), (*CloudStackMachineTemplate)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(a.(*v1beta3.CloudStackMachineTemplate), b.(*CloudStackMachineTemplate), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateList)(nil), (*v1beta3.CloudStackMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(a.(*CloudStackMachineTemplateList), b.(*v1beta3.CloudStackMachineTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineTemplateList)(nil), (*CloudStackMachineTemplateList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(a.(*v1beta3.CloudStackMachineTemplateList), b.(*CloudStackMachineTemplateList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackMachineTemplateResource)(nil), (*v1beta3.CloudStackMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(a.(*CloudStackMachineTemplateResource), b.(*v1beta3.CloudStackMachineTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackMachineTemplateResource)(nil), (*CloudStackMachineTemplateResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(a.(*v1beta3.CloudStackMachineTemplateResource), b.(*CloudStackMachineTemplateResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackResourceDiskOffering)(nil), (*v1beta3.CloudStackResourceDiskOffering)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(a.(*CloudStackResourceDiskOffering), b.(*v1beta3.CloudStackResourceDiskOffering), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackResourceDiskOffering)(nil), (*CloudStackResourceDiskOffering)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(a.(*v1beta3.CloudStackResourceDiskOffering), b.(*CloudStackResourceDiskOffering), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackResourceIdentifier)(nil), (*v1beta3.CloudStackResourceIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(a.(*CloudStackResourceIdentifier), b.(*v1beta3.CloudStackResourceIdentifier), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackResourceIdentifier)(nil), (*CloudStackResourceIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(a.(*v1beta3.CloudStackResourceIdentifier), b.(*CloudStackResourceIdentifier), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*CloudStackZoneSpec)(nil), (*v1beta3.CloudStackZoneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(a.(*CloudStackZoneSpec), b.(*v1beta3.CloudStackZoneSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1beta3.CloudStackZoneSpec)(nil), (*CloudStackZoneSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(a.(*v1beta3.CloudStackZoneSpec), b.(*CloudStackZoneSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*Network)(nil), (*v1beta3.Network)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Network_To_v1beta3_Network(a.(*Network), b.(*v1beta3.Network), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1.ObjectMeta)(nil), (*v1beta1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_ObjectMeta_To_v1beta1_ObjectMeta(a.(*v1.ObjectMeta), b.(*v1beta1.ObjectMeta), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta1.ObjectMeta)(nil), (*v1.ObjectMeta)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ObjectMeta_To_v1_ObjectMeta(a.(*v1beta1.ObjectMeta), b.(*v1.ObjectMeta), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*CloudStackMachineTemplateSpec)(nil), (*v1beta3.CloudStackMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(a.(*CloudStackMachineTemplateSpec), b.(*v1beta3.CloudStackMachineTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackClusterSpec)(nil), (*CloudStackClusterSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackClusterSpec_To_v1beta2_CloudStackClusterSpec(a.(*v1beta3.CloudStackClusterSpec), b.(*CloudStackClusterSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackClusterStatus)(nil), (*CloudStackClusterStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackClusterStatus_To_v1beta2_CloudStackClusterStatus(a.(*v1beta3.CloudStackClusterStatus), b.(*CloudStackClusterStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackFailureDomainSpec)(nil), (*CloudStackFailureDomainSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackFailureDomainSpec_To_v1beta2_CloudStackFailureDomainSpec(a.(*v1beta3.CloudStackFailureDomainSpec), b.(*CloudStackFailureDomainSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackIsolatedNetworkSpec)(nil), (*CloudStackIsolatedNetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(a.(*v1beta3.CloudStackIsolatedNetworkSpec), b.(*CloudStackIsolatedNetworkSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackIsolatedNetworkStatus)(nil), (*CloudStackIsolatedNetworkStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(a.(*v1beta3.CloudStackIsolatedNetworkStatus), b.(*CloudStackIsolatedNetworkStatus), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackMachineSpec)(nil), (*CloudStackMachineSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(a.(*v1beta3.CloudStackMachineSpec), b.(*CloudStackMachineSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.CloudStackMachineTemplateSpec)(nil), (*CloudStackMachineTemplateSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(a.(*v1beta3.CloudStackMachineTemplateSpec), b.(*CloudStackMachineTemplateSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1beta3.Network)(nil), (*Network)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_Network_To_v1beta2_Network(a.(*v1beta3.Network), b.(*Network), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta2_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(in *CloudStackAffinityGroup, out *v1beta3.CloudStackAffinityGroup, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup is an autogenerated conversion function. +func Convert_v1beta2_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(in *CloudStackAffinityGroup, out *v1beta3.CloudStackAffinityGroup, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackAffinityGroup_To_v1beta3_CloudStackAffinityGroup(in, out, s) +} + +func autoConvert_v1beta3_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(in *v1beta3.CloudStackAffinityGroup, out *CloudStackAffinityGroup, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(in *v1beta3.CloudStackAffinityGroup, out *CloudStackAffinityGroup, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroup_To_v1beta2_CloudStackAffinityGroup(in, out, s) +} + +func autoConvert_v1beta2_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(in *CloudStackAffinityGroupList, out *v1beta3.CloudStackAffinityGroupList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]v1beta3.CloudStackAffinityGroup)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta2_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(in *CloudStackAffinityGroupList, out *v1beta3.CloudStackAffinityGroupList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackAffinityGroupList_To_v1beta3_CloudStackAffinityGroupList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(in *v1beta3.CloudStackAffinityGroupList, out *CloudStackAffinityGroupList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]CloudStackAffinityGroup)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta3_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(in *v1beta3.CloudStackAffinityGroupList, out *CloudStackAffinityGroupList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroupList_To_v1beta2_CloudStackAffinityGroupList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(in *CloudStackAffinityGroupSpec, out *v1beta3.CloudStackAffinityGroupSpec, s conversion.Scope) error { + out.Type = in.Type + out.Name = in.Name + out.ID = in.ID + out.FailureDomainName = in.FailureDomainName + return nil +} + +// Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(in *CloudStackAffinityGroupSpec, out *v1beta3.CloudStackAffinityGroupSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackAffinityGroupSpec_To_v1beta3_CloudStackAffinityGroupSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(in *v1beta3.CloudStackAffinityGroupSpec, out *CloudStackAffinityGroupSpec, s conversion.Scope) error { + out.Type = in.Type + out.Name = in.Name + out.ID = in.ID + out.FailureDomainName = in.FailureDomainName + return nil +} + +// Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(in *v1beta3.CloudStackAffinityGroupSpec, out *CloudStackAffinityGroupSpec, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroupSpec_To_v1beta2_CloudStackAffinityGroupSpec(in, out, s) +} + +func autoConvert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(in *CloudStackAffinityGroupStatus, out *v1beta3.CloudStackAffinityGroupStatus, s conversion.Scope) error { + out.Ready = in.Ready + return nil +} + +// Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus is an autogenerated conversion function. +func Convert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(in *CloudStackAffinityGroupStatus, out *v1beta3.CloudStackAffinityGroupStatus, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackAffinityGroupStatus_To_v1beta3_CloudStackAffinityGroupStatus(in, out, s) +} + +func autoConvert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(in *v1beta3.CloudStackAffinityGroupStatus, out *CloudStackAffinityGroupStatus, s conversion.Scope) error { + out.Ready = in.Ready + return nil +} + +// Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus is an autogenerated conversion function. +func Convert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(in *v1beta3.CloudStackAffinityGroupStatus, out *CloudStackAffinityGroupStatus, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackAffinityGroupStatus_To_v1beta2_CloudStackAffinityGroupStatus(in, out, s) +} + +func autoConvert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster(in *CloudStackCluster, out *v1beta3.CloudStackCluster, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackClusterSpec_To_v1beta3_CloudStackClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackClusterStatus_To_v1beta3_CloudStackClusterStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster is an autogenerated conversion function. +func Convert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster(in *CloudStackCluster, out *v1beta3.CloudStackCluster, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster(in, out, s) +} + +func autoConvert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster(in *v1beta3.CloudStackCluster, out *CloudStackCluster, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackClusterSpec_To_v1beta2_CloudStackClusterSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackClusterStatus_To_v1beta2_CloudStackClusterStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster is an autogenerated conversion function. +func Convert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster(in *v1beta3.CloudStackCluster, out *CloudStackCluster, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster(in, out, s) +} + +func autoConvert_v1beta2_CloudStackClusterList_To_v1beta3_CloudStackClusterList(in *CloudStackClusterList, out *v1beta3.CloudStackClusterList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta3.CloudStackCluster, len(*in)) + for i := range *in { + if err := Convert_v1beta2_CloudStackCluster_To_v1beta3_CloudStackCluster(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_CloudStackClusterList_To_v1beta3_CloudStackClusterList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackClusterList_To_v1beta3_CloudStackClusterList(in *CloudStackClusterList, out *v1beta3.CloudStackClusterList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackClusterList_To_v1beta3_CloudStackClusterList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackClusterList_To_v1beta2_CloudStackClusterList(in *v1beta3.CloudStackClusterList, out *CloudStackClusterList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackCluster, len(*in)) + for i := range *in { + if err := Convert_v1beta3_CloudStackCluster_To_v1beta2_CloudStackCluster(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta3_CloudStackClusterList_To_v1beta2_CloudStackClusterList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackClusterList_To_v1beta2_CloudStackClusterList(in *v1beta3.CloudStackClusterList, out *CloudStackClusterList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackClusterList_To_v1beta2_CloudStackClusterList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackClusterSpec_To_v1beta3_CloudStackClusterSpec(in *CloudStackClusterSpec, out *v1beta3.CloudStackClusterSpec, s conversion.Scope) error { + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make([]v1beta3.CloudStackFailureDomainSpec, len(*in)) + for i := range *in { + if err := Convert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.FailureDomains = nil + } + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + return nil +} + +// Convert_v1beta2_CloudStackClusterSpec_To_v1beta3_CloudStackClusterSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackClusterSpec_To_v1beta3_CloudStackClusterSpec(in *CloudStackClusterSpec, out *v1beta3.CloudStackClusterSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackClusterSpec_To_v1beta3_CloudStackClusterSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackClusterSpec_To_v1beta2_CloudStackClusterSpec(in *v1beta3.CloudStackClusterSpec, out *CloudStackClusterSpec, s conversion.Scope) error { + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make([]CloudStackFailureDomainSpec, len(*in)) + for i := range *in { + if err := Convert_v1beta3_CloudStackFailureDomainSpec_To_v1beta2_CloudStackFailureDomainSpec(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.FailureDomains = nil + } + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + // WARNING: in.SyncWithACS requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_CloudStackClusterStatus_To_v1beta3_CloudStackClusterStatus(in *CloudStackClusterStatus, out *v1beta3.CloudStackClusterStatus, s conversion.Scope) error { + out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) + out.Ready = in.Ready + return nil +} + +// Convert_v1beta2_CloudStackClusterStatus_To_v1beta3_CloudStackClusterStatus is an autogenerated conversion function. +func Convert_v1beta2_CloudStackClusterStatus_To_v1beta3_CloudStackClusterStatus(in *CloudStackClusterStatus, out *v1beta3.CloudStackClusterStatus, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackClusterStatus_To_v1beta3_CloudStackClusterStatus(in, out, s) +} + +func autoConvert_v1beta3_CloudStackClusterStatus_To_v1beta2_CloudStackClusterStatus(in *v1beta3.CloudStackClusterStatus, out *CloudStackClusterStatus, s conversion.Scope) error { + out.FailureDomains = *(*v1beta1.FailureDomains)(unsafe.Pointer(&in.FailureDomains)) + // WARNING: in.CloudStackClusterID requires manual conversion: does not exist in peer-type + out.Ready = in.Ready + return nil +} + +func autoConvert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain(in *CloudStackFailureDomain, out *v1beta3.CloudStackFailureDomain, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackFailureDomainStatus_To_v1beta3_CloudStackFailureDomainStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain is an autogenerated conversion function. +func Convert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain(in *CloudStackFailureDomain, out *v1beta3.CloudStackFailureDomain, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain(in, out, s) +} + +func autoConvert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain(in *v1beta3.CloudStackFailureDomain, out *CloudStackFailureDomain, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackFailureDomainSpec_To_v1beta2_CloudStackFailureDomainSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackFailureDomainStatus_To_v1beta2_CloudStackFailureDomainStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain is an autogenerated conversion function. +func Convert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain(in *v1beta3.CloudStackFailureDomain, out *CloudStackFailureDomain, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain(in, out, s) +} + +func autoConvert_v1beta2_CloudStackFailureDomainList_To_v1beta3_CloudStackFailureDomainList(in *CloudStackFailureDomainList, out *v1beta3.CloudStackFailureDomainList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta3.CloudStackFailureDomain, len(*in)) + for i := range *in { + if err := Convert_v1beta2_CloudStackFailureDomain_To_v1beta3_CloudStackFailureDomain(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_CloudStackFailureDomainList_To_v1beta3_CloudStackFailureDomainList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackFailureDomainList_To_v1beta3_CloudStackFailureDomainList(in *CloudStackFailureDomainList, out *v1beta3.CloudStackFailureDomainList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackFailureDomainList_To_v1beta3_CloudStackFailureDomainList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackFailureDomainList_To_v1beta2_CloudStackFailureDomainList(in *v1beta3.CloudStackFailureDomainList, out *CloudStackFailureDomainList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackFailureDomain, len(*in)) + for i := range *in { + if err := Convert_v1beta3_CloudStackFailureDomain_To_v1beta2_CloudStackFailureDomain(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta3_CloudStackFailureDomainList_To_v1beta2_CloudStackFailureDomainList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackFailureDomainList_To_v1beta2_CloudStackFailureDomainList(in *v1beta3.CloudStackFailureDomainList, out *CloudStackFailureDomainList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackFailureDomainList_To_v1beta2_CloudStackFailureDomainList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec(in *CloudStackFailureDomainSpec, out *v1beta3.CloudStackFailureDomainSpec, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_v1beta2_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(&in.Zone, &out.Zone, s); err != nil { + return err + } + out.Account = in.Account + out.Domain = in.Domain + out.ACSEndpoint = in.ACSEndpoint + return nil +} + +// Convert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec(in *CloudStackFailureDomainSpec, out *v1beta3.CloudStackFailureDomainSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackFailureDomainSpec_To_v1beta3_CloudStackFailureDomainSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackFailureDomainSpec_To_v1beta2_CloudStackFailureDomainSpec(in *v1beta3.CloudStackFailureDomainSpec, out *CloudStackFailureDomainSpec, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_v1beta3_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(&in.Zone, &out.Zone, s); err != nil { + return err + } + out.Account = in.Account + out.Domain = in.Domain + // WARNING: in.Project requires manual conversion: does not exist in peer-type + out.ACSEndpoint = in.ACSEndpoint + return nil +} + +func autoConvert_v1beta2_CloudStackFailureDomainStatus_To_v1beta3_CloudStackFailureDomainStatus(in *CloudStackFailureDomainStatus, out *v1beta3.CloudStackFailureDomainStatus, s conversion.Scope) error { + out.Ready = in.Ready + return nil +} + +// Convert_v1beta2_CloudStackFailureDomainStatus_To_v1beta3_CloudStackFailureDomainStatus is an autogenerated conversion function. +func Convert_v1beta2_CloudStackFailureDomainStatus_To_v1beta3_CloudStackFailureDomainStatus(in *CloudStackFailureDomainStatus, out *v1beta3.CloudStackFailureDomainStatus, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackFailureDomainStatus_To_v1beta3_CloudStackFailureDomainStatus(in, out, s) +} + +func autoConvert_v1beta3_CloudStackFailureDomainStatus_To_v1beta2_CloudStackFailureDomainStatus(in *v1beta3.CloudStackFailureDomainStatus, out *CloudStackFailureDomainStatus, s conversion.Scope) error { + out.Ready = in.Ready + return nil +} + +// Convert_v1beta3_CloudStackFailureDomainStatus_To_v1beta2_CloudStackFailureDomainStatus is an autogenerated conversion function. +func Convert_v1beta3_CloudStackFailureDomainStatus_To_v1beta2_CloudStackFailureDomainStatus(in *v1beta3.CloudStackFailureDomainStatus, out *CloudStackFailureDomainStatus, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackFailureDomainStatus_To_v1beta2_CloudStackFailureDomainStatus(in, out, s) +} + +func autoConvert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(in *CloudStackIsolatedNetwork, out *v1beta3.CloudStackIsolatedNetwork, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork is an autogenerated conversion function. +func Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(in *CloudStackIsolatedNetwork, out *v1beta3.CloudStackIsolatedNetwork, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(in, out, s) +} + +func autoConvert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(in *v1beta3.CloudStackIsolatedNetwork, out *CloudStackIsolatedNetwork, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork is an autogenerated conversion function. +func Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(in *v1beta3.CloudStackIsolatedNetwork, out *CloudStackIsolatedNetwork, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(in, out, s) +} + +func autoConvert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(in *CloudStackIsolatedNetworkList, out *v1beta3.CloudStackIsolatedNetworkList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta3.CloudStackIsolatedNetwork, len(*in)) + for i := range *in { + if err := Convert_v1beta2_CloudStackIsolatedNetwork_To_v1beta3_CloudStackIsolatedNetwork(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(in *CloudStackIsolatedNetworkList, out *v1beta3.CloudStackIsolatedNetworkList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackIsolatedNetworkList_To_v1beta3_CloudStackIsolatedNetworkList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(in *v1beta3.CloudStackIsolatedNetworkList, out *CloudStackIsolatedNetworkList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackIsolatedNetwork, len(*in)) + for i := range *in { + if err := Convert_v1beta3_CloudStackIsolatedNetwork_To_v1beta2_CloudStackIsolatedNetwork(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(in *v1beta3.CloudStackIsolatedNetworkList, out *CloudStackIsolatedNetworkList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackIsolatedNetworkList_To_v1beta2_CloudStackIsolatedNetworkList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(in *CloudStackIsolatedNetworkSpec, out *v1beta3.CloudStackIsolatedNetworkSpec, s conversion.Scope) error { + out.Name = in.Name + out.ID = in.ID + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + out.FailureDomainName = in.FailureDomainName + return nil +} + +// Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(in *CloudStackIsolatedNetworkSpec, out *v1beta3.CloudStackIsolatedNetworkSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackIsolatedNetworkSpec_To_v1beta3_CloudStackIsolatedNetworkSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(in *v1beta3.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, s conversion.Scope) error { + out.Name = in.Name + out.ID = in.ID + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + out.FailureDomainName = in.FailureDomainName + // WARNING: in.Gateway requires manual conversion: does not exist in peer-type + // WARNING: in.Netmask requires manual conversion: does not exist in peer-type + // WARNING: in.Offering requires manual conversion: does not exist in peer-type + // WARNING: in.VPC requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(in *CloudStackIsolatedNetworkStatus, out *v1beta3.CloudStackIsolatedNetworkStatus, s conversion.Scope) error { + out.PublicIPID = in.PublicIPID + out.LBRuleID = in.LBRuleID + out.Ready = in.Ready + return nil +} + +// Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus is an autogenerated conversion function. +func Convert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(in *CloudStackIsolatedNetworkStatus, out *v1beta3.CloudStackIsolatedNetworkStatus, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackIsolatedNetworkStatus_To_v1beta3_CloudStackIsolatedNetworkStatus(in, out, s) +} + +func autoConvert_v1beta3_CloudStackIsolatedNetworkStatus_To_v1beta2_CloudStackIsolatedNetworkStatus(in *v1beta3.CloudStackIsolatedNetworkStatus, out *CloudStackIsolatedNetworkStatus, s conversion.Scope) error { + out.PublicIPID = in.PublicIPID + out.LBRuleID = in.LBRuleID + // WARNING: in.RoutingMode requires manual conversion: does not exist in peer-type + // WARNING: in.FirewallRulesOpened requires manual conversion: does not exist in peer-type + out.Ready = in.Ready + return nil +} + +func autoConvert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(in *CloudStackMachine, out *v1beta3.CloudStackMachine, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(in *CloudStackMachine, out *v1beta3.CloudStackMachine, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(in *v1beta3.CloudStackMachine, out *CloudStackMachine, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(in *v1beta3.CloudStackMachine, out *CloudStackMachine, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineList_To_v1beta3_CloudStackMachineList(in *CloudStackMachineList, out *v1beta3.CloudStackMachineList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta3.CloudStackMachine, len(*in)) + for i := range *in { + if err := Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_CloudStackMachineList_To_v1beta3_CloudStackMachineList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineList_To_v1beta3_CloudStackMachineList(in *CloudStackMachineList, out *v1beta3.CloudStackMachineList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineList_To_v1beta3_CloudStackMachineList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineList_To_v1beta2_CloudStackMachineList(in *v1beta3.CloudStackMachineList, out *CloudStackMachineList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackMachine, len(*in)) + for i := range *in { + if err := Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta3_CloudStackMachineList_To_v1beta2_CloudStackMachineList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineList_To_v1beta2_CloudStackMachineList(in *v1beta3.CloudStackMachineList, out *CloudStackMachineList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineList_To_v1beta2_CloudStackMachineList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(in *CloudStackMachineSpec, out *v1beta3.CloudStackMachineSpec, s conversion.Scope) error { + out.Name = in.Name + out.ID = in.ID + out.InstanceID = (*string)(unsafe.Pointer(in.InstanceID)) + if err := Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(&in.Offering, &out.Offering, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(&in.DiskOffering, &out.DiskOffering, s); err != nil { + return err + } + out.SSHKey = in.SSHKey + out.Details = *(*map[string]string)(unsafe.Pointer(&in.Details)) + out.AffinityGroupIDs = *(*[]string)(unsafe.Pointer(&in.AffinityGroupIDs)) + out.Affinity = in.Affinity + out.AffinityGroupRef = (*corev1.ObjectReference)(unsafe.Pointer(in.AffinityGroupRef)) + out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) + out.FailureDomainName = in.FailureDomainName + out.UncompressedUserData = (*bool)(unsafe.Pointer(in.UncompressedUserData)) + return nil +} + +// Convert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(in *CloudStackMachineSpec, out *v1beta3.CloudStackMachineSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in *v1beta3.CloudStackMachineSpec, out *CloudStackMachineSpec, s conversion.Scope) error { + out.Name = in.Name + out.ID = in.ID + out.InstanceID = (*string)(unsafe.Pointer(in.InstanceID)) + if err := Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(&in.Offering, &out.Offering, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(&in.DiskOffering, &out.DiskOffering, s); err != nil { + return err + } + // WARNING: in.Networks requires manual conversion: does not exist in peer-type + out.SSHKey = in.SSHKey + out.Details = *(*map[string]string)(unsafe.Pointer(&in.Details)) + out.AffinityGroupIDs = *(*[]string)(unsafe.Pointer(&in.AffinityGroupIDs)) + out.Affinity = in.Affinity + out.AffinityGroupRef = (*corev1.ObjectReference)(unsafe.Pointer(in.AffinityGroupRef)) + out.ProviderID = (*string)(unsafe.Pointer(in.ProviderID)) + out.FailureDomainName = in.FailureDomainName + out.UncompressedUserData = (*bool)(unsafe.Pointer(in.UncompressedUserData)) + return nil +} + +func autoConvert_v1beta2_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(in *CloudStackMachineStateChecker, out *v1beta3.CloudStackMachineStateChecker, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(in *CloudStackMachineStateChecker, out *v1beta3.CloudStackMachineStateChecker, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineStateChecker_To_v1beta3_CloudStackMachineStateChecker(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(in *v1beta3.CloudStackMachineStateChecker, out *CloudStackMachineStateChecker, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(in *v1beta3.CloudStackMachineStateChecker, out *CloudStackMachineStateChecker, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateChecker_To_v1beta2_CloudStackMachineStateChecker(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(in *CloudStackMachineStateCheckerList, out *v1beta3.CloudStackMachineStateCheckerList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]v1beta3.CloudStackMachineStateChecker)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(in *CloudStackMachineStateCheckerList, out *v1beta3.CloudStackMachineStateCheckerList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineStateCheckerList_To_v1beta3_CloudStackMachineStateCheckerList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(in *v1beta3.CloudStackMachineStateCheckerList, out *CloudStackMachineStateCheckerList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]CloudStackMachineStateChecker)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(in *v1beta3.CloudStackMachineStateCheckerList, out *CloudStackMachineStateCheckerList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateCheckerList_To_v1beta2_CloudStackMachineStateCheckerList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(in *CloudStackMachineStateCheckerSpec, out *v1beta3.CloudStackMachineStateCheckerSpec, s conversion.Scope) error { + out.InstanceID = in.InstanceID + return nil +} + +// Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(in *CloudStackMachineStateCheckerSpec, out *v1beta3.CloudStackMachineStateCheckerSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineStateCheckerSpec_To_v1beta3_CloudStackMachineStateCheckerSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(in *v1beta3.CloudStackMachineStateCheckerSpec, out *CloudStackMachineStateCheckerSpec, s conversion.Scope) error { + out.InstanceID = in.InstanceID + return nil +} + +// Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(in *v1beta3.CloudStackMachineStateCheckerSpec, out *CloudStackMachineStateCheckerSpec, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateCheckerSpec_To_v1beta2_CloudStackMachineStateCheckerSpec(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(in *CloudStackMachineStateCheckerStatus, out *v1beta3.CloudStackMachineStateCheckerStatus, s conversion.Scope) error { + out.Ready = in.Ready + return nil +} + +// Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(in *CloudStackMachineStateCheckerStatus, out *v1beta3.CloudStackMachineStateCheckerStatus, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineStateCheckerStatus_To_v1beta3_CloudStackMachineStateCheckerStatus(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(in *v1beta3.CloudStackMachineStateCheckerStatus, out *CloudStackMachineStateCheckerStatus, s conversion.Scope) error { + out.Ready = in.Ready + return nil +} + +// Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(in *v1beta3.CloudStackMachineStateCheckerStatus, out *CloudStackMachineStateCheckerStatus, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStateCheckerStatus_To_v1beta2_CloudStackMachineStateCheckerStatus(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(in *CloudStackMachineStatus, out *v1beta3.CloudStackMachineStatus, s conversion.Scope) error { + out.Addresses = *(*[]corev1.NodeAddress)(unsafe.Pointer(&in.Addresses)) + out.InstanceState = in.InstanceState + out.InstanceStateLastUpdated = in.InstanceStateLastUpdated + out.Ready = in.Ready + out.Status = (*string)(unsafe.Pointer(in.Status)) + out.Reason = (*string)(unsafe.Pointer(in.Reason)) + return nil +} + +// Convert_v1beta2_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(in *CloudStackMachineStatus, out *v1beta3.CloudStackMachineStatus, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineStatus_To_v1beta3_CloudStackMachineStatus(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(in *v1beta3.CloudStackMachineStatus, out *CloudStackMachineStatus, s conversion.Scope) error { + out.Addresses = *(*[]corev1.NodeAddress)(unsafe.Pointer(&in.Addresses)) + out.InstanceState = in.InstanceState + out.InstanceStateLastUpdated = in.InstanceStateLastUpdated + out.Ready = in.Ready + out.Status = (*string)(unsafe.Pointer(in.Status)) + out.Reason = (*string)(unsafe.Pointer(in.Reason)) + return nil +} + +// Convert_v1beta3_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(in *v1beta3.CloudStackMachineStatus, out *CloudStackMachineStatus, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineStatus_To_v1beta2_CloudStackMachineStatus(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(in *CloudStackMachineTemplate, out *v1beta3.CloudStackMachineTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(in *CloudStackMachineTemplate, out *v1beta3.CloudStackMachineTemplate, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(in *v1beta3.CloudStackMachineTemplate, out *CloudStackMachineTemplate, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(in *v1beta3.CloudStackMachineTemplate, out *CloudStackMachineTemplate, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(in *CloudStackMachineTemplateList, out *v1beta3.CloudStackMachineTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta3.CloudStackMachineTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta2_CloudStackMachineTemplate_To_v1beta3_CloudStackMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(in *CloudStackMachineTemplateList, out *v1beta3.CloudStackMachineTemplateList, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineTemplateList_To_v1beta3_CloudStackMachineTemplateList(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(in *v1beta3.CloudStackMachineTemplateList, out *CloudStackMachineTemplateList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackMachineTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta3_CloudStackMachineTemplate_To_v1beta2_CloudStackMachineTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta3_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(in *v1beta3.CloudStackMachineTemplateList, out *CloudStackMachineTemplateList, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineTemplateList_To_v1beta2_CloudStackMachineTemplateList(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(in *CloudStackMachineTemplateResource, out *v1beta3.CloudStackMachineTemplateResource, s conversion.Scope) error { + if err := Convert_v1_ObjectMeta_To_v1beta1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta2_CloudStackMachineSpec_To_v1beta3_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource is an autogenerated conversion function. +func Convert_v1beta2_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(in *CloudStackMachineTemplateResource, out *v1beta3.CloudStackMachineTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackMachineTemplateResource_To_v1beta3_CloudStackMachineTemplateResource(in, out, s) +} + +func autoConvert_v1beta3_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(in *v1beta3.CloudStackMachineTemplateResource, out *CloudStackMachineTemplateResource, s conversion.Scope) error { + if err := Convert_v1beta1_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource is an autogenerated conversion function. +func Convert_v1beta3_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(in *v1beta3.CloudStackMachineTemplateResource, out *CloudStackMachineTemplateResource, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackMachineTemplateResource_To_v1beta2_CloudStackMachineTemplateResource(in, out, s) +} + +func autoConvert_v1beta2_CloudStackMachineTemplateSpec_To_v1beta3_CloudStackMachineTemplateSpec(in *CloudStackMachineTemplateSpec, out *v1beta3.CloudStackMachineTemplateSpec, s conversion.Scope) error { + // WARNING: in.Spec requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta3_CloudStackMachineTemplateSpec_To_v1beta2_CloudStackMachineTemplateSpec(in *v1beta3.CloudStackMachineTemplateSpec, out *CloudStackMachineTemplateSpec, s conversion.Scope) error { + // WARNING: in.Template requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_v1beta2_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(in *CloudStackResourceDiskOffering, out *v1beta3.CloudStackResourceDiskOffering, s conversion.Scope) error { + if err := Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(&in.CloudStackResourceIdentifier, &out.CloudStackResourceIdentifier, s); err != nil { + return err + } + out.CustomSize = in.CustomSize + out.MountPath = in.MountPath + out.Device = in.Device + out.Filesystem = in.Filesystem + out.Label = in.Label + return nil +} + +// Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering is an autogenerated conversion function. +func Convert_v1beta2_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(in *CloudStackResourceDiskOffering, out *v1beta3.CloudStackResourceDiskOffering, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackResourceDiskOffering_To_v1beta3_CloudStackResourceDiskOffering(in, out, s) +} + +func autoConvert_v1beta3_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(in *v1beta3.CloudStackResourceDiskOffering, out *CloudStackResourceDiskOffering, s conversion.Scope) error { + if err := Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(&in.CloudStackResourceIdentifier, &out.CloudStackResourceIdentifier, s); err != nil { + return err + } + out.CustomSize = in.CustomSize + out.MountPath = in.MountPath + out.Device = in.Device + out.Filesystem = in.Filesystem + out.Label = in.Label + return nil +} + +// Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering is an autogenerated conversion function. +func Convert_v1beta3_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(in *v1beta3.CloudStackResourceDiskOffering, out *CloudStackResourceDiskOffering, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackResourceDiskOffering_To_v1beta2_CloudStackResourceDiskOffering(in, out, s) +} + +func autoConvert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(in *CloudStackResourceIdentifier, out *v1beta3.CloudStackResourceIdentifier, s conversion.Scope) error { + out.ID = in.ID + out.Name = in.Name + return nil +} + +// Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier is an autogenerated conversion function. +func Convert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(in *CloudStackResourceIdentifier, out *v1beta3.CloudStackResourceIdentifier, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackResourceIdentifier_To_v1beta3_CloudStackResourceIdentifier(in, out, s) +} + +func autoConvert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(in *v1beta3.CloudStackResourceIdentifier, out *CloudStackResourceIdentifier, s conversion.Scope) error { + out.ID = in.ID + out.Name = in.Name + return nil +} + +// Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier is an autogenerated conversion function. +func Convert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(in *v1beta3.CloudStackResourceIdentifier, out *CloudStackResourceIdentifier, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackResourceIdentifier_To_v1beta2_CloudStackResourceIdentifier(in, out, s) +} + +func autoConvert_v1beta2_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(in *CloudStackZoneSpec, out *v1beta3.CloudStackZoneSpec, s conversion.Scope) error { + out.Name = in.Name + out.ID = in.ID + if err := Convert_v1beta2_Network_To_v1beta3_Network(&in.Network, &out.Network, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec is an autogenerated conversion function. +func Convert_v1beta2_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(in *CloudStackZoneSpec, out *v1beta3.CloudStackZoneSpec, s conversion.Scope) error { + return autoConvert_v1beta2_CloudStackZoneSpec_To_v1beta3_CloudStackZoneSpec(in, out, s) +} + +func autoConvert_v1beta3_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(in *v1beta3.CloudStackZoneSpec, out *CloudStackZoneSpec, s conversion.Scope) error { + out.Name = in.Name + out.ID = in.ID + if err := Convert_v1beta3_Network_To_v1beta2_Network(&in.Network, &out.Network, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta3_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec is an autogenerated conversion function. +func Convert_v1beta3_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(in *v1beta3.CloudStackZoneSpec, out *CloudStackZoneSpec, s conversion.Scope) error { + return autoConvert_v1beta3_CloudStackZoneSpec_To_v1beta2_CloudStackZoneSpec(in, out, s) +} + +func autoConvert_v1beta2_Network_To_v1beta3_Network(in *Network, out *v1beta3.Network, s conversion.Scope) error { + out.ID = in.ID + out.Type = in.Type + out.Name = in.Name + return nil +} + +// Convert_v1beta2_Network_To_v1beta3_Network is an autogenerated conversion function. +func Convert_v1beta2_Network_To_v1beta3_Network(in *Network, out *v1beta3.Network, s conversion.Scope) error { + return autoConvert_v1beta2_Network_To_v1beta3_Network(in, out, s) +} + +func autoConvert_v1beta3_Network_To_v1beta2_Network(in *v1beta3.Network, out *Network, s conversion.Scope) error { + out.ID = in.ID + out.Type = in.Type + out.Name = in.Name + // WARNING: in.Gateway requires manual conversion: does not exist in peer-type + // WARNING: in.Netmask requires manual conversion: does not exist in peer-type + // WARNING: in.Offering requires manual conversion: does not exist in peer-type + // WARNING: in.VPC requires manual conversion: does not exist in peer-type + // WARNING: in.RoutingMode requires manual conversion: does not exist in peer-type + return nil +} diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index be635822..fe845d5d 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* Copyright 2022. diff --git a/api/v1beta3/cloudstackaffinitygroup_types.go b/api/v1beta3/cloudstackaffinitygroup_types.go new file mode 100644 index 00000000..568f3564 --- /dev/null +++ b/api/v1beta3/cloudstackaffinitygroup_types.go @@ -0,0 +1,73 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const AffinityGroupFinalizer = "affinitygroup.infrastructure.cluster.x-k8s.io" + +// CloudStackAffinityGroupSpec defines the desired state of CloudStackAffinityGroup +type CloudStackAffinityGroupSpec struct { + // Mutually exclusive parameter with AffinityGroupIDs. + // Can be "host affinity" or "host anti-affinity". Will create an affinity group per machine set. + Type string `json:"type,omitempty"` + + // Name. + Name string `json:"name,omitempty"` + + // ID. + //+optional + ID string `json:"id,omitempty"` + + // FailureDomainName -- the name of the FailureDomain the machine is placed in. + // +optional + FailureDomainName string `json:"failureDomainName,omitempty"` +} + +// CloudStackAffinityGroupStatus defines the observed state of CloudStackAffinityGroup +type CloudStackAffinityGroupStatus struct { + // Reflects the readiness of the CS Affinity Group. + Ready bool `json:"ready"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// CloudStackAffinityGroup is the Schema for the cloudstackaffinitygroups API +type CloudStackAffinityGroup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackAffinityGroupSpec `json:"spec,omitempty"` + Status CloudStackAffinityGroupStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackAffinityGroupList contains a list of CloudStackAffinityGroup +type CloudStackAffinityGroupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackAffinityGroup `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackAffinityGroup{}, &CloudStackAffinityGroupList{}) +} diff --git a/api/v1beta3/cloudstackcluster_types.go b/api/v1beta3/cloudstackcluster_types.go new file mode 100644 index 00000000..55cabc77 --- /dev/null +++ b/api/v1beta3/cloudstackcluster_types.go @@ -0,0 +1,84 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + ClusterFinalizer = "cloudstackcluster.infrastructure.cluster.x-k8s.io" +) + +var K8sClient client.Client + +// CloudStackClusterSpec defines the desired state of CloudStackCluster. +type CloudStackClusterSpec struct { + FailureDomains []CloudStackFailureDomainSpec `json:"failureDomains"` + + // The kubernetes control plane endpoint. + ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"` + + // SyncWithACS determines if an externalManaged CKS cluster should be created on ACS. + // +optional + SyncWithACS *bool `json:"syncWithACS,omitempty"` +} + +// The status of the CloudStackCluster object. +type CloudStackClusterStatus struct { + // CAPI recognizes failure domains as a method to spread machines. + // CAPC sets failure domains to indicate functioning CloudStackFailureDomains. + // +optional + FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"` + + // Id of CAPC managed kubernetes cluster created in CloudStack + // +optional + CloudStackClusterID string `json:"cloudStackClusterId"` + + // Reflects the readiness of the CS cluster. + Ready bool `json:"ready"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// CloudStackCluster is the Schema for the cloudstackclusters API +type CloudStackCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackClusterSpec `json:"spec,omitempty"` + + // The actual cluster state reported by CloudStack. + Status CloudStackClusterStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackClusterList contains a list of CloudStackCluster +type CloudStackClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackCluster `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackCluster{}, &CloudStackClusterList{}) +} diff --git a/api/v1beta2/cloudstackcluster_webhook.go b/api/v1beta3/cloudstackcluster_webhook.go similarity index 84% rename from api/v1beta2/cloudstackcluster_webhook.go rename to api/v1beta3/cloudstackcluster_webhook.go index fff72af0..2b645b52 100644 --- a/api/v1beta2/cloudstackcluster_webhook.go +++ b/api/v1beta3/cloudstackcluster_webhook.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta2 +package v1beta3 import ( "fmt" @@ -27,6 +27,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -38,7 +39,7 @@ func (r *CloudStackCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackcluster,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters,verbs=create;update,versions=v1beta2,name=mcloudstackcluster.kb.io,admissionReviewVersions=v1beta1 +//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackcluster,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters,verbs=create;update,versions=v1beta3,name=mcloudstackcluster.kb.io,admissionReviewVersions=v1;v1beta1 var _ webhook.Defaulter = &CloudStackCluster{} @@ -48,12 +49,12 @@ func (r *CloudStackCluster) Default() { // No defaulted values supported yet. } -// +kubebuilder:webhook:name=vcloudstackcluster.kb.io,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters,versions=v1beta2,verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackcluster,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1beta1 +// +kubebuilder:webhook:name=vcloudstackcluster.kb.io,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters,versions=v1beta3,verbs=create;update,path=/validate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackcluster,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1 var _ webhook.Validator = &CloudStackCluster{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackCluster) ValidateCreate() error { +func (r *CloudStackCluster) ValidateCreate() (admission.Warnings, error) { cloudstackclusterlog.V(1).Info("entered validate create webhook", "api resource name", r.Name) var errorList field.ErrorList @@ -80,18 +81,18 @@ func (r *CloudStackCluster) ValidateCreate() error { } } - return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) + return nil, webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackCluster) ValidateUpdate(old runtime.Object) error { +func (r *CloudStackCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { cloudstackclusterlog.V(1).Info("entered validate update webhook", "api resource name", r.Name) spec := r.Spec oldCluster, ok := old.(*CloudStackCluster) if !ok { - return errors.NewBadRequest(fmt.Sprintf("expected a CloudStackCluster but got a %T", old)) + return nil, errors.NewBadRequest(fmt.Sprintf("expected a CloudStackCluster but got a %T", old)) } oldSpec := oldCluster.Spec @@ -109,7 +110,7 @@ func (r *CloudStackCluster) ValidateUpdate(old runtime.Object) error { "controlplaneendpoint.port", errorList) } - return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) + return nil, webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) } // ValidateFailureDomainUpdates verifies that at least one failure domain has not been deleted, and @@ -150,8 +151,8 @@ func FailureDomainsEqual(fd1, fd2 CloudStackFailureDomainSpec) bool { } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackCluster) ValidateDelete() error { +func (r *CloudStackCluster) ValidateDelete() (admission.Warnings, error) { cloudstackclusterlog.V(1).Info("entered validate delete webhook", "api resource name", r.Name) // No deletion validations. Deletion webhook not enabled. - return nil + return nil, nil } diff --git a/api/v1beta3/cloudstackcluster_webhook_test.go b/api/v1beta3/cloudstackcluster_webhook_test.go new file mode 100644 index 00000000..3769133b --- /dev/null +++ b/api/v1beta3/cloudstackcluster_webhook_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3_test + +import ( + "context" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" +) + +var _ = ginkgo.Describe("CloudStackCluster webhooks", func() { + var ctx context.Context + forbiddenRegex := "admission webhook.*denied the request.*Forbidden\\: %s" + requiredRegex := "admission webhook.*denied the request.*Required value\\: %s" + + ginkgo.BeforeEach(func() { // Reset test vars to initial state. + ctx = context.Background() + dummies.SetDummyVars() // Reset cluster var. + _ = k8sClient.Delete(ctx, dummies.CSCluster) // Delete any remnants. + dummies.SetDummyVars() // Reset again since the k8s client can set this on delete. + }) + + ginkgo.Context("When creating a CloudStackCluster", func() { + ginkgo.It("Should accept a CloudStackCluster with all attributes present", func() { + gomega.Expect(k8sClient.Create(ctx, dummies.CSCluster)).Should(gomega.Succeed()) + }) + + ginkgo.It("Should reject a CloudStackCluster with missing Zones.Network attribute", func() { + dummies.CSCluster.Spec.FailureDomains = []infrav1.CloudStackFailureDomainSpec{{}} + dummies.CSCluster.Spec.FailureDomains[0].Zone.Name = "ZoneWNoNetwork" + gomega.Expect(k8sClient.Create(ctx, dummies.CSCluster)).Should( + gomega.MatchError(gomega.MatchRegexp(requiredRegex, "each Zone requires a Network specification"))) + }) + + ginkgo.It("Should reject a CloudStackCluster with missing Zone attribute", func() { + dummies.CSCluster.Spec.FailureDomains[0].Zone = infrav1.CloudStackZoneSpec{} + gomega.Expect(k8sClient.Create(ctx, dummies.CSCluster)).Should(gomega.MatchError(gomega.MatchRegexp(requiredRegex, + "each Zone requires a Network specification"))) + }) + }) + + ginkgo.Context("When updating a CloudStackCluster", func() { + ginkgo.BeforeEach(func() { + gomega.Expect(k8sClient.Create(ctx, dummies.CSCluster)).Should(gomega.Succeed()) + }) + + ginkgo.It("Should reject updates to CloudStackCluster FailureDomains", func() { + dummies.CSCluster.Spec.FailureDomains[0].Zone.Name = "SomeRandomUpdate" + gomega.Expect(k8sClient.Update(ctx, dummies.CSCluster)).Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "Cannot change FailureDomain"))) + }) + ginkgo.It("Should reject updates to Networks specified in CloudStackCluster Zones", func() { + dummies.CSCluster.Spec.FailureDomains[0].Zone.Network.Name = "ArbitraryUpdateNetworkName" + gomega.Expect(k8sClient.Update(ctx, dummies.CSCluster)).Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "Cannot change FailureDomain"))) + }) + ginkgo.It("Should reject updates to CloudStackCluster controlplaneendpoint.host", func() { + dummies.CSCluster.Spec.ControlPlaneEndpoint.Host = "1.1.1.1" + gomega.Expect(k8sClient.Update(ctx, dummies.CSCluster)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "controlplaneendpoint\\.host"))) + }) + + ginkgo.It("Should reject updates to CloudStackCluster controlplaneendpoint.port", func() { + dummies.CSCluster.Spec.ControlPlaneEndpoint.Port = int32(1234) + gomega.Expect(k8sClient.Update(ctx, dummies.CSCluster)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "controlplaneendpoint\\.port"))) + }) + }) +}) diff --git a/api/v1beta3/cloudstackfailuredomain_types.go b/api/v1beta3/cloudstackfailuredomain_types.go new file mode 100644 index 00000000..02a6cd00 --- /dev/null +++ b/api/v1beta3/cloudstackfailuredomain_types.go @@ -0,0 +1,170 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + "crypto/md5" // #nosec G501 -- weak cryptographic primitive doesn't matter here. Not security related. + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FailureDomainHashedMetaName returns an MD5 name generated from the FailureDomain and Cluster name. +// FailureDomains must have a unique name even when potentially sharing a namespace with other clusters. +// In the future we may remove the ability to run multiple clusters in a single namespace, but today +// this is a consequence of being upstream of EKS-A which does run multiple clusters in a single namepace. +func FailureDomainHashedMetaName(fdName, clusterName string) string { + return fmt.Sprintf("%x", md5.Sum([]byte(fdName+clusterName))) // #nosec G401 -- weak cryptographic primitive doesn't matter here. Not security related. +} + +const ( + FailureDomainFinalizer = "cloudstackfailuredomain.infrastructure.cluster.x-k8s.io" + FailureDomainLabelName = "cloudstackfailuredomain.infrastructure.cluster.x-k8s.io/name" +) + +const ( + NetworkTypeIsolated = "Isolated" + NetworkTypeShared = "Shared" +) + +type Network struct { + // Cloudstack Network ID the cluster is built in. + // +optional + ID string `json:"id,omitempty"` + + // Cloudstack Network Type the cluster is built in. + // + optional + Type string `json:"type,omitempty"` + + // Cloudstack Network Name the cluster is built in. + Name string `json:"name"` + + // Cloudstack Network Gateway the cluster is built in. + // +optional + Gateway string `json:"gateway,omitempty"` + + // Cloudstack Network Netmask the cluster is built in. + // +optional + Netmask string `json:"netmask,omitempty"` + + // Cloudstack Network Offering the cluster is built in. + // Default is "DefaultIsolatedNetworkOfferingWithSourceNatService" for + // isolated networks and "DefaultIsolatedNetworkOfferingForVpcNetworks" + // for VPC networks. + // +optional + Offering string `json:"offering,omitempty"` + + // Cloudstack VPC the network belongs to. + // +optional + VPC *VPC `json:"vpc,omitempty"` + + // Cloudstack Network's routing mode. + // Routing mode can be Dynamic, or Static. + // Empty value means the network mode is NATTED, not ROUTED. + // +optional + RoutingMode string `json:"routingMode,omitempty"` +} + +type VPC struct { + // Cloudstack VPC ID of the network. + // +optional + ID string `json:"id,omitempty"` + + // Cloudstack VPC Name of the network. + // +optional + Name string `json:"name"` + + // CIDR for the VPC. + // +optional + CIDR string `json:"cidr,omitempty"` + + // Cloudstack VPC Offering for the network. + // Default is "Default VPC offering" + // +optional + Offering string `json:"offering,omitempty"` +} + +// CloudStackZoneSpec specifies a Zone's details. +type CloudStackZoneSpec struct { + // Name. + //+optional + Name string `json:"name,omitempty"` + + // ID. + //+optional + ID string `json:"id,omitempty"` + + // The network within the Zone to use. + Network Network `json:"network"` +} + +// CloudStackFailureDomainSpec defines the desired state of CloudStackFailureDomain +type CloudStackFailureDomainSpec struct { + // The failure domain unique name. + Name string `json:"name"` + + // The ACS Zone for this failure domain. + Zone CloudStackZoneSpec `json:"zone"` + + // CloudStack account. + // +optional + Account string `json:"account,omitempty"` + + // CloudStack domain. + // +optional + Domain string `json:"domain,omitempty"` + + // CloudStack project. + // +optional + Project string `json:"project,omitempty"` + + // Apache CloudStack Endpoint secret reference. + ACSEndpoint corev1.SecretReference `json:"acsEndpoint"` +} + +// CloudStackFailureDomainStatus defines the observed state of CloudStackFailureDomain +type CloudStackFailureDomainStatus struct { + // Reflects the readiness of the CloudStack Failure Domain. + Ready bool `json:"ready"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// CloudStackFailureDomain is the Schema for the cloudstackfailuredomains API +type CloudStackFailureDomain struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackFailureDomainSpec `json:"spec"` + Status CloudStackFailureDomainStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackFailureDomainList contains a list of CloudStackFailureDomain +type CloudStackFailureDomainList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackFailureDomain `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackFailureDomain{}, &CloudStackFailureDomainList{}) +} diff --git a/api/v1beta3/cloudstackisolatednetwork_types.go b/api/v1beta3/cloudstackisolatednetwork_types.go new file mode 100644 index 00000000..9f37eb79 --- /dev/null +++ b/api/v1beta3/cloudstackisolatednetwork_types.go @@ -0,0 +1,120 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// The presence of a finalizer prevents CAPI from deleting the corresponding CAPI data. +const IsolatedNetworkFinalizer = "cloudstackisolatednetwork.infrastructure.cluster.x-k8s.io" + +// CloudStackIsolatedNetworkSpec defines the desired state of CloudStackIsolatedNetwork +type CloudStackIsolatedNetworkSpec struct { + // Name. + //+optional + Name string `json:"name,omitempty"` + + // ID. + //+optional + ID string `json:"id,omitempty"` + + // The kubernetes control plane endpoint. + ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"` + + // FailureDomainName -- the FailureDomain the network is placed in. + FailureDomainName string `json:"failureDomainName"` + + // Gateway for the network. + // +optional + Gateway string `json:"gateway,omitempty"` + + // Netmask for the network. + // +optional + Netmask string `json:"netmask,omitempty"` + + // Offering for the network. + // Default is "DefaultIsolatedNetworkOfferingWithSourceNatService" for + // isolated networks and "DefaultIsolatedNetworkOfferingForVpcNetworks" + // for VPC networks. + // +optional + Offering string `json:"offering,omitempty"` + + // VPC the network belongs to. + // +optional + VPC *VPC `json:"vpc,omitempty"` +} + +// CloudStackIsolatedNetworkStatus defines the observed state of CloudStackIsolatedNetwork +type CloudStackIsolatedNetworkStatus struct { + // The CS public IP ID to use for the k8s endpoint. + PublicIPID string `json:"publicIPID,omitempty"` + + // The ID of the lb rule used to assign VMs to the lb. + LBRuleID string `json:"loadBalancerRuleID,omitempty"` + + // Routing mode of the network. + // Routing mode can be Dynamic, or Static. + // Empty value means the network mode is NATTED, not ROUTED. + RoutingMode string `json:"routingMode,omitempty"` + + // Indicates whether the necessary firewall egress and routing rules for the isolated network have been applied successfully. + FirewallRulesOpened bool `json:"firewallRulesOpened,omitempty"` + + // Ready indicates the readiness of this provider resource. + Ready bool `json:"ready"` +} + +func (n *CloudStackIsolatedNetwork) Network() *Network { + return &Network{ + Name: n.Spec.Name, + Type: "IsolatedNetwork", + ID: n.Spec.ID, + Gateway: n.Spec.Gateway, + Netmask: n.Spec.Netmask, + VPC: n.Spec.VPC, + Offering: n.Spec.Offering, + RoutingMode: n.Status.RoutingMode, + } +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// CloudStackIsolatedNetwork is the Schema for the cloudstackisolatednetworks API +type CloudStackIsolatedNetwork struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackIsolatedNetworkSpec `json:"spec,omitempty"` + Status CloudStackIsolatedNetworkStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackIsolatedNetworkList contains a list of CloudStackIsolatedNetwork +type CloudStackIsolatedNetworkList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackIsolatedNetwork `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackIsolatedNetwork{}, &CloudStackIsolatedNetworkList{}) +} diff --git a/api/v1beta3/cloudstackmachine_types.go b/api/v1beta3/cloudstackmachine_types.go new file mode 100644 index 00000000..34946f26 --- /dev/null +++ b/api/v1beta3/cloudstackmachine_types.go @@ -0,0 +1,203 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// The presence of a finalizer prevents CAPI from deleting the corresponding CAPI data. +const MachineFinalizer = "cloudstackmachine.infrastructure.cluster.x-k8s.io" + +const ( + ProAffinity = "pro" + AntiAffinity = "anti" + NoAffinity = "no" +) + +type NetworkSpec struct { + // CloudStack Network Name (required to resolve ID) + Name string `json:"name"` + + // Optional IP in the network + IP string `json:"ip,omitempty"` + + // Optional Network ID (overrides Name if set) + ID string `json:"id,omitempty"` +} + +// CloudStackMachineSpec defines the desired state of CloudStackMachine +type CloudStackMachineSpec struct { + // Name. + //+optional + Name string `json:"name,omitempty"` + + // ID. + //+optional + ID string `json:"id,omitempty"` + + // Instance ID. Should only be useful to modify an existing instance. + InstanceID *string `json:"instanceID,omitempty"` + + // CloudStack compute offering. + Offering CloudStackResourceIdentifier `json:"offering"` + + // CloudStack template to use. + Template CloudStackResourceIdentifier `json:"template"` + + // CloudStack disk offering to use. + // +optional + DiskOffering CloudStackResourceDiskOffering `json:"diskOffering,omitempty"` + + // The list of networks (overrides zone.network) + // +optional + // In CloudStackMachineSpec + Networks []NetworkSpec `json:"networks,omitempty"` + + // CloudStack ssh key to use. + // +optional + SSHKey string `json:"sshKey"` + + // Optional details map for deployVirtualMachine + Details map[string]string `json:"details,omitempty"` + + // Optional affinitygroupids for deployVirtualMachine + // +optional + AffinityGroupIDs []string `json:"affinityGroupIDs,omitempty"` + + // Mutually exclusive parameter with AffinityGroupIDs. + // Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. + // +optional + Affinity string `json:"affinity,omitempty"` + + // Mutually exclusive parameter with AffinityGroupIDs. + // Is a reference to a CloudStack affinity group CRD. + // +optional + AffinityGroupRef *corev1.ObjectReference `json:"cloudstackAffinityRef,omitempty"` + + // The CS specific unique identifier. Of the form: fmt.Sprintf("cloudstack:///%s", CS Machine ID) + // +optional + ProviderID *string `json:"providerID,omitempty"` + + // FailureDomainName -- the name of the FailureDomain the machine is placed in. + // +optional + FailureDomainName string `json:"failureDomainName,omitempty"` + + // UncompressedUserData specifies whether the user data is gzip-compressed. + // cloud-init has built-in support for gzip-compressed user data, ignition does not + // + // +optional + UncompressedUserData *bool `json:"uncompressedUserData,omitempty"` +} + +func (c *CloudStackMachine) CompressUserdata() bool { + return c.Spec.UncompressedUserData == nil || !*c.Spec.UncompressedUserData +} + +type CloudStackResourceIdentifier struct { + // Cloudstack resource ID. + // +optional + ID string `json:"id,omitempty"` + + // Cloudstack resource Name + // +optional + Name string `json:"name,omitempty"` +} + +type CloudStackResourceDiskOffering struct { + CloudStackResourceIdentifier `json:",inline"` + // Desired disk size. Used if disk offering is customizable as indicated by the ACS field 'Custom Disk Size'. + // +optional + CustomSize int64 `json:"customSizeInGB"` + // mount point the data disk uses to mount. The actual partition, mkfs and mount are done by cloud-init generated by kubeadmConfig. + MountPath string `json:"mountPath"` + // device name of data disk, for example /dev/vdb + Device string `json:"device"` + // filesystem used by data disk, for example, ext4, xfs + Filesystem string `json:"filesystem"` + // label of data disk, used by mkfs as label parameter + Label string `json:"label"` +} + +// Type pulled mostly from the CloudStack API. +type CloudStackMachineStatus struct { + // Addresses contains a CloudStack VM instance's IP addresses. + Addresses []corev1.NodeAddress `json:"addresses,omitempty"` + + // InstanceState is the state of the CloudStack instance for this machine. + // +optional + InstanceState string `json:"instanceState,omitempty"` + + // InstanceStateLastUpdated is the time the instance state was last updated. + // +optional + InstanceStateLastUpdated metav1.Time `json:"instanceStateLastUpdated,omitempty"` + + // Ready indicates the readiness of the provider resource. + Ready bool `json:"ready"` + + // Status indicates the status of the provider resource. + // +optional + Status *string `json:"status,omitempty"` + + // Reason indicates the reason of status failure + // +optional + Reason *string `json:"reason,omitempty"` +} + +// TimeSinceLastStateChange returns the amount of time that's elapsed since the state was last updated. If the state +// hasn't ever been updated, it returns a negative value. +func (s *CloudStackMachineStatus) TimeSinceLastStateChange() time.Duration { + if s.InstanceStateLastUpdated.IsZero() { + return time.Duration(-1) + } + return time.Since(s.InstanceStateLastUpdated.Time) +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=cloudstackmachines,scope=Namespaced,categories=cluster-api,shortName=csm +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster to which this CloudStackMachine belongs" +// +kubebuilder:printcolumn:name="InstanceState",type="string",JSONPath=".status.instanceState",description="CloudStack instance state" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="Machine ready status" +// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="CloudStack instance ID" +// +kubebuilder:printcolumn:name="Machine",type="string",JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object which owns with this CloudStackMachine" + +// CloudStackMachine is the Schema for the cloudstackmachines API +type CloudStackMachine struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackMachineSpec `json:"spec,omitempty"` + Status CloudStackMachineStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackMachineList contains a list of CloudStackMachine +type CloudStackMachineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackMachine `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackMachine{}, &CloudStackMachineList{}) +} diff --git a/api/v1beta2/cloudstackmachine_webhook.go b/api/v1beta3/cloudstackmachine_webhook.go similarity index 83% rename from api/v1beta2/cloudstackmachine_webhook.go rename to api/v1beta3/cloudstackmachine_webhook.go index 6c2824ee..b9c0fdca 100644 --- a/api/v1beta2/cloudstackmachine_webhook.go +++ b/api/v1beta3/cloudstackmachine_webhook.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta2 +package v1beta3 import ( "fmt" @@ -27,6 +27,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -38,7 +39,7 @@ func (r *CloudStackMachine) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachine,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines,verbs=create;update,versions=v1beta2,name=mcloudstackmachine.kb.io,admissionReviewVersions=v1beta1 +//+kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachine,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines,verbs=create;update,versions=v1beta3,name=mcloudstackmachine.kb.io,admissionReviewVersions=v1;v1beta1 var _ webhook.Defaulter = &CloudStackMachine{} @@ -48,12 +49,12 @@ func (r *CloudStackMachine) Default() { // No defaulted values supported yet. } -//+kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachine,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines,verbs=create;update,versions=v1beta2,name=vcloudstackmachine.kb.io,admissionReviewVersions=v1beta1 +//+kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachine,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines,verbs=create;update,versions=v1beta3,name=vcloudstackmachine.kb.io,admissionReviewVersions=v1;v1beta1 var _ webhook.Validator = &CloudStackMachine{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackMachine) ValidateCreate() error { +func (r *CloudStackMachine) ValidateCreate() (admission.Warnings, error) { cloudstackmachinelog.V(1).Info("entered validate create webhook", "api resource name", r.Name) var errorList field.ErrorList @@ -64,18 +65,18 @@ func (r *CloudStackMachine) ValidateCreate() error { errorList = webhookutil.EnsureIntFieldsAreNotNegative(r.Spec.DiskOffering.CustomSize, "customSizeInGB", errorList) } - return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) + return nil, webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackMachine) ValidateUpdate(old runtime.Object) error { +func (r *CloudStackMachine) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { cloudstackmachinelog.V(1).Info("entered validate update webhook", "api resource name", r.Name) var errorList field.ErrorList oldMachine, ok := old.(*CloudStackMachine) if !ok { - return errors.NewBadRequest(fmt.Sprintf("expected a CloudStackMachine but got a %T", old)) + return nil, errors.NewBadRequest(fmt.Sprintf("expected a CloudStackMachine but got a %T", old)) } oldSpec := oldMachine.Spec @@ -98,12 +99,12 @@ func (r *CloudStackMachine) ValidateUpdate(old runtime.Object) error { errorList = append(errorList, field.Forbidden(field.NewPath("spec", "AffinityGroupIDs"), "AffinityGroupIDs")) } - return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) + return nil, webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackMachine) ValidateDelete() error { +func (r *CloudStackMachine) ValidateDelete() (admission.Warnings, error) { cloudstackmachinelog.V(1).Info("entered validate delete webhook", "api resource name", r.Name) // No deletion validations. Deletion webhook not enabled. - return nil + return nil, nil } diff --git a/api/v1beta3/cloudstackmachine_webhook_test.go b/api/v1beta3/cloudstackmachine_webhook_test.go new file mode 100644 index 00000000..2bd170e9 --- /dev/null +++ b/api/v1beta3/cloudstackmachine_webhook_test.go @@ -0,0 +1,110 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3_test + +import ( + "context" + + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" + + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe("CloudStackMachine webhook", func() { + var ctx context.Context + forbiddenRegex := "admission webhook.*denied the request.*Forbidden\\: %s" + requiredRegex := "admission webhook.*denied the request.*Required value\\: %s" + + ginkgo.BeforeEach(func() { // Reset test vars to initial state. + dummies.SetDummyVars() + ctx = context.Background() + _ = k8sClient.Delete(ctx, dummies.CSMachine1) // Clear out any remaining machines. Ignore errors. + }) + + ginkgo.Context("When creating a CloudStackMachine", func() { + ginkgo.It("should accept CloudStackMachine with all attributes", func() { + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(gomega.Succeed()) + }) + + ginkgo.It("should accept a CloudStackMachine with disk Offering attribute", func() { + dummies.CSMachine1.Spec.DiskOffering = dummies.DiskOffering + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(gomega.Succeed()) + }) + + ginkgo.It("should accept a CloudStackMachine with positive disk Offering size attribute", func() { + dummies.CSMachine1.Spec.DiskOffering = dummies.DiskOffering + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 1 + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(gomega.Succeed()) + }) + + ginkgo.It("should not accept a CloudStackMachine with negative disk Offering size attribute", func() { + dummies.CSMachine1.Spec.DiskOffering = dummies.DiskOffering + dummies.CSMachine1.Spec.DiskOffering.CustomSize = -1 + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachine1)).Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "customSizeInGB"))) + }) + + ginkgo.It("should reject a CloudStackMachine with missing Offering attribute", func() { + dummies.CSMachine1.Spec.Offering = infrav1.CloudStackResourceIdentifier{ID: "", Name: ""} + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegex, "Offering"))) + }) + + ginkgo.It("should reject a CloudStackMachine with missing Template attribute", func() { + dummies.CSMachine1.Spec.Template = infrav1.CloudStackResourceIdentifier{ID: "", Name: ""} + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegex, "Template"))) + }) + }) + + ginkgo.Context("When updating a CloudStackMachine", func() { + ginkgo.BeforeEach(func() { + gomega.Ω(k8sClient.Create(ctx, dummies.CSMachine1)).Should(gomega.Succeed()) + }) + + ginkgo.It("should reject VM offering updates to the CloudStackMachine", func() { + dummies.CSMachine1.Spec.Offering = infrav1.CloudStackResourceIdentifier{Name: "ArbitraryUpdateOffering"} + gomega.Ω(k8sClient.Update(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "offering"))) + }) + + ginkgo.It("should reject VM template updates to the CloudStackMachine", func() { + dummies.CSMachine1.Spec.Template = infrav1.CloudStackResourceIdentifier{Name: "ArbitraryUpdateTemplate"} + gomega.Ω(k8sClient.Update(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "template"))) + }) + + ginkgo.It("should reject VM disk offering updates to the CloudStackMachine", func() { + dummies.CSMachine1.Spec.DiskOffering.Name = "medium" + gomega.Ω(k8sClient.Update(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "diskOffering"))) + }) + + ginkgo.It("should reject updates to VM details of the CloudStackMachine", func() { + dummies.CSMachine1.Spec.Details = map[string]string{"memoryOvercommitRatio": "1.5"} + gomega.Ω(k8sClient.Update(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "details"))) + }) + + ginkgo.It("should reject updates to the list of affinty groups of the CloudStackMachine", func() { + dummies.CSMachine1.Spec.AffinityGroupIDs = []string{"28b907b8-75a7-4214-bd3d-6c61961fc2af"} + gomega.Ω(k8sClient.Update(ctx, dummies.CSMachine1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "AffinityGroupIDs"))) + }) + }) +}) diff --git a/api/v1beta3/cloudstackmachinestatechecker_types.go b/api/v1beta3/cloudstackmachinestatechecker_types.go new file mode 100644 index 00000000..2cbdb889 --- /dev/null +++ b/api/v1beta3/cloudstackmachinestatechecker_types.go @@ -0,0 +1,57 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// CloudStackMachineStateCheckerSpec +type CloudStackMachineStateCheckerSpec struct { + // CloudStack machine instance ID + InstanceID string `json:"instanceID,omitempty"` +} + +// CloudStackMachineStateCheckerStatus defines the observed state of CloudStackMachineStateChecker +type CloudStackMachineStateCheckerStatus struct { + // Reflects the readiness of the Machine State Checker. + Ready bool `json:"ready"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// CloudStackMachineStateChecker is the Schema for the cloudstackmachinestatecheckers API +type CloudStackMachineStateChecker struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackMachineStateCheckerSpec `json:"spec,omitempty"` + Status CloudStackMachineStateCheckerStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackMachineStateCheckerList contains a list of CloudStackMachineStateChecker +type CloudStackMachineStateCheckerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackMachineStateChecker `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackMachineStateChecker{}, &CloudStackMachineStateCheckerList{}) +} diff --git a/api/v1beta3/cloudstackmachinetemplate_types.go b/api/v1beta3/cloudstackmachinetemplate_types.go new file mode 100644 index 00000000..639e9eaf --- /dev/null +++ b/api/v1beta3/cloudstackmachinetemplate_types.go @@ -0,0 +1,63 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// CloudStackMachineTemplateResource defines the data needed to create a CloudstackMachine from a template +type CloudStackMachineTemplateResource struct { + // 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"` + + // Spec is the specification of a desired behavior of the machine + Spec CloudStackMachineSpec `json:"spec"` +} + +// CloudStackMachineTemplateSpec defines the desired state of CloudstackMachineTemplate +type CloudStackMachineTemplateSpec struct { + Template CloudStackMachineTemplateResource `json:"template"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:storageversion + +// CloudStackMachineTemplate is the Schema for the cloudstackmachinetemplates API +type CloudStackMachineTemplate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CloudStackMachineTemplateSpec `json:"spec,omitempty"` +} + +//+kubebuilder:object:root=true + +// CloudStackMachineTemplateList contains a list of CloudStackMachineTemplate +type CloudStackMachineTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CloudStackMachineTemplate `json:"items"` +} + +func init() { + objectTypes = append(objectTypes, &CloudStackMachineTemplate{}, &CloudStackMachineTemplateList{}) +} diff --git a/api/v1beta2/cloudstackmachinetemplate_webhook.go b/api/v1beta3/cloudstackmachinetemplate_webhook.go similarity index 82% rename from api/v1beta2/cloudstackmachinetemplate_webhook.go rename to api/v1beta3/cloudstackmachinetemplate_webhook.go index cc452c3f..553183ea 100644 --- a/api/v1beta2/cloudstackmachinetemplate_webhook.go +++ b/api/v1beta3/cloudstackmachinetemplate_webhook.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta2 +package v1beta3 import ( "fmt" @@ -28,6 +28,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) // log is for logging in this package. @@ -39,7 +40,8 @@ func (r *CloudStackMachineTemplate) SetupWebhookWithManager(mgr ctrl.Manager) er Complete() } -// +kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachinetemplate,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachinetemplates,verbs=create;update,versions=v1beta2,name=mcloudstackmachinetemplate.kb.io,admissionReviewVersions=v1beta1 +// +kubebuilder:webhook:path=/mutate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachinetemplate,mutating=true,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachinetemplates,verbs=create;update,versions=v1beta3,name=mcloudstackmachinetemplate.kb.io,admissionReviewVersions=v1;v1beta1 + var _ webhook.Defaulter = &CloudStackMachineTemplate{} // Default implements webhook.Defaulter so a webhook will be registered for the type @@ -48,17 +50,18 @@ func (r *CloudStackMachineTemplate) Default() { // No defaulted values supported yet. } -// +kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachinetemplate,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachinetemplates,verbs=create;update,versions=v1beta2,name=vcloudstackmachinetemplate.kb.io,admissionReviewVersions=v1beta1 +// +kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachinetemplate,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachinetemplates,verbs=create;update,versions=v1beta3,name=vcloudstackmachinetemplate.kb.io,admissionReviewVersions=v1;v1beta1 + var _ webhook.Validator = &CloudStackMachineTemplate{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackMachineTemplate) ValidateCreate() error { +func (r *CloudStackMachineTemplate) ValidateCreate() (admission.Warnings, error) { cloudstackmachinetemplatelog.V(1).Info("entered validate create webhook", "api resource name", r.Name) var errorList field.ErrorList - // CloudStackMachineTemplateSpec.CloudStackMachineTemplateResource.CloudStackMachineSpec - spec := r.Spec.Spec.Spec + // CloudStackMachineTemplateSpec.CloudStackMachineSpec + spec := r.Spec.Template.Spec affinity := strings.ToLower(spec.Affinity) if !(affinity == "" || affinity == "no" || affinity == "pro" || affinity == "anti") { @@ -73,21 +76,21 @@ func (r *CloudStackMachineTemplate) ValidateCreate() error { errorList = webhookutil.EnsureAtLeastOneFieldExists(spec.Offering.ID, spec.Offering.Name, "Offering", errorList) errorList = webhookutil.EnsureAtLeastOneFieldExists(spec.Template.ID, spec.Template.Name, "Template", errorList) - return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) + return nil, webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackMachineTemplate) ValidateUpdate(old runtime.Object) error { +func (r *CloudStackMachineTemplate) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { cloudstackmachinetemplatelog.V(1).Info("entered validate update webhook", "api resource name", r.Name) oldMachineTemplate, ok := old.(*CloudStackMachineTemplate) if !ok { - return errors.NewBadRequest(fmt.Sprintf("expected a CloudStackMachineTemplate but got a %T", old)) + return nil, errors.NewBadRequest(fmt.Sprintf("expected a CloudStackMachineTemplate but got a %T", old)) } // CloudStackMachineTemplateSpec.CloudStackMachineTemplateResource.CloudStackMachineSpec - spec := r.Spec.Spec.Spec - oldSpec := oldMachineTemplate.Spec.Spec.Spec + spec := r.Spec.Template.Spec + oldSpec := oldMachineTemplate.Spec.Template.Spec errorList := field.ErrorList(nil) errorList = webhookutil.EnsureEqualStrings(spec.Offering.ID, oldSpec.Offering.ID, "offering", errorList) @@ -104,12 +107,12 @@ func (r *CloudStackMachineTemplate) ValidateUpdate(old runtime.Object) error { errorList = append(errorList, field.Forbidden(field.NewPath("spec", "AffinityGroupIDs"), "AffinityGroupIDs")) } - return webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) + return nil, webhookutil.AggregateObjErrors(r.GroupVersionKind().GroupKind(), r.Name, errorList) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *CloudStackMachineTemplate) ValidateDelete() error { +func (r *CloudStackMachineTemplate) ValidateDelete() (admission.Warnings, error) { cloudstackmachinetemplatelog.V(1).Info("entered validate delete webhook", "api resource name", r.Name) // No deletion validations. Deletion webhook not enabled. - return nil + return nil, nil } diff --git a/api/v1beta3/cloudstackmachinetemplate_webhook_test.go b/api/v1beta3/cloudstackmachinetemplate_webhook_test.go new file mode 100644 index 00000000..e7207cac --- /dev/null +++ b/api/v1beta3/cloudstackmachinetemplate_webhook_test.go @@ -0,0 +1,99 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3_test + +import ( + "context" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" +) + +var _ = ginkgo.Describe("CloudStackMachineTemplate webhook", func() { + var ctx context.Context + forbiddenRegex := "admission webhook.*denied the request.*Forbidden\\: %s" + requiredRegex := "admission webhook.*denied the request.*Required value\\: %s" + + ginkgo.BeforeEach(func() { // Reset test vars to initial state. + dummies.SetDummyVars() + ctx = context.Background() + _ = k8sClient.Delete(ctx, dummies.CSMachineTemplate1) // Delete any remnants. + }) + + ginkgo.Context("When creating a CloudStackMachineTemplate", func() { + ginkgo.It("Should accept a CloudStackMachineTemplate with all attributes present", func() { + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)).Should(gomega.Succeed()) + }) + + ginkgo.It("Should accept a CloudStackMachineTemplate when missing the VM Disk Offering attribute", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.DiskOffering = infrav1.CloudStackResourceDiskOffering{ + CloudStackResourceIdentifier: infrav1.CloudStackResourceIdentifier{Name: "", ID: ""}, + } + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)).Should(gomega.Succeed()) + }) + + ginkgo.It("Should reject a CloudStackMachineTemplate when missing the VM Offering attribute", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.Offering = infrav1.CloudStackResourceIdentifier{Name: "", ID: ""} + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)). + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegex, "Offering"))) + }) + + ginkgo.It("Should reject a CloudStackMachineTemplate when missing the VM Template attribute", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.Template = infrav1.CloudStackResourceIdentifier{Name: "", ID: ""} + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)). + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegex, "Template"))) + }) + }) + + ginkgo.Context("When updating a CloudStackMachineTemplate", func() { + ginkgo.BeforeEach(func() { // Reset test vars to initial state. + gomega.Expect(k8sClient.Create(ctx, dummies.CSMachineTemplate1)).Should(gomega.Succeed()) + }) + + ginkgo.It("should reject VM template updates to the CloudStackMachineTemplate", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.Template = infrav1.CloudStackResourceIdentifier{Name: "ArbitraryUpdateTemplate"} + gomega.Expect(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "template"))) + }) + + ginkgo.It("should reject VM disk offering updates to the CloudStackMachineTemplate", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.DiskOffering = infrav1.CloudStackResourceDiskOffering{ + CloudStackResourceIdentifier: infrav1.CloudStackResourceIdentifier{Name: "DiskOffering2"}} + gomega.Expect(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "diskOffering"))) + }) + + ginkgo.It("should reject VM offering updates to the CloudStackMachineTemplate", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.Offering = infrav1.CloudStackResourceIdentifier{Name: "Offering2"} + gomega.Expect(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "offering"))) + }) + + ginkgo.It("should reject updates to VM details of the CloudStackMachineTemplate", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.Details = map[string]string{"memoryOvercommitRatio": "1.5"} + gomega.Expect(k8sClient.Update(ctx, dummies.CSMachineTemplate1)). + Should(gomega.MatchError(gomega.MatchRegexp(forbiddenRegex, "details"))) + }) + + ginkgo.It("should reject updates to the list of AffinityGroupIDs of the CloudStackMachineTemplate", func() { + dummies.CSMachineTemplate1.Spec.Template.Spec.AffinityGroupIDs = []string{"28b907b8-75a7-4214-bd3d-6c61961fc2ag"} + gomega.Expect(k8sClient.Update(ctx, dummies.CSMachineTemplate1)).ShouldNot(gomega.Succeed()) + }) + }) +}) diff --git a/api/v1beta3/conversion.go b/api/v1beta3/conversion.go new file mode 100644 index 00000000..307498eb --- /dev/null +++ b/api/v1beta3/conversion.go @@ -0,0 +1,59 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta3 + +// Hub marks CloudStackCluster as a conversion hub. +func (*CloudStackCluster) Hub() {} + +// Hub marks CloudStackClusterList as a conversion hub. +func (*CloudStackClusterList) Hub() {} + +// Hub marks CloudStackMachine as a conversion hub. +func (*CloudStackMachine) Hub() {} + +// Hub marks CloudStackMachineList as a conversion hub. +func (*CloudStackMachineList) Hub() {} + +// Hub marks CloudStackMachineTemplate as a conversion hub. +func (*CloudStackMachineTemplate) Hub() {} + +// Hub marks CloudStackMachineTemplateList as a conversion hub. +func (*CloudStackMachineTemplateList) Hub() {} + +// Hub marks CloudStackIsolatedNetwork as a conversion hub. +func (*CloudStackIsolatedNetwork) Hub() {} + +// Hub marks CloudStackIsolatedNetworkList as a conversion hub. +func (*CloudStackIsolatedNetworkList) Hub() {} + +// Hub marks CloudStackFailureDomain as a conversion hub. +func (*CloudStackFailureDomain) Hub() {} + +// Hub marks CloudStackFailureDomainList as a conversion hub. +func (*CloudStackFailureDomainList) Hub() {} + +// Hub marks CloudStackAffinityGroup as a conversion hub. +func (*CloudStackAffinityGroup) Hub() {} + +// Hub marks CloudStackAffinityGroupList as a conversion hub. +func (*CloudStackAffinityGroupList) Hub() {} + +// Hub marks CloudStackMachineStateChecker as a conversion hub. +func (*CloudStackMachineStateChecker) Hub() {} + +// Hub marks CloudStackMachineStateCheckerList as a conversion hub. +func (*CloudStackMachineStateCheckerList) Hub() {} diff --git a/api/v1beta3/doc.go b/api/v1beta3/doc.go new file mode 100644 index 00000000..9613646b --- /dev/null +++ b/api/v1beta3/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta3 contains API Schema definitions for the infrastructure v1beta3 API group +// +kubebuilder:object:generate=true +// +groupName=infrastructure.cluster.x-k8s.io +package v1beta3 diff --git a/api/v1beta3/groupversion_info.go b/api/v1beta3/groupversion_info.go new file mode 100644 index 00000000..3cfdeb6e --- /dev/null +++ b/api/v1beta3/groupversion_info.go @@ -0,0 +1,46 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta3 contains API Schema definitions for the infrastructure v1beta3 API group +// +kubebuilder:object:generate=true +// +groupName=infrastructure.cluster.x-k8s.io +package v1beta3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta3"} + + // 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 + + // So the auto-generated code can compile + objectTypes = []runtime.Object{} +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, objectTypes...) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} diff --git a/api/v1beta2/webhook_suite_test.go b/api/v1beta3/webhook_suite_test.go similarity index 61% rename from api/v1beta2/webhook_suite_test.go rename to api/v1beta3/webhook_suite_test.go index 0eff6d1d..baabf603 100644 --- a/api/v1beta2/webhook_suite_test.go +++ b/api/v1beta3/webhook_suite_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta2_test +package v1beta3_test import ( "context" @@ -27,16 +27,18 @@ import ( "k8s.io/client-go/rest" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" admissionv1beta1 "k8s.io/api/admission/v1beta1" //+kubebuilder:scaffold:imports "k8s.io/apimachinery/pkg/runtime" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to @@ -48,15 +50,15 @@ var ctx context.Context var cancel context.CancelFunc func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) + gomega.RegisterFailHandler(ginkgo.Fail) - RunSpecs(t, "Webhook Suite") + ginkgo.RunSpecs(t, "Webhook Suite") } -var _ = BeforeSuite(func() { +var _ = ginkgo.BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) - By("bootstrapping test environment") + ginkgo.By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("../../", "config", "crd", "bases")}, ErrorIfCRDPathMissing: false, @@ -69,68 +71,73 @@ var _ = BeforeSuite(func() { var err error done := make(chan interface{}) go func() { - defer GinkgoRecover() + defer ginkgo.GinkgoRecover() cfg, err = testEnv.Start() close(done) }() - Eventually(done).WithTimeout(time.Minute).Should(BeClosed()) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) + gomega.Eventually(done).WithTimeout(time.Minute).Should(gomega.BeClosed()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(cfg).NotTo(gomega.BeNil()) scheme := runtime.NewScheme() err = infrav1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) err = admissionv1beta1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) //+kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(k8sClient).NotTo(gomega.BeNil()) // start webhook server using Manager webhookInstallOptions := &testEnv.WebhookInstallOptions mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - LeaderElection: false, - MetricsBindAddress: "0", + Scheme: scheme, + WebhookServer: webhook.NewServer(webhook.Options{ + + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + LeaderElection: false, + Metrics: server.Options{ + BindAddress: "0", + }, }) - Expect(err).NotTo(HaveOccurred()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) - Ω((&infrav1.CloudStackCluster{}).SetupWebhookWithManager(mgr)).Should(Succeed()) - Ω((&infrav1.CloudStackMachine{}).SetupWebhookWithManager(mgr)).Should(Succeed()) - Ω((&infrav1.CloudStackMachineTemplate{}).SetupWebhookWithManager(mgr)).Should(Succeed()) + gomega.Ω((&infrav1.CloudStackCluster{}).SetupWebhookWithManager(mgr)).Should(gomega.Succeed()) + gomega.Ω((&infrav1.CloudStackMachine{}).SetupWebhookWithManager(mgr)).Should(gomega.Succeed()) + gomega.Ω((&infrav1.CloudStackMachineTemplate{}).SetupWebhookWithManager(mgr)).Should(gomega.Succeed()) //+kubebuilder:scaffold:webhook go func() { - defer GinkgoRecover() + defer ginkgo.GinkgoRecover() err = mgr.Start(ctrl.SetupSignalHandler()) - Expect(err).NotTo(HaveOccurred()) + gomega.Expect(err).NotTo(gomega.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 { + gomega.Eventually(func() error { conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) //nolint:gosec //Used for testing only if err != nil { return err } conn.Close() return nil - }).Should(Succeed()) + }).Should(gomega.Succeed()) }) -var _ = AfterSuite(func() { +var _ = ginkgo.AfterSuite(func() { cancel() - By("tearing down the test environment") + ginkgo.By("tearing down the test environment") err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) diff --git a/api/v1beta3/zz_generated.deepcopy.go b/api/v1beta3/zz_generated.deepcopy.go new file mode 100644 index 00000000..54c04c69 --- /dev/null +++ b/api/v1beta3/zz_generated.deepcopy.go @@ -0,0 +1,833 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta3 + +import ( + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackAffinityGroup) DeepCopyInto(out *CloudStackAffinityGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackAffinityGroup. +func (in *CloudStackAffinityGroup) DeepCopy() *CloudStackAffinityGroup { + if in == nil { + return nil + } + out := new(CloudStackAffinityGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackAffinityGroup) 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 *CloudStackAffinityGroupList) DeepCopyInto(out *CloudStackAffinityGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackAffinityGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackAffinityGroupList. +func (in *CloudStackAffinityGroupList) DeepCopy() *CloudStackAffinityGroupList { + if in == nil { + return nil + } + out := new(CloudStackAffinityGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackAffinityGroupList) 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 *CloudStackAffinityGroupSpec) DeepCopyInto(out *CloudStackAffinityGroupSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackAffinityGroupSpec. +func (in *CloudStackAffinityGroupSpec) DeepCopy() *CloudStackAffinityGroupSpec { + if in == nil { + return nil + } + out := new(CloudStackAffinityGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackAffinityGroupStatus) DeepCopyInto(out *CloudStackAffinityGroupStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackAffinityGroupStatus. +func (in *CloudStackAffinityGroupStatus) DeepCopy() *CloudStackAffinityGroupStatus { + if in == nil { + return nil + } + out := new(CloudStackAffinityGroupStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackCluster) DeepCopyInto(out *CloudStackCluster) { + *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 CloudStackCluster. +func (in *CloudStackCluster) DeepCopy() *CloudStackCluster { + if in == nil { + return nil + } + out := new(CloudStackCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackCluster) 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 *CloudStackClusterList) DeepCopyInto(out *CloudStackClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackClusterList. +func (in *CloudStackClusterList) DeepCopy() *CloudStackClusterList { + if in == nil { + return nil + } + out := new(CloudStackClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackClusterList) 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 *CloudStackClusterSpec) DeepCopyInto(out *CloudStackClusterSpec) { + *out = *in + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make([]CloudStackFailureDomainSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if in.SyncWithACS != nil { + in, out := &in.SyncWithACS, &out.SyncWithACS + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackClusterSpec. +func (in *CloudStackClusterSpec) DeepCopy() *CloudStackClusterSpec { + if in == nil { + return nil + } + out := new(CloudStackClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackClusterStatus) DeepCopyInto(out *CloudStackClusterStatus) { + *out = *in + if in.FailureDomains != nil { + in, out := &in.FailureDomains, &out.FailureDomains + *out = make(v1beta1.FailureDomains, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackClusterStatus. +func (in *CloudStackClusterStatus) DeepCopy() *CloudStackClusterStatus { + if in == nil { + return nil + } + out := new(CloudStackClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackFailureDomain) DeepCopyInto(out *CloudStackFailureDomain) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackFailureDomain. +func (in *CloudStackFailureDomain) DeepCopy() *CloudStackFailureDomain { + if in == nil { + return nil + } + out := new(CloudStackFailureDomain) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackFailureDomain) 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 *CloudStackFailureDomainList) DeepCopyInto(out *CloudStackFailureDomainList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackFailureDomain, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackFailureDomainList. +func (in *CloudStackFailureDomainList) DeepCopy() *CloudStackFailureDomainList { + if in == nil { + return nil + } + out := new(CloudStackFailureDomainList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackFailureDomainList) 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 *CloudStackFailureDomainSpec) DeepCopyInto(out *CloudStackFailureDomainSpec) { + *out = *in + in.Zone.DeepCopyInto(&out.Zone) + out.ACSEndpoint = in.ACSEndpoint +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackFailureDomainSpec. +func (in *CloudStackFailureDomainSpec) DeepCopy() *CloudStackFailureDomainSpec { + if in == nil { + return nil + } + out := new(CloudStackFailureDomainSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackFailureDomainStatus) DeepCopyInto(out *CloudStackFailureDomainStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackFailureDomainStatus. +func (in *CloudStackFailureDomainStatus) DeepCopy() *CloudStackFailureDomainStatus { + if in == nil { + return nil + } + out := new(CloudStackFailureDomainStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackIsolatedNetwork) DeepCopyInto(out *CloudStackIsolatedNetwork) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackIsolatedNetwork. +func (in *CloudStackIsolatedNetwork) DeepCopy() *CloudStackIsolatedNetwork { + if in == nil { + return nil + } + out := new(CloudStackIsolatedNetwork) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackIsolatedNetwork) 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 *CloudStackIsolatedNetworkList) DeepCopyInto(out *CloudStackIsolatedNetworkList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackIsolatedNetwork, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackIsolatedNetworkList. +func (in *CloudStackIsolatedNetworkList) DeepCopy() *CloudStackIsolatedNetworkList { + if in == nil { + return nil + } + out := new(CloudStackIsolatedNetworkList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackIsolatedNetworkList) 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 *CloudStackIsolatedNetworkSpec) DeepCopyInto(out *CloudStackIsolatedNetworkSpec) { + *out = *in + out.ControlPlaneEndpoint = in.ControlPlaneEndpoint + if in.VPC != nil { + in, out := &in.VPC, &out.VPC + *out = new(VPC) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackIsolatedNetworkSpec. +func (in *CloudStackIsolatedNetworkSpec) DeepCopy() *CloudStackIsolatedNetworkSpec { + if in == nil { + return nil + } + out := new(CloudStackIsolatedNetworkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackIsolatedNetworkStatus) DeepCopyInto(out *CloudStackIsolatedNetworkStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackIsolatedNetworkStatus. +func (in *CloudStackIsolatedNetworkStatus) DeepCopy() *CloudStackIsolatedNetworkStatus { + if in == nil { + return nil + } + out := new(CloudStackIsolatedNetworkStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackMachine) DeepCopyInto(out *CloudStackMachine) { + *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 CloudStackMachine. +func (in *CloudStackMachine) DeepCopy() *CloudStackMachine { + if in == nil { + return nil + } + out := new(CloudStackMachine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackMachine) 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 *CloudStackMachineList) DeepCopyInto(out *CloudStackMachineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackMachine, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineList. +func (in *CloudStackMachineList) DeepCopy() *CloudStackMachineList { + if in == nil { + return nil + } + out := new(CloudStackMachineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackMachineList) 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 *CloudStackMachineSpec) DeepCopyInto(out *CloudStackMachineSpec) { + *out = *in + if in.InstanceID != nil { + in, out := &in.InstanceID, &out.InstanceID + *out = new(string) + **out = **in + } + out.Offering = in.Offering + out.Template = in.Template + out.DiskOffering = in.DiskOffering + if in.Networks != nil { + in, out := &in.Networks, &out.Networks + *out = make([]NetworkSpec, len(*in)) + copy(*out, *in) + } + if in.Details != nil { + in, out := &in.Details, &out.Details + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.AffinityGroupIDs != nil { + in, out := &in.AffinityGroupIDs, &out.AffinityGroupIDs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AffinityGroupRef != nil { + in, out := &in.AffinityGroupRef, &out.AffinityGroupRef + *out = new(v1.ObjectReference) + **out = **in + } + if in.ProviderID != nil { + in, out := &in.ProviderID, &out.ProviderID + *out = new(string) + **out = **in + } + if in.UncompressedUserData != nil { + in, out := &in.UncompressedUserData, &out.UncompressedUserData + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineSpec. +func (in *CloudStackMachineSpec) DeepCopy() *CloudStackMachineSpec { + if in == nil { + return nil + } + out := new(CloudStackMachineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackMachineStateChecker) DeepCopyInto(out *CloudStackMachineStateChecker) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineStateChecker. +func (in *CloudStackMachineStateChecker) DeepCopy() *CloudStackMachineStateChecker { + if in == nil { + return nil + } + out := new(CloudStackMachineStateChecker) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackMachineStateChecker) 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 *CloudStackMachineStateCheckerList) DeepCopyInto(out *CloudStackMachineStateCheckerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackMachineStateChecker, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineStateCheckerList. +func (in *CloudStackMachineStateCheckerList) DeepCopy() *CloudStackMachineStateCheckerList { + if in == nil { + return nil + } + out := new(CloudStackMachineStateCheckerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackMachineStateCheckerList) 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 *CloudStackMachineStateCheckerSpec) DeepCopyInto(out *CloudStackMachineStateCheckerSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineStateCheckerSpec. +func (in *CloudStackMachineStateCheckerSpec) DeepCopy() *CloudStackMachineStateCheckerSpec { + if in == nil { + return nil + } + out := new(CloudStackMachineStateCheckerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackMachineStateCheckerStatus) DeepCopyInto(out *CloudStackMachineStateCheckerStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineStateCheckerStatus. +func (in *CloudStackMachineStateCheckerStatus) DeepCopy() *CloudStackMachineStateCheckerStatus { + if in == nil { + return nil + } + out := new(CloudStackMachineStateCheckerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackMachineStatus) DeepCopyInto(out *CloudStackMachineStatus) { + *out = *in + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]v1.NodeAddress, len(*in)) + copy(*out, *in) + } + in.InstanceStateLastUpdated.DeepCopyInto(&out.InstanceStateLastUpdated) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(string) + **out = **in + } + if in.Reason != nil { + in, out := &in.Reason, &out.Reason + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineStatus. +func (in *CloudStackMachineStatus) DeepCopy() *CloudStackMachineStatus { + if in == nil { + return nil + } + out := new(CloudStackMachineStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackMachineTemplate) DeepCopyInto(out *CloudStackMachineTemplate) { + *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 CloudStackMachineTemplate. +func (in *CloudStackMachineTemplate) DeepCopy() *CloudStackMachineTemplate { + if in == nil { + return nil + } + out := new(CloudStackMachineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackMachineTemplate) 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 *CloudStackMachineTemplateList) DeepCopyInto(out *CloudStackMachineTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CloudStackMachineTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineTemplateList. +func (in *CloudStackMachineTemplateList) DeepCopy() *CloudStackMachineTemplateList { + if in == nil { + return nil + } + out := new(CloudStackMachineTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CloudStackMachineTemplateList) 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 *CloudStackMachineTemplateResource) DeepCopyInto(out *CloudStackMachineTemplateResource) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineTemplateResource. +func (in *CloudStackMachineTemplateResource) DeepCopy() *CloudStackMachineTemplateResource { + if in == nil { + return nil + } + out := new(CloudStackMachineTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackMachineTemplateSpec) DeepCopyInto(out *CloudStackMachineTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackMachineTemplateSpec. +func (in *CloudStackMachineTemplateSpec) DeepCopy() *CloudStackMachineTemplateSpec { + if in == nil { + return nil + } + out := new(CloudStackMachineTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackResourceDiskOffering) DeepCopyInto(out *CloudStackResourceDiskOffering) { + *out = *in + out.CloudStackResourceIdentifier = in.CloudStackResourceIdentifier +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackResourceDiskOffering. +func (in *CloudStackResourceDiskOffering) DeepCopy() *CloudStackResourceDiskOffering { + if in == nil { + return nil + } + out := new(CloudStackResourceDiskOffering) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackResourceIdentifier) DeepCopyInto(out *CloudStackResourceIdentifier) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackResourceIdentifier. +func (in *CloudStackResourceIdentifier) DeepCopy() *CloudStackResourceIdentifier { + if in == nil { + return nil + } + out := new(CloudStackResourceIdentifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudStackZoneSpec) DeepCopyInto(out *CloudStackZoneSpec) { + *out = *in + in.Network.DeepCopyInto(&out.Network) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudStackZoneSpec. +func (in *CloudStackZoneSpec) DeepCopy() *CloudStackZoneSpec { + if in == nil { + return nil + } + out := new(CloudStackZoneSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Network) DeepCopyInto(out *Network) { + *out = *in + if in.VPC != nil { + in, out := &in.VPC, &out.VPC + *out = new(VPC) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Network. +func (in *Network) DeepCopy() *Network { + if in == nil { + return nil + } + out := new(Network) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkSpec. +func (in *NetworkSpec) DeepCopy() *NetworkSpec { + if in == nil { + return nil + } + out := new(NetworkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPC) DeepCopyInto(out *VPC) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPC. +func (in *VPC) DeepCopy() *VPC { + if in == nil { + return nil + } + out := new(VPC) + in.DeepCopyInto(out) + return out +} diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml index 95f333f3..769af4bf 100644 --- a/config/certmanager/kustomization.yaml +++ b/config/certmanager/kustomization.yaml @@ -1,5 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - certificate.yaml - configurations: - kustomizeconfig.yaml diff --git a/config/crd/bases/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml b/config/crd/bases/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml index cca2e027..ad9a00eb 100644 --- a/config/crd/bases/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml +++ b/config/crd/bases/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 + controller-gen.kubebuilder.io/version: v0.11.3 creationTimestamp: null name: cloudstackmachineowners.fakes.infrastructure.cluster.x-k8s.io spec: @@ -59,9 +59,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackaffinitygroups.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackaffinitygroups.yaml index d1b38f32..f67b8431 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackaffinitygroups.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackaffinitygroups.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackaffinitygroups.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -44,9 +48,9 @@ spec: description: Name. type: string type: - description: Mutually exclusive parameter with AffinityGroupIDs. Can - be "host affinity" or "host anti-affinity". Will create an affinity - group per machine set. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Can be "host affinity" or "host anti-affinity". Will create an affinity group per machine set. type: string type: object status: @@ -71,14 +75,19 @@ spec: 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' + 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' + 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 @@ -97,9 +106,67 @@ spec: description: Name. type: string type: - description: Mutually exclusive parameter with AffinityGroupIDs. Can - be "host affinity" or "host anti-affinity". Will create an affinity - group per machine set. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Can be "host affinity" or "host anti-affinity". Will create an affinity group per machine set. + type: string + type: object + status: + description: CloudStackAffinityGroupStatus defines the observed state + of CloudStackAffinityGroup + properties: + ready: + description: Reflects the readiness of the CS Affinity Group. + type: boolean + required: + - ready + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackAffinityGroup is the Schema for the cloudstackaffinitygroups + 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: CloudStackAffinityGroupSpec defines the desired state of + CloudStackAffinityGroup + properties: + failureDomainName: + description: FailureDomainName -- the name of the FailureDomain the + machine is placed in. + type: string + id: + description: ID. + type: string + name: + description: Name. + type: string + type: + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Can be "host affinity" or "host anti-affinity". Will create an affinity group per machine set. type: string type: object status: @@ -117,9 +184,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml index d69e6ccf..97b8df7b 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackclusters.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackclusters.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -21,14 +20,19 @@ spec: description: CloudStackCluster is the Schema for the cloudstackclusters 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' + 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' + 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 @@ -56,12 +60,14 @@ spec: description: CloudStack domain. type: string identityRef: - description: CloudStackIdentityReference is a reference to an infrastructure + description: |- + CloudStackIdentityReference is a reference to an infrastructure provider identity to be used to provision cluster resources. properties: kind: - description: Kind of the identity. Must be supported by the infrastructure - provider and may be either cluster or namespace-scoped. + description: |- + Kind of the identity. Must be supported by the infrastructure provider + and may be either cluster or namespace-scoped. minLength: 1 type: string name: @@ -112,23 +118,24 @@ spec: type: string failureDomains: additionalProperties: - description: FailureDomainSpec is the Schema for Cluster API failure - domains. It allows controllers to understand how many failure - domains a cluster can optionally span across. + description: |- + FailureDomainSpec 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 + 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 + description: controlPlane determines if this failure domain is suitable for use by control plane machines. type: boolean type: object - description: CAPI recognizes failure domains as a method to spread - machines. CAPC sets failure domains to indicate functioning Zones. + description: |- + CAPI recognizes failure domains as a method to spread machines. + CAPC sets failure domains to indicate functioning Zones. type: object loadBalancerRuleID: description: The ID of the lb rule used to assign VMs to the lb. @@ -186,14 +193,19 @@ spec: description: CloudStackCluster is the Schema for the cloudstackclusters 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' + 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' + 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 @@ -234,6 +246,7 @@ spec: secret name must be unique. type: string type: object + x-kubernetes-map-type: atomic domain: description: CloudStack domain. type: string @@ -285,23 +298,214 @@ spec: properties: failureDomains: additionalProperties: - description: FailureDomainSpec is the Schema for Cluster API failure - domains. It allows controllers to understand how many failure - domains a cluster can optionally span across. + description: |- + FailureDomainSpec 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 + 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 + description: controlPlane determines if this failure domain is suitable for use by control plane machines. type: boolean type: object - description: CAPI recognizes failure domains as a method to spread - machines. CAPC sets failure domains to indicate functioning CloudStackFailureDomains. + description: |- + CAPI recognizes failure domains as a method to spread machines. + CAPC sets failure domains to indicate functioning CloudStackFailureDomains. + type: object + ready: + description: Reflects the readiness of the CS cluster. + type: boolean + required: + - ready + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackCluster is the Schema for the cloudstackclusters 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: CloudStackClusterSpec defines the desired state of CloudStackCluster. + properties: + controlPlaneEndpoint: + description: The kubernetes control plane endpoint. + properties: + host: + description: The hostname on which the API server is serving. + type: string + port: + description: The port on which the API server is serving. + format: int32 + type: integer + required: + - host + - port + type: object + failureDomains: + items: + description: CloudStackFailureDomainSpec defines the desired state + of CloudStackFailureDomain + properties: + account: + description: CloudStack account. + type: string + acsEndpoint: + description: Apache CloudStack Endpoint secret reference. + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + domain: + description: CloudStack domain. + type: string + name: + description: The failure domain unique name. + type: string + project: + description: CloudStack project. + type: string + zone: + description: The ACS Zone for this failure domain. + properties: + id: + description: ID. + type: string + name: + description: Name. + type: string + network: + description: The network within the Zone to use. + properties: + gateway: + description: Cloudstack Network Gateway the cluster + is built in. + type: string + id: + description: Cloudstack Network ID the cluster is built + in. + type: string + name: + description: Cloudstack Network Name the cluster is + built in. + type: string + netmask: + description: Cloudstack Network Netmask the cluster + is built in. + type: string + offering: + description: |- + Cloudstack Network Offering the cluster is built in. + Default is "DefaultIsolatedNetworkOfferingWithSourceNatService" for + isolated networks and "DefaultIsolatedNetworkOfferingForVpcNetworks" + for VPC networks. + type: string + routingMode: + description: |- + Cloudstack Network's routing mode. + Routing mode can be Dynamic, or Static. + Empty value means the network mode is NATTED, not ROUTED. + type: string + type: + description: Cloudstack Network Type the cluster is + built in. + type: string + vpc: + description: Cloudstack VPC the network belongs to. + properties: + cidr: + description: CIDR for the VPC. + type: string + id: + description: Cloudstack VPC ID of the network. + type: string + name: + description: Cloudstack VPC Name of the network. + type: string + offering: + description: |- + Cloudstack VPC Offering for the network. + Default is "Default VPC offering" + type: string + type: object + required: + - name + type: object + required: + - network + type: object + required: + - acsEndpoint + - name + - zone + type: object + type: array + syncWithACS: + description: SyncWithACS determines if an externalManaged CKS cluster + should be created on ACS. + type: boolean + required: + - controlPlaneEndpoint + - failureDomains + type: object + status: + description: The actual cluster state reported by CloudStack. + properties: + cloudStackClusterId: + description: Id of CAPC managed kubernetes cluster created in CloudStack + type: string + failureDomains: + additionalProperties: + description: |- + FailureDomainSpec 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 + type: object + description: |- + CAPI recognizes failure domains as a method to spread machines. + CAPC sets failure domains to indicate functioning CloudStackFailureDomains. type: object ready: description: Reflects the readiness of the CS cluster. @@ -314,9 +518,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml index 1bb3f525..51b7123f 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackfailuredomains.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackfailuredomains.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -52,6 +56,7 @@ spec: name must be unique. type: string type: object + x-kubernetes-map-type: atomic domain: description: CloudStack domain. type: string @@ -106,12 +111,148 @@ spec: - spec type: object served: true + storage: false + subresources: + status: {} + - name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackFailureDomain is the Schema for the cloudstackfailuredomains + 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: CloudStackFailureDomainSpec defines the desired state of + CloudStackFailureDomain + properties: + account: + description: CloudStack account. + type: string + acsEndpoint: + description: Apache CloudStack Endpoint secret reference. + properties: + name: + description: name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: namespace defines the space within which the secret + name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + domain: + description: CloudStack domain. + type: string + name: + description: The failure domain unique name. + type: string + project: + description: CloudStack project. + type: string + zone: + description: The ACS Zone for this failure domain. + properties: + id: + description: ID. + type: string + name: + description: Name. + type: string + network: + description: The network within the Zone to use. + properties: + gateway: + description: Cloudstack Network Gateway the cluster is built + in. + type: string + id: + description: Cloudstack Network ID the cluster is built in. + type: string + name: + description: Cloudstack Network Name the cluster is built + in. + type: string + netmask: + description: Cloudstack Network Netmask the cluster is built + in. + type: string + offering: + description: |- + Cloudstack Network Offering the cluster is built in. + Default is "DefaultIsolatedNetworkOfferingWithSourceNatService" for + isolated networks and "DefaultIsolatedNetworkOfferingForVpcNetworks" + for VPC networks. + type: string + routingMode: + description: |- + Cloudstack Network's routing mode. + Routing mode can be Dynamic, or Static. + Empty value means the network mode is NATTED, not ROUTED. + type: string + type: + description: Cloudstack Network Type the cluster is built + in. + type: string + vpc: + description: Cloudstack VPC the network belongs to. + properties: + cidr: + description: CIDR for the VPC. + type: string + id: + description: Cloudstack VPC ID of the network. + type: string + name: + description: Cloudstack VPC Name of the network. + type: string + offering: + description: |- + Cloudstack VPC Offering for the network. + Default is "Default VPC offering" + type: string + type: object + required: + - name + type: object + required: + - network + type: object + required: + - acsEndpoint + - name + - zone + type: object + status: + description: CloudStackFailureDomainStatus defines the observed state + of CloudStackFailureDomain + properties: + ready: + description: Reflects the readiness of the CloudStack Failure Domain. + type: boolean + required: + - ready + type: object + required: + - spec + type: object + served: true storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackisolatednetworks.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackisolatednetworks.yaml index 3ab91de7..1dcfbc36 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackisolatednetworks.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackisolatednetworks.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackisolatednetworks.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -88,14 +92,19 @@ spec: 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' + 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' + 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 @@ -149,12 +158,122 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackIsolatedNetwork is the Schema for the cloudstackisolatednetworks + 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: CloudStackIsolatedNetworkSpec defines the desired state of + CloudStackIsolatedNetwork + properties: + controlPlaneEndpoint: + description: The kubernetes control plane endpoint. + properties: + host: + description: The hostname on which the API server is serving. + type: string + port: + description: The port on which the API server is serving. + format: int32 + type: integer + required: + - host + - port + type: object + failureDomainName: + description: FailureDomainName -- the FailureDomain the network is + placed in. + type: string + gateway: + description: Gateway for the network. + type: string + id: + description: ID. + type: string + name: + description: Name. + type: string + netmask: + description: Netmask for the network. + type: string + offering: + description: |- + Offering for the network. + Default is "DefaultIsolatedNetworkOfferingWithSourceNatService" for + isolated networks and "DefaultIsolatedNetworkOfferingForVpcNetworks" + for VPC networks. + type: string + vpc: + description: VPC the network belongs to. + properties: + cidr: + description: CIDR for the VPC. + type: string + id: + description: Cloudstack VPC ID of the network. + type: string + name: + description: Cloudstack VPC Name of the network. + type: string + offering: + description: |- + Cloudstack VPC Offering for the network. + Default is "Default VPC offering" + type: string + type: object + required: + - controlPlaneEndpoint + - failureDomainName + type: object + status: + description: CloudStackIsolatedNetworkStatus defines the observed state + of CloudStackIsolatedNetwork + properties: + firewallRulesOpened: + description: Firewall rules open status. + type: boolean + loadBalancerRuleID: + description: The ID of the lb rule used to assign VMs to the lb. + type: string + publicIPID: + description: The CS public IP ID to use for the k8s endpoint. + type: string + ready: + description: Ready indicates the readiness of this provider resource. + type: boolean + routingMode: + description: |- + Routing mode of the network. + Routing mode can be Dynamic, or Static. + Empty value means the network mode is NATTED, not ROUTED. + type: string + required: + - ready + type: object + type: object + served: true storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachines.yaml index 3a2907f4..3c957df4 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachines.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackmachines.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -46,14 +45,19 @@ spec: description: CloudStackMachine is the Schema for the cloudstackmachines 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' + 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' + 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 @@ -61,9 +65,9 @@ spec: description: CloudStackMachineSpec defines the desired state of CloudStackMachine properties: affinity: - description: Mutually exclusive parameter with AffinityGroupIDs. Defaults - to `no`. Can be `pro` or `anti`. Will create an affinity group per - machine set. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. type: string affinitygroupids: description: Optional affinitygroupids for deployVirtualMachine @@ -71,42 +75,50 @@ spec: type: string type: array cloudstackaffinityref: - description: Mutually exclusive parameter with AffinityGroupIDs. Is - a reference to a CloudStack affiniity group CRD. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Is a reference to a CloudStack affiniity group CRD. properties: apiVersion: description: API version of the referent. type: string fieldPath: - description: 'If referring to a piece of an object instead of - an entire object, this string should contain a valid JSON/Go - field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part of - an object. TODO: this design is not final and this field is - subject to change in the future.' + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. type: string kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ type: string resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency type: string uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids type: string type: object + x-kubernetes-map-type: atomic details: additionalProperties: type: string @@ -155,8 +167,9 @@ spec: when reconciling this cluster properties: kind: - description: Kind of the identity. Must be supported by the infrastructure - provider and may be either cluster or namespace-scoped. + description: |- + Kind of the identity. Must be supported by the infrastructure provider + and may be either cluster or namespace-scoped. minLength: 1 type: string name: @@ -281,14 +294,19 @@ spec: description: CloudStackMachine is the Schema for the cloudstackmachines 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' + 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' + 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 @@ -296,9 +314,9 @@ spec: description: CloudStackMachineSpec defines the desired state of CloudStackMachine properties: affinity: - description: Mutually exclusive parameter with AffinityGroupIDs. Defaults - to `no`. Can be `pro` or `anti`. Will create an affinity group per - machine set. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. type: string affinityGroupIDs: description: Optional affinitygroupids for deployVirtualMachine @@ -306,42 +324,50 @@ spec: type: string type: array cloudstackAffinityRef: - description: Mutually exclusive parameter with AffinityGroupIDs. Is - a reference to a CloudStack affinity group CRD. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Is a reference to a CloudStack affinity group CRD. properties: apiVersion: description: API version of the referent. type: string fieldPath: - description: 'If referring to a piece of an object instead of - an entire object, this string should contain a valid JSON/Go - field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part of - an object. TODO: this design is not final and this field is - subject to change in the future.' + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. type: string kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ type: string resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency type: string uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids type: string type: object + x-kubernetes-map-type: atomic details: additionalProperties: type: string @@ -424,9 +450,265 @@ spec: type: string type: object uncompressedUserData: - description: UncompressedUserData specifies whether the user data - is gzip-compressed. cloud-init has built-in support for gzip-compressed - user data, ignition does not + description: |- + UncompressedUserData specifies whether the user data is gzip-compressed. + cloud-init has built-in support for gzip-compressed user data, ignition does not + type: boolean + required: + - offering + - template + type: object + status: + description: Type pulled mostly from the CloudStack API. + properties: + addresses: + description: Addresses contains a CloudStack VM instance's IP addresses. + items: + description: NodeAddress contains information for the node's address. + properties: + address: + description: The node address. + type: string + type: + description: Node address type, one of Hostname, ExternalIP + or InternalIP. + type: string + required: + - address + - type + type: object + type: array + instanceState: + description: InstanceState is the state of the CloudStack instance + for this machine. + type: string + instanceStateLastUpdated: + description: InstanceStateLastUpdated is the time the instance state + was last updated. + format: date-time + type: string + ready: + description: Ready indicates the readiness of the provider resource. + type: boolean + reason: + description: Reason indicates the reason of status failure + type: string + status: + description: Status indicates the status of the provider resource. + type: string + required: + - ready + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: Cluster to which this CloudStackMachine belongs + jsonPath: .metadata.labels.cluster\.x-k8s\.io/cluster-name + name: Cluster + type: string + - description: CloudStack instance state + jsonPath: .status.instanceState + name: InstanceState + type: string + - description: Machine ready status + jsonPath: .status.ready + name: Ready + type: string + - description: CloudStack instance ID + jsonPath: .spec.providerID + name: ProviderID + type: string + - description: Machine object which owns with this CloudStackMachine + jsonPath: .metadata.ownerReferences[?(@.kind=="Machine")].name + name: Machine + type: string + name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackMachine is the Schema for the cloudstackmachines 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: CloudStackMachineSpec defines the desired state of CloudStackMachine + properties: + affinity: + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. + type: string + affinityGroupIDs: + description: Optional affinitygroupids for deployVirtualMachine + items: + type: string + type: array + cloudstackAffinityRef: + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Is a reference to a CloudStack affinity group CRD. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + details: + additionalProperties: + type: string + description: Optional details map for deployVirtualMachine + type: object + diskOffering: + description: CloudStack disk offering to use. + properties: + customSizeInGB: + description: Desired disk size. Used if disk offering is customizable + as indicated by the ACS field 'Custom Disk Size'. + format: int64 + type: integer + device: + description: device name of data disk, for example /dev/vdb + type: string + filesystem: + description: filesystem used by data disk, for example, ext4, + xfs + type: string + id: + description: Cloudstack resource ID. + type: string + label: + description: label of data disk, used by mkfs as label parameter + type: string + mountPath: + description: mount point the data disk uses to mount. The actual + partition, mkfs and mount are done by cloud-init generated by + kubeadmConfig. + type: string + name: + description: Cloudstack resource Name + type: string + required: + - device + - filesystem + - label + - mountPath + type: object + failureDomainName: + description: FailureDomainName -- the name of the FailureDomain the + machine is placed in. + type: string + id: + description: ID. + type: string + instanceID: + description: Instance ID. Should only be useful to modify an existing + instance. + type: string + name: + description: Name. + type: string + networks: + description: |- + The list of networks (overrides zone.network) + In CloudStackMachineSpec + items: + properties: + id: + description: Optional Network ID (overrides Name if set) + type: string + ip: + description: Optional IP in the network + type: string + name: + description: CloudStack Network Name (required to resolve ID) + type: string + required: + - name + type: object + type: array + offering: + description: CloudStack compute offering. + properties: + id: + description: Cloudstack resource ID. + type: string + name: + description: Cloudstack resource Name + type: string + type: object + providerID: + description: 'The CS specific unique identifier. Of the form: fmt.Sprintf("cloudstack:///%s", + CS Machine ID)' + type: string + sshKey: + description: CloudStack ssh key to use. + type: string + template: + description: CloudStack template to use. + properties: + id: + description: Cloudstack resource ID. + type: string + name: + description: Cloudstack resource Name + type: string + type: object + uncompressedUserData: + description: |- + UncompressedUserData specifies whether the user data is gzip-compressed. + cloud-init has built-in support for gzip-compressed user data, ignition does not type: boolean required: - offering @@ -478,9 +760,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinestatecheckers.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinestatecheckers.yaml index cac96b8d..d08c1f33 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinestatecheckers.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinestatecheckers.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackmachinestatecheckers.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -62,14 +66,64 @@ spec: 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' + 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' + 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: CloudStackMachineStateCheckerSpec + properties: + instanceID: + description: CloudStack machine instance ID + type: string + type: object + status: + description: CloudStackMachineStateCheckerStatus defines the observed + state of CloudStackMachineStateChecker + properties: + ready: + description: Reflects the readiness of the Machine State Checker. + type: boolean + required: + - ready + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackMachineStateChecker is the Schema for the cloudstackmachinestatecheckers + 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 @@ -95,9 +149,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinetemplates.yaml index cd022e67..5e23cd36 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackmachinetemplates.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackmachinetemplates.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -40,16 +44,18 @@ spec: template: properties: metadata: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + description: |- + Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata type: object spec: description: CloudStackMachineSpec defines the desired state of CloudStackMachine properties: affinity: - description: Mutually exclusive parameter with AffinityGroupIDs. - Defaults to `no`. Can be `pro` or `anti`. Will create an - affinity group per machine set. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. type: string affinitygroupids: description: Optional affinitygroupids for deployVirtualMachine @@ -57,43 +63,50 @@ spec: type: string type: array cloudstackaffinityref: - description: Mutually exclusive parameter with AffinityGroupIDs. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. Is a reference to a CloudStack affiniity group CRD. properties: apiVersion: description: API version of the referent. type: string fieldPath: - description: 'If referring to a piece of an object instead - of an entire object, this string should contain a valid - JSON/Go field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container - within a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that - triggered the event) or if no container name is specified - "spec.containers[2]" (container with index 2 in this - pod). This syntax is chosen only to have some well-defined - way of referencing a part of an object. TODO: this design - is not final and this field is subject to change in - the future.' + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. type: string kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ type: string resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency type: string uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids type: string type: object + x-kubernetes-map-type: atomic details: additionalProperties: type: string @@ -144,9 +157,9 @@ spec: be used when reconciling this cluster properties: kind: - description: Kind of the identity. Must be supported by - the infrastructure provider and may be either cluster - or namespace-scoped. + description: |- + Kind of the identity. Must be supported by the infrastructure provider + and may be either cluster or namespace-scoped. minLength: 1 type: string name: @@ -221,14 +234,19 @@ spec: 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' + 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' + 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 @@ -239,7 +257,9 @@ spec: template: properties: metadata: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + description: |- + Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata nullable: true type: object spec: @@ -247,9 +267,9 @@ spec: CloudStackMachine properties: affinity: - description: Mutually exclusive parameter with AffinityGroupIDs. - Defaults to `no`. Can be `pro` or `anti`. Will create an - affinity group per machine set. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. type: string affinityGroupIDs: description: Optional affinitygroupids for deployVirtualMachine @@ -257,43 +277,50 @@ spec: type: string type: array cloudstackAffinityRef: - description: Mutually exclusive parameter with AffinityGroupIDs. + description: |- + Mutually exclusive parameter with AffinityGroupIDs. Is a reference to a CloudStack affinity group CRD. properties: apiVersion: description: API version of the referent. type: string fieldPath: - description: 'If referring to a piece of an object instead - of an entire object, this string should contain a valid - JSON/Go field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container - within a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that - triggered the event) or if no container name is specified - "spec.containers[2]" (container with index 2 in this - pod). This syntax is chosen only to have some well-defined - way of referencing a part of an object. TODO: this design - is not final and this field is subject to change in - the future.' + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. type: string kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ type: string resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency type: string uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids type: string type: object + x-kubernetes-map-type: atomic details: additionalProperties: type: string @@ -378,9 +405,247 @@ spec: type: string type: object uncompressedUserData: - description: UncompressedUserData specifies whether the user - data is gzip-compressed. cloud-init has built-in support - for gzip-compressed user data, ignition does not + description: |- + UncompressedUserData specifies whether the user data is gzip-compressed. + cloud-init has built-in support for gzip-compressed user data, ignition does not + type: boolean + required: + - offering + - template + type: object + required: + - spec + type: object + required: + - template + type: object + type: object + served: true + storage: false + subresources: + status: {} + - name: v1beta3 + schema: + openAPIV3Schema: + description: CloudStackMachineTemplate is the Schema for the cloudstackmachinetemplates + 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: CloudStackMachineTemplateSpec defines the desired state of + CloudstackMachineTemplate + properties: + template: + description: CloudStackMachineTemplateResource defines the data needed + to create a CloudstackMachine from a template + properties: + metadata: + description: |- + Standard object's metadata. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + 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: |- + 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 is the specification of a desired behavior of + the machine + properties: + affinity: + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Defaults to `no`. Can be `pro` or `anti`. Will create an affinity group per machine set. + type: string + affinityGroupIDs: + description: Optional affinitygroupids for deployVirtualMachine + items: + type: string + type: array + cloudstackAffinityRef: + description: |- + Mutually exclusive parameter with AffinityGroupIDs. + Is a reference to a CloudStack affinity group CRD. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + details: + additionalProperties: + type: string + description: Optional details map for deployVirtualMachine + type: object + diskOffering: + description: CloudStack disk offering to use. + properties: + customSizeInGB: + description: Desired disk size. Used if disk offering + is customizable as indicated by the ACS field 'Custom + Disk Size'. + format: int64 + type: integer + device: + description: device name of data disk, for example /dev/vdb + type: string + filesystem: + description: filesystem used by data disk, for example, + ext4, xfs + type: string + id: + description: Cloudstack resource ID. + type: string + label: + description: label of data disk, used by mkfs as label + parameter + type: string + mountPath: + description: mount point the data disk uses to mount. + The actual partition, mkfs and mount are done by cloud-init + generated by kubeadmConfig. + type: string + name: + description: Cloudstack resource Name + type: string + required: + - device + - filesystem + - label + - mountPath + type: object + failureDomainName: + description: FailureDomainName -- the name of the FailureDomain + the machine is placed in. + type: string + id: + description: ID. + type: string + instanceID: + description: Instance ID. Should only be useful to modify + an existing instance. + type: string + name: + description: Name. + type: string + networks: + description: |- + The list of networks (overrides zone.network) + In CloudStackMachineSpec + items: + properties: + id: + description: Optional Network ID (overrides Name if + set) + type: string + ip: + description: Optional IP in the network + type: string + name: + description: CloudStack Network Name (required to resolve + ID) + type: string + required: + - name + type: object + type: array + offering: + description: CloudStack compute offering. + properties: + id: + description: Cloudstack resource ID. + type: string + name: + description: Cloudstack resource Name + type: string + type: object + providerID: + description: 'The CS specific unique identifier. Of the form: + fmt.Sprintf("cloudstack:///%s", CS Machine ID)' + type: string + sshKey: + description: CloudStack ssh key to use. + type: string + template: + description: CloudStack template to use. + properties: + id: + description: Cloudstack resource ID. + type: string + name: + description: Cloudstack resource Name + type: string + type: object + uncompressedUserData: + description: |- + UncompressedUserData specifies whether the user data is gzip-compressed. + cloud-init has built-in support for gzip-compressed user data, ignition does not type: boolean required: - offering @@ -397,9 +662,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackzones.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackzones.yaml index b30d42cd..01122151 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackzones.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_cloudstackzones.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackzones.infrastructure.cluster.x-k8s.io spec: group: infrastructure.cluster.x-k8s.io @@ -21,14 +20,19 @@ spec: description: CloudStackZone is the Schema for the cloudstackzones 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' + 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' + 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 @@ -71,9 +75,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 89694afa..f2d97573 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -1,5 +1,5 @@ commonLabels: - cluster.x-k8s.io/v1beta1: v1beta2 + cluster.x-k8s.io/v1beta1: v1beta1_v1beta2_v1beta3 # 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. diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index fef7060c..07457c88 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -28,7 +28,6 @@ patchesStrategicMerge: - manager_image_patch_edited.yaml - manager_webhook_patch.yaml - webhookcainjection_patch.yaml -- manager_prometheus_metrics_patch.yaml # Protect the /metrics endpoint by putting it behind auth. # If you want your controller-manager to expose the /metrics diff --git a/config/default/manager_prometheus_metrics_patch.yaml b/config/default/manager_prometheus_metrics_patch.yaml deleted file mode 100644 index 0b96c681..00000000 --- a/config/default/manager_prometheus_metrics_patch.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# This patch enables Prometheus scraping for the manager pod. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - metadata: - annotations: - prometheus.io/scrape: 'true' - spec: - containers: - # Expose the prometheus metrics on default port - - name: manager - ports: - - containerPort: 8080 - name: metrics - protocol: TCP diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index cf21f798..59addb60 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -17,15 +17,29 @@ spec: spec: securityContext: runAsNonRoot: true + seccompProfile: + type: RuntimeDefault containers: - command: - /manager args: - - --leader-elect + - "--leader-elect" + - "--diagnostics-address=${CAPI_DIAGNOSTICS_ADDRESS:=:8443}" + - "--insecure-diagnostics=${CAPI_INSECURE_DIAGNOSTICS:=false}" + - "--cloudstackcluster-concurrency=${CAPC_CLOUDSTACKCLUSTER_CONCURRENCY:=10}" + - "--cloudstackmachine-concurrency=${CAPC_CLOUDSTACKMACHINE_CONCURRENCY:=10}" + - "--enable-cloudstack-cks-sync=${CAPC_CLOUDSTACKMACHINE_CKS_SYNC:=false}" image: controller:latest name: manager securityContext: allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + runAsUser: 65532 + runAsGroup: 65532 + terminationMessagePolicy: FallbackToLogsOnError livenessProbe: httpGet: path: /healthz @@ -48,7 +62,7 @@ spec: serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane + - effect: NoSchedule + key: node-role.kubernetes.io/master + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 83ecdc65..c7283f28 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,13 +2,13 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: - "" resources: - configmaps + - secrets verbs: - get - list @@ -24,45 +24,14 @@ rules: - patch - update - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch - apiGroups: - cluster.x-k8s.io resources: - clusters - clusters/status - verbs: - - get - - list - - watch -- apiGroups: - - cluster.x-k8s.io - resources: - - machines - verbs: - - delete - - get - - list - - watch -- apiGroups: - - cluster.x-k8s.io - resources: - - machines - machines/status - verbs: - - get - - list - - watch -- apiGroups: - - cluster.x-k8s.io - resources: - machinesets + - machinesets/status verbs: - get - list @@ -70,8 +39,9 @@ rules: - apiGroups: - cluster.x-k8s.io resources: - - machinesets/status + - machines verbs: + - delete - get - list - watch @@ -79,13 +49,6 @@ rules: - controlplane.cluster.x-k8s.io resources: - kubeadmcontrolplanes - verbs: - - get - - list - - watch -- apiGroups: - - controlplane.cluster.x-k8s.io - resources: - kubeadmcontrolplanes/status verbs: - get @@ -95,13 +58,6 @@ rules: - etcdcluster.cluster.x-k8s.io resources: - etcdadmclusters - verbs: - - get - - list - - watch -- apiGroups: - - etcdcluster.cluster.x-k8s.io - resources: - etcdadmclusters/status verbs: - get @@ -111,111 +67,11 @@ rules: - infrastructure.cluster.x-k8s.io resources: - cloudstackaffinitygroups - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackaffinitygroups/finalizers - verbs: - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackaffinitygroups/status - verbs: - - get - - patch - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - cloudstackclusters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackclusters/finalizers - verbs: - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackclusters/status - verbs: - - create - - get - - patch - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - cloudstackfailuredomains - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackfailuredomains/finalizers - verbs: - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackfailuredomains/status - verbs: - - get - - patch - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - cloudstackisolatednetworks - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackisolatednetworks/finalizers - verbs: - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackisolatednetworks/status - verbs: - - get - - patch - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - cloudstackmachines + - cloudstackmachinestatecheckers verbs: - create - delete @@ -227,13 +83,22 @@ rules: - apiGroups: - infrastructure.cluster.x-k8s.io resources: + - cloudstackaffinitygroups/finalizers + - cloudstackclusters/finalizers + - cloudstackfailuredomains/finalizers + - cloudstackisolatednetworks/finalizers - cloudstackmachines/finalizers + - cloudstackmachinestatecheckers/finalizers verbs: - update - apiGroups: - infrastructure.cluster.x-k8s.io resources: + - cloudstackaffinitygroups/status + - cloudstackfailuredomains/status + - cloudstackisolatednetworks/status - cloudstackmachines/status + - cloudstackmachinestatecheckers/status verbs: - get - patch @@ -241,26 +106,9 @@ rules: - apiGroups: - infrastructure.cluster.x-k8s.io resources: - - cloudstackmachinestatecheckers + - cloudstackclusters/status verbs: - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackmachinestatecheckers/finalizers - verbs: - - update -- apiGroups: - - infrastructure.cluster.x-k8s.io - resources: - - cloudstackmachinestatecheckers/status - verbs: - get - patch - update diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index dccc5d03..8246aad0 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -2,23 +2,23 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: + - v1 - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackcluster + path: /mutate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackcluster failurePolicy: Fail name: mcloudstackcluster.kb.io rules: - apiGroups: - infrastructure.cluster.x-k8s.io apiVersions: - - v1beta2 + - v1beta3 operations: - CREATE - UPDATE @@ -26,46 +26,68 @@ webhooks: - cloudstackclusters sideEffects: None - admissionReviewVersions: + - v1 - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /mutate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachine + path: /mutate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachine failurePolicy: Fail name: mcloudstackmachine.kb.io rules: - apiGroups: - infrastructure.cluster.x-k8s.io apiVersions: - - v1beta2 + - v1beta3 operations: - CREATE - UPDATE resources: - cloudstackmachines sideEffects: None +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachinetemplate + failurePolicy: Fail + name: mcloudstackmachinetemplate.kb.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1beta3 + operations: + - CREATE + - UPDATE + resources: + - cloudstackmachinetemplates + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: + - v1 - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackcluster + path: /validate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackcluster failurePolicy: Fail name: vcloudstackcluster.kb.io rules: - apiGroups: - infrastructure.cluster.x-k8s.io apiVersions: - - v1beta2 + - v1beta3 operations: - CREATE - UPDATE @@ -73,19 +95,20 @@ webhooks: - cloudstackclusters sideEffects: None - admissionReviewVersions: + - v1 - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachine + path: /validate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachine failurePolicy: Fail name: vcloudstackmachine.kb.io rules: - apiGroups: - infrastructure.cluster.x-k8s.io apiVersions: - - v1beta2 + - v1beta3 operations: - CREATE - UPDATE @@ -93,19 +116,20 @@ webhooks: - cloudstackmachines sideEffects: None - admissionReviewVersions: + - v1 - v1beta1 clientConfig: service: name: webhook-service namespace: system - path: /validate-infrastructure-cluster-x-k8s-io-v1beta2-cloudstackmachinetemplate + path: /validate-infrastructure-cluster-x-k8s-io-v1beta3-cloudstackmachinetemplate failurePolicy: Fail name: vcloudstackmachinetemplate.kb.io rules: - apiGroups: - infrastructure.cluster.x-k8s.io apiVersions: - - v1beta2 + - v1beta3 operations: - CREATE - UPDATE diff --git a/controllers/cks_cluster_controller.go b/controllers/cks_cluster_controller.go new file mode 100644 index 00000000..c7b4a982 --- /dev/null +++ b/controllers/cks_cluster_controller.go @@ -0,0 +1,118 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + "strings" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" +) + +const CksClusterFinalizer = "ckscluster.infrastructure.cluster.x-k8s.io" + +// RBAC permissions for CloudStackCluster. +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters/status,verbs=create;get;update;patch +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackclusters/finalizers,verbs=update + +// CksClusterReconciliationRunner is a ReconciliationRunner with extensions specific to CloudStackClusters. +// The runner does the actual reconciliation. +type CksClusterReconciliationRunner struct { + *csCtrlrUtils.ReconciliationRunner + FailureDomains *infrav1.CloudStackFailureDomainList + ReconciliationSubject *infrav1.CloudStackCluster +} + +// CksClusterReconciler is the k8s controller manager's interface to reconcile a CloudStackCluster. +type CksClusterReconciler struct { + csCtrlrUtils.ReconcilerBase +} + +// Initialize a new CloudStackCluster reconciliation runner with concrete types and initialized member fields. +func NewCksClusterReconciliationRunner() *CksClusterReconciliationRunner { + // Set concrete type and init pointers. + runner := &CksClusterReconciliationRunner{ReconciliationSubject: &infrav1.CloudStackCluster{}} + runner.FailureDomains = &infrav1.CloudStackFailureDomainList{} + // Setup the base runner. Initializes pointers and links reconciliation methods. + runner.ReconciliationRunner = csCtrlrUtils.NewRunner(runner, runner.ReconciliationSubject, "CKSClusterController") + runner.CSCluster = runner.ReconciliationSubject + return runner +} + +// Reconcile is the method k8s will call upon a reconciliation request. +func (reconciler *CksClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (retRes ctrl.Result, retErr error) { + r := NewCksClusterReconciliationRunner() + r.UsingBaseReconciler(reconciler.ReconcilerBase).ForRequest(req).WithRequestCtx(ctx) + r.WithAdditionalCommonStages(r.GetFailureDomains(r.FailureDomains)) + return r.RunBaseReconciliationStages() +} + +// Reconcile actually reconciles the CloudStackCluster. +func (r *CksClusterReconciliationRunner) Reconcile() (res ctrl.Result, reterr error) { + if r.CSCluster.Spec.SyncWithACS == nil || !*r.CSCluster.Spec.SyncWithACS || len(r.FailureDomains.Items) == 0 { + return ctrl.Result{}, nil + } + // Prevent premature deletion. + controllerutil.AddFinalizer(r.ReconciliationSubject, CksClusterFinalizer) + + res, err := r.AsFailureDomainUser(&r.FailureDomains.Items[0].Spec)() + if r.ShouldReturn(res, err) { + return res, err + } + + r.Log.Info("Creating entry with CKS") + err = r.CSUser.GetOrCreateCksCluster(r.CAPICluster, r.ReconciliationSubject, &r.FailureDomains.Items[0].Spec) + if err != nil { + return r.RequeueWithMessage(fmt.Sprintf("Failed creating ExternalManaged CKS cluster on CloudStack. error: %s", err.Error())) + } + return ctrl.Result{}, nil +} + +// ReconcileDelete cleans up resources used by the cluster and finally removes the CloudStackCluster's finalizers. +func (r *CksClusterReconciliationRunner) ReconcileDelete() (ctrl.Result, error) { + if r.ReconciliationSubject.Status.CloudStackClusterID != "" { + if len(r.FailureDomains.Items) == 0 { + return ctrl.Result{}, fmt.Errorf("no failure domains found") + } + res, err := r.AsFailureDomainUser(&r.FailureDomains.Items[0].Spec)() + if r.ShouldReturn(res, err) { + return res, err + } + err = r.CSUser.DeleteCksCluster(r.ReconciliationSubject) + if err != nil && !strings.Contains(err.Error(), " not found") { + return r.RequeueWithMessage(fmt.Sprintf("Deleting cks cluster on CloudStack failed. error: %s", err.Error())) + } + } + controllerutil.RemoveFinalizer(r.ReconciliationSubject, CksClusterFinalizer) + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (reconciler *CksClusterReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error { + return ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). + Named("cks-cluster-controller"). + For(&infrav1.CloudStackCluster{}). + Complete(reconciler) +} diff --git a/controllers/cks_cluster_controller_test.go b/controllers/cks_cluster_controller_test.go new file mode 100644 index 00000000..b50835ff --- /dev/null +++ b/controllers/cks_cluster_controller_test.go @@ -0,0 +1,97 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers_test + +import ( + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + "go.uber.org/mock/gomock" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/cluster-api-provider-cloudstack/controllers" + "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" +) + +var _ = ginkgo.Describe("CksCloudStackClusterReconciler", func() { + ginkgo.Context("With k8s like test environment.", func() { + ginkgo.BeforeEach(func() { + dummies.SetDummyVars() + SetupTestEnvironment() + gomega.Ω(ClusterReconciler.SetupWithManager(ctx, k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack ClusterReconciler. + gomega.Ω(FailureDomainReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack FailureDomainReconciler. + gomega.Ω(CksClusterReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack Cks ClusterReconciler. + }) + + ginkgo.It("Should create a cluster in CKS.", func() { + mockCloudClient.EXPECT().GetOrCreateCksCluster(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_, arg1, _ interface{}) { + arg1.(*infrav1.CloudStackCluster).Status.CloudStackClusterID = "cluster-id-123" + }).MinTimes(1).Return(nil) + mockCloudClient.EXPECT().ResolveZone(gomock.Any()).AnyTimes() + mockCloudClient.EXPECT().ResolveNetworkForZone(gomock.Any()).AnyTimes().Do( + func(arg1 interface{}) { + arg1.(*infrav1.CloudStackZoneSpec).Network.ID = "SomeID" + arg1.(*infrav1.CloudStackZoneSpec).Network.Type = cloud.NetworkTypeShared + }).MinTimes(1) + + gomega.Eventually(func() string { + key := client.ObjectKeyFromObject(dummies.CSCluster) + if err := k8sClient.Get(ctx, key, dummies.CSCluster); err != nil { + return "" + } + return dummies.CSCluster.Status.CloudStackClusterID + }, timeout).WithPolling(pollInterval).Should(gomega.Equal("cluster-id-123")) + + }) + }) + + ginkgo.Context("With k8s like test environment.", func() { + ginkgo.BeforeEach(func() { + dummies.SetDummyVars() + dummies.CSCluster.Status.CloudStackClusterID = "cluster-id-123" + SetupTestEnvironment() + gomega.Ω(ClusterReconciler.SetupWithManager(ctx, k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack ClusterReconciler. + gomega.Ω(FailureDomainReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack FailureDomainReconciler. + gomega.Ω(CksClusterReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack Cks ClusterReconciler. + }) + + ginkgo.It("Should delete the cluster in CKS.", func() { + + gomega.Ω(k8sClient.Delete(ctx, dummies.CSCluster)).Should(gomega.Succeed()) + + gomega.Eventually(func() bool { + csCluster := &infrav1.CloudStackCluster{} + csClusterKey := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSCluster.Name} + if err := k8sClient.Get(ctx, csClusterKey, csCluster); err != nil { + return errors.IsNotFound(err) + } + return false + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + }) + + }) + + ginkgo.Context("Without a k8s test environment.", func() { + ginkgo.It("Should create a reconciliation runner with a Cloudstack Cluster as the reconciliation subject.", func() { + reconRunner := controllers.NewCksClusterReconciliationRunner() + gomega.Ω(reconRunner.ReconciliationSubject).ShouldNot(gomega.BeNil()) + }) + }) +}) diff --git a/controllers/cks_machine_controller.go b/controllers/cks_machine_controller.go new file mode 100644 index 00000000..9f0e527c --- /dev/null +++ b/controllers/cks_machine_controller.go @@ -0,0 +1,122 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" +) + +const CksMachineFinalizer = "cksMachine.infrastructure.cluster.x-k8s.io" + +// RBAC permissions for CloudStackMachines. +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines,verbs=get;list;watch +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines/status,verbs=get +// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines/finalizers,verbs=update + +// CksMachineReconciliationRunner is a ReconciliationRunner with extensions specific to CloudStackMachines. +// The runner does the actual reconciliation. +type CksMachineReconciliationRunner struct { + *csCtrlrUtils.ReconciliationRunner + FailureDomain *infrav1.CloudStackFailureDomain + ReconciliationSubject *infrav1.CloudStackMachine +} + +// CksMachineReconciler is the k8s controller manager's interface to reconcile CloudStackMachines with a CKS cluster. +type CksMachineReconciler struct { + csCtrlrUtils.ReconcilerBase +} + +// Initialize a new CloudStackMachines reconciliation runner with concrete types and initialized member fields. +func NewCksMachineReconciliationRunner() *CksMachineReconciliationRunner { + // Set concrete type and init pointers. + runner := &CksMachineReconciliationRunner{ReconciliationSubject: &infrav1.CloudStackMachine{}} + runner.FailureDomain = &infrav1.CloudStackFailureDomain{} + // Setup the base runner. Initializes pointers and links reconciliation methods. + runner.ReconciliationRunner = csCtrlrUtils.NewRunner(runner, runner.ReconciliationSubject, "CKSMachineController") + return runner +} + +// Reconcile is the method k8s will call upon a reconciliation request. +func (reconciler *CksMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (retRes ctrl.Result, retErr error) { + r := NewCksMachineReconciliationRunner() + r.UsingBaseReconciler(reconciler.ReconcilerBase).ForRequest(req).WithRequestCtx(ctx) + r.WithAdditionalCommonStages( + r.GetFailureDomainByName(func() string { return r.ReconciliationSubject.Spec.FailureDomainName }, r.FailureDomain), + r.AsFailureDomainUser(&r.FailureDomain.Spec)) + return r.RunBaseReconciliationStages() +} + +// Reconcile actually reconciles the CloudStackMachine. +func (r *CksMachineReconciliationRunner) Reconcile() (res ctrl.Result, reterr error) { + if r.CSCluster.Spec.SyncWithACS == nil || !*r.CSCluster.Spec.SyncWithACS { + return ctrl.Result{}, nil + } + if r.CSCluster.Status.CloudStackClusterID == "" { + return r.RequeueWithMessage("CloudStackClusterID is not set") + } + + if r.ReconciliationSubject.Spec.InstanceID == nil || *r.ReconciliationSubject.Spec.InstanceID == "" { + return r.RequeueWithMessage("InstanceID is not set") + } + + // Prevent premature deletion. + controllerutil.AddFinalizer(r.ReconciliationSubject, CksMachineFinalizer) + + res, err := r.AsFailureDomainUser(&r.FailureDomain.Spec)() + if r.ShouldReturn(res, err) { + return res, err + } + r.Log.Info("Assigning VM to CKS") + err = r.CSUser.AddVMToCksCluster(r.CSCluster, r.ReconciliationSubject) + if err != nil { + return r.RequeueWithMessage(fmt.Sprintf("Adding VM to CloudStack CKS cluster failed. error: %s", err.Error())) + } + r.Log.Info("Assigned VM to CKS") + return ctrl.Result{}, nil + +} + +// ReconcileDelete cleans up resources used by the cluster and finally removes the CloudStackMachine's finalizers. +func (r *CksMachineReconciliationRunner) ReconcileDelete() (ctrl.Result, error) { + r.Log.Info("Removing VM from CKS") + if r.ReconciliationSubject.Spec.InstanceID != nil && *r.ReconciliationSubject.Spec.InstanceID != "" { + err := r.CSUser.RemoveVMFromCksCluster(r.CSCluster, r.ReconciliationSubject) + if err != nil { + return r.RequeueWithMessage(fmt.Sprintf("Removing VM from CloudStack CKS cluster failed. error: %s", err.Error())) + } + } + r.Log.Info("Removed VM from CKS") + controllerutil.RemoveFinalizer(r.ReconciliationSubject, CksMachineFinalizer) + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (reconciler *CksMachineReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error { + return ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). + Named("cks-machine-controller"). + For(&infrav1.CloudStackMachine{}). + Complete(reconciler) +} diff --git a/controllers/cks_machine_controller_test.go b/controllers/cks_machine_controller_test.go new file mode 100644 index 00000000..d575a55a --- /dev/null +++ b/controllers/cks_machine_controller_test.go @@ -0,0 +1,136 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers_test + +import ( + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + "go.uber.org/mock/gomock" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/ptr" + + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/cluster-api-provider-cloudstack/controllers" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +var _ = ginkgo.Describe("CksCloudStackMachineReconciler", func() { + ginkgo.Context("With machine controller running.", func() { + ginkgo.BeforeEach(func() { + dummies.SetDummyVars() + dummies.CSCluster.Spec.SyncWithACS = ptr.To(true) + dummies.CSCluster.Spec.FailureDomains = dummies.CSCluster.Spec.FailureDomains[:1] + dummies.CSCluster.Spec.FailureDomains[0].Name = dummies.CSFailureDomain1.Spec.Name + dummies.CSCluster.Status.CloudStackClusterID = "cluster-id-123" + + SetupTestEnvironment() // Must happen before setting up managers/reconcilers. + gomega.Ω(MachineReconciler.SetupWithManager(ctx, k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register the CloudStack MachineReconciler. + gomega.Ω(CksClusterReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register the CloudStack MachineReconciler. + gomega.Ω(CksMachineReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register the CloudStack MachineReconciler. + + mockCloudClient.EXPECT().GetOrCreateCksCluster(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_, arg1, _ interface{}) { + arg1.(*infrav1.CloudStackCluster).Status.CloudStackClusterID = "cluster-id-123" + }).MinTimes(1).Return(nil) + // Point CAPI machine Bootstrap secret ref to dummy bootstrap secret. + dummies.CAPIMachine.Spec.Bootstrap.DataSecretName = &dummies.BootstrapSecret.Name + gomega.Ω(k8sClient.Create(ctx, dummies.BootstrapSecret)).Should(gomega.Succeed()) + + // Setup a failure domain for the machine reconciler to find. + gomega.Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)).Should(gomega.Succeed()) + setClusterReady(k8sClient) + }) + + ginkgo.It("Should call AddVMToCksCluster", func() { + // Mock a call to GetOrCreateVMInstance and set the machine to running. + mockCloudClient.EXPECT().GetOrCreateVMInstance( + gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any()).Do( + func(arg1, _, _, _, _, _ interface{}) { + arg1.(*infrav1.CloudStackMachine).Status.InstanceState = "Running" + }).AnyTimes() + + mockCloudClient.EXPECT().AddVMToCksCluster( + gomock.Any(), gomock.Any()).MinTimes(1).Return(nil) + // Have to do this here or the reconcile call to GetOrCreateVMInstance may happen too early. + setupMachineCRDs() + + // Eventually the machine should set ready to true. + gomega.Eventually(func() bool { + tempMachine := &infrav1.CloudStackMachine{} + key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} + if err := k8sClient.Get(ctx, key, tempMachine); err == nil { + if tempMachine.Status.Ready == true { + return len(tempMachine.ObjectMeta.Finalizers) > 1 + } + } + return false + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + }) + + ginkgo.It("Should call RemoveVMFromCksCluster when CS machine deleted", func() { + // Mock a call to GetOrCreateVMInstance and set the machine to running. + mockCloudClient.EXPECT().GetOrCreateVMInstance( + gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any()).Do( + func(arg1, _, _, _, _, _ interface{}) { + arg1.(*infrav1.CloudStackMachine).Status.InstanceState = "Running" + controllerutil.AddFinalizer(arg1.(*infrav1.CloudStackMachine), infrav1.MachineFinalizer) + }).AnyTimes() + + mockCloudClient.EXPECT().AddVMToCksCluster(gomock.Any(), gomock.Any()).MinTimes(1).Return(nil) + + mockCloudClient.EXPECT().DestroyVMInstance(gomock.Any()).MinTimes(1).Return(nil) + mockCloudClient.EXPECT().RemoveVMFromCksCluster( + gomock.Any(), gomock.Any()).MinTimes(1).Return(nil) + // Have to do this here or the reconcile call to GetOrCreateVMInstance may happen too early. + setupMachineCRDs() + + // Eventually the machine should set ready to true. + gomega.Eventually(func() bool { + tempMachine := &infrav1.CloudStackMachine{} + key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} + if err := k8sClient.Get(ctx, key, tempMachine); err == nil { + if tempMachine.Status.Ready == true { + return len(tempMachine.ObjectMeta.Finalizers) > 1 + } + } + return false + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + + gomega.Ω(k8sClient.Delete(ctx, dummies.CSMachine1)).Should(gomega.Succeed()) + + gomega.Eventually(func() bool { + tempMachine := &infrav1.CloudStackMachine{} + key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} + if err := k8sClient.Get(ctx, key, tempMachine); err != nil { + return errors.IsNotFound(err) + } + return false + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + }) + }) + + ginkgo.Context("Without a k8s test environment.", func() { + ginkgo.It("Should create a reconciliation runner with a Cloudstack Machine as the reconciliation subject.", func() { + reconRunner := controllers.NewCksMachineReconciliationRunner() + gomega.Ω(reconRunner.ReconciliationSubject).ShouldNot(gomega.BeNil()) + }) + }) +}) diff --git a/controllers/cloudstackaffinitygroup_controller.go b/controllers/cloudstackaffinitygroup_controller.go index 80ff10a1..d825abfd 100644 --- a/controllers/cloudstackaffinitygroup_controller.go +++ b/controllers/cloudstackaffinitygroup_controller.go @@ -20,9 +20,10 @@ import ( "context" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" ) @@ -76,9 +77,14 @@ func (r *CloudStackAGReconciliationRunner) Reconcile() (ctrl.Result, error) { } func (r *CloudStackAGReconciliationRunner) ReconcileDelete() (ctrl.Result, error) { - group := &cloud.AffinityGroup{Name: r.ReconciliationSubject.Name} + group := &cloud.AffinityGroup{Name: r.ReconciliationSubject.Spec.Name} _ = r.CSUser.FetchAffinityGroup(group) - if group.ID == "" { // Affinity group not found, must have been deleted. + // Affinity group not found, must have been deleted. + if group.ID == "" { + // Deleting affinity groups on Cloudstack can return error but succeed in + // deleting the affinity group. Removing finalizer if affinity group is not + // present on Cloudstack ensures affinity group object deletion is not blocked. + controllerutil.RemoveFinalizer(r.ReconciliationSubject, infrav1.AffinityGroupFinalizer) return ctrl.Result{}, nil } if err := r.CSUser.DeleteAffinityGroup(group); err != nil { @@ -89,8 +95,9 @@ func (r *CloudStackAGReconciliationRunner) ReconcileDelete() (ctrl.Result, error } // SetupWithManager sets up the controller with the Manager. -func (reconciler *CloudStackAffinityGroupReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (reconciler *CloudStackAffinityGroupReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error { return ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). For(&infrav1.CloudStackAffinityGroup{}). Complete(reconciler) } diff --git a/controllers/cloudstackaffinitygroup_controller_test.go b/controllers/cloudstackaffinitygroup_controller_test.go index 06f34a10..ded23512 100644 --- a/controllers/cloudstackaffinitygroup_controller_test.go +++ b/controllers/cloudstackaffinitygroup_controller_test.go @@ -17,32 +17,58 @@ limitations under the License. package controllers_test import ( - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" ) -var _ = Describe("CloudStackAffinityGroupReconciler", func() { - BeforeEach(func() { +var _ = ginkgo.Describe("CloudStackAffinityGroupReconciler", func() { + ginkgo.BeforeEach(func() { SetupTestEnvironment() // Must happen before setting up managers/reconcilers. dummies.SetDummyVars() - Ω(AffinityGReconciler.SetupWithManager(k8sManager)).Should(Succeed()) // Register CloudStack AffinityGReconciler. + gomega.Ω(AffinityGReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack AffinityGReconciler. }) - It("Should patch back the affinity group as ready after calling GetOrCreateAffinityGroup.", func() { + ginkgo.It("Should patch back the affinity group as ready after calling GetOrCreateAffinityGroup.", func() { // Modify failure domain name the same way the cluster controller would. dummies.CSAffinityGroup.Spec.FailureDomainName = dummies.CSFailureDomain1.Spec.Name - Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)) - Ω(k8sClient.Create(ctx, dummies.CSAffinityGroup)).Should(Succeed()) + mockCloudClient.EXPECT().GetOrCreateAffinityGroup(gomock.Any()).AnyTimes() + + gomega.Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)) + gomega.Ω(k8sClient.Create(ctx, dummies.CSAffinityGroup)).Should(gomega.Succeed()) + + // Test that the AffinityGroup controller sets Status.Ready to true. + gomega.Eventually(func() bool { + nameSpaceFilter := &client.ListOptions{Namespace: dummies.ClusterNameSpace} + affinityGroups := &infrav1.CloudStackAffinityGroupList{} + if err := k8sClient.List(ctx, affinityGroups, nameSpaceFilter); err == nil { + if len(affinityGroups.Items) == 1 { + return affinityGroups.Items[0].Status.Ready + } + } + return false + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + }) + + ginkgo.It("Should remove affinity group finalizer if corresponding affinity group is not present on Cloudstack.", func() { + // Modify failure domain name the same way the cluster controller would. + dummies.CSAffinityGroup.Spec.FailureDomainName = dummies.CSFailureDomain1.Spec.Name mockCloudClient.EXPECT().GetOrCreateAffinityGroup(gomock.Any()).AnyTimes() + gomega.Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)) + gomega.Ω(k8sClient.Create(ctx, dummies.CSAffinityGroup)).Should(gomega.Succeed()) + // Test that the AffinityGroup controller sets Status.Ready to true. - Eventually(func() bool { + gomega.Eventually(func() bool { nameSpaceFilter := &client.ListOptions{Namespace: dummies.ClusterNameSpace} affinityGroups := &infrav1.CloudStackAffinityGroupList{} if err := k8sClient.List(ctx, affinityGroups, nameSpaceFilter); err == nil { @@ -51,6 +77,23 @@ var _ = Describe("CloudStackAffinityGroupReconciler", func() { } } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + + mockCloudClient.EXPECT().FetchAffinityGroup(gomock.Any()).Do(func(arg1 interface{}) { + arg1.(*cloud.AffinityGroup).ID = "" + }).AnyTimes().Return(nil) + gomega.Ω(k8sClient.Delete(ctx, dummies.CSAffinityGroup)) + + gomega.Ω(k8sClient.Delete(ctx, dummies.CSAffinityGroup)) + + // Once the affinity group id was set to "" the controller should remove the finalizer and unblock deleting affinity group resource + gomega.Eventually(func() bool { + retrievedAffinityGroup := &infrav1.CloudStackAffinityGroup{} + affinityGroupKey := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.AffinityGroup.Name} + if err := k8sClient.Get(ctx, affinityGroupKey, retrievedAffinityGroup); err != nil { + return errors.IsNotFound(err) + } + return false + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) }) diff --git a/controllers/cloudstackcluster_controller.go b/controllers/cloudstackcluster_controller.go index 7bb68b64..19268c10 100644 --- a/controllers/cloudstackcluster_controller.go +++ b/controllers/cloudstackcluster_controller.go @@ -22,17 +22,19 @@ import ( "reflect" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" "github.com/pkg/errors" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/predicates" ) // RBAC permissions used in all reconcilers. Events and Secrets. @@ -129,7 +131,8 @@ func (r *CloudStackClusterReconciliationRunner) SetFailureDomainsStatusMap() (ct for _, fdSpec := range r.ReconciliationSubject.Spec.FailureDomains { metaHashName := infrav1.FailureDomainHashedMetaName(fdSpec.Name, r.CAPICluster.Name) r.ReconciliationSubject.Status.FailureDomains[fdSpec.Name] = clusterv1.FailureDomainSpec{ - ControlPlane: true, Attributes: map[string]string{"MetaHashName": metaHashName}} + ControlPlane: true, Attributes: map[string]string{"MetaHashName": metaHashName}, + } } return ctrl.Result{}, nil } @@ -153,10 +156,12 @@ func (r *CloudStackClusterReconciliationRunner) ReconcileDelete() (ctrl.Result, } // Called in main, this registers the cluster reconciler to the CAPI controller manager. -func (reconciler *CloudStackClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - controller, err := ctrl.NewControllerManagedBy(mgr). - For(&infrav1.CloudStackCluster{}). - WithEventFilter( +func (reconciler *CloudStackClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts controller.Options) error { + log := ctrl.LoggerFrom(ctx) + + b := ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). + For(&infrav1.CloudStackCluster{}, builder.WithPredicates( predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { oldCluster := e.ObjectOld.(*infrav1.CloudStackCluster).DeepCopy() @@ -177,23 +182,22 @@ func (reconciler *CloudStackClusterReconciler) SetupWithManager(ctx context.Cont return !reflect.DeepEqual(oldCluster, newCluster) }, }, - ).Build(reconciler) - if err != nil { - return errors.Wrap(err, "building CloudStackCluster controller") - } + ), + ) // Add a watch on CAPI Cluster objects for unpause and ready events. - err = controller.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, + b = b.Watches( + &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc( util.ClusterToInfrastructureMapFunc(ctx, infrav1.GroupVersion.WithKind("CloudStackCluster"), mgr.GetClient(), &infrav1.CloudStackCluster{})), - predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - oldCluster := e.ObjectOld.(*clusterv1.Cluster) - newCluster := e.ObjectNew.(*clusterv1.Cluster) - return oldCluster.Spec.Paused && !newCluster.Spec.Paused - }, - DeleteFunc: func(e event.DeleteEvent) bool { return false }, - CreateFunc: func(e event.CreateEvent) bool { return false }}) - return errors.Wrap(err, "building CloudStackCluster controller") + builder.WithPredicates( + predicates.ClusterUnpaused(mgr.GetScheme(), log), + ), + ) + + if err := b.Complete(reconciler); err != nil { + return errors.Wrap(err, "building CloudStackCluster controller") + } + + return nil } diff --git a/controllers/cloudstackcluster_controller_test.go b/controllers/cloudstackcluster_controller_test.go index 6c15661b..259504b7 100644 --- a/controllers/cloudstackcluster_controller_test.go +++ b/controllers/cloudstackcluster_controller_test.go @@ -17,41 +17,43 @@ limitations under the License. package controllers_test import ( - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/controllers" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" ) -var _ = Describe("CloudStackClusterReconciler", func() { - Context("With k8s like test environment.", func() { - BeforeEach(func() { - SetupTestEnvironment() // Must happen before setting up managers/reconcilers. - Ω(ClusterReconciler.SetupWithManager(ctx, k8sManager)).Should(Succeed()) // Register CloudStack ClusterReconciler. - Ω(FailureDomainReconciler.SetupWithManager(k8sManager)).Should(Succeed()) // Register CloudStack FailureDomainReconciler. +var _ = ginkgo.Describe("CloudStackClusterReconciler", func() { + ginkgo.Context("With k8s like test environment.", func() { + ginkgo.BeforeEach(func() { + SetupTestEnvironment() // Must happen before setting up managers/reconcilers. + gomega.Ω(ClusterReconciler.SetupWithManager(ctx, k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack ClusterReconciler. + gomega.Ω(FailureDomainReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack FailureDomainReconciler. }) - It("Should create a CloudStackFailureDomain.", func() { + ginkgo.It("Should create a CloudStackFailureDomain.", func() { tempfd := &infrav1.CloudStackFailureDomain{} mockCloudClient.EXPECT().ResolveZone(gomock.Any()).AnyTimes() - Eventually(func() bool { + gomega.Eventually(func() bool { key := client.ObjectKeyFromObject(dummies.CSFailureDomain1) key.Name = key.Name + "-" + dummies.CSCluster.Name if err := k8sClient.Get(ctx, key, tempfd); err != nil { return true } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) }) - Context("Without a k8s test environment.", func() { - It("Should create a reconciliation runner with a Cloudstack Cluster as the reconciliation subject.", func() { + ginkgo.Context("Without a k8s test environment.", func() { + ginkgo.It("Should create a reconciliation runner with a Cloudstack Cluster as the reconciliation subject.", func() { reconRunenr := controllers.NewCSClusterReconciliationRunner() - Ω(reconRunenr.ReconciliationSubject).ShouldNot(BeNil()) + gomega.Ω(reconRunenr.ReconciliationSubject).ShouldNot(gomega.BeNil()) }) }) }) diff --git a/controllers/cloudstackfailuredomain_controller.go b/controllers/cloudstackfailuredomain_controller.go index da0df8f5..4dbb04cc 100644 --- a/controllers/cloudstackfailuredomain_controller.go +++ b/controllers/cloudstackfailuredomain_controller.go @@ -18,16 +18,18 @@ package controllers import ( "context" + "sort" + "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sort" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" ) @@ -105,7 +107,9 @@ func (r *CloudStackFailureDomainReconciliationRunner) Reconcile() (retRes ctrl.R r.ReconciliationSubject.Spec.Zone.Network.Type == infrav1.NetworkTypeIsolated { netName := r.ReconciliationSubject.Spec.Zone.Network.Name if res, err := r.GenerateIsolatedNetwork( - netName, func() string { return r.ReconciliationSubject.Spec.Name })(); r.ShouldReturn(res, err) { + netName, + func() string { return r.ReconciliationSubject.Spec.Name }, + r.ReconciliationSubject.Spec.Zone.Network)(); r.ShouldReturn(res, err) { return res, err } else if res, err := r.GetObjectByName(r.IsoNetMetaName(netName), r.IsoNet)(); r.ShouldReturn(res, err) { return res, err @@ -263,7 +267,9 @@ func (r *CloudStackFailureDomainReconciliationRunner) RemoveFinalizer() (ctrl.Re } // SetupWithManager sets up the controller with the Manager. -func (reconciler *CloudStackFailureDomainReconciler) SetupWithManager(mgr ctrl.Manager) error { - _, err := ctrl.NewControllerManagedBy(mgr).For(&infrav1.CloudStackFailureDomain{}).Build(reconciler) - return err +func (reconciler *CloudStackFailureDomainReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error { + return ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). + For(&infrav1.CloudStackFailureDomain{}). + Complete(reconciler) } diff --git a/controllers/cloudstackfailuredomain_controller_test.go b/controllers/cloudstackfailuredomain_controller_test.go index 1874ed06..572d8148 100644 --- a/controllers/cloudstackfailuredomain_controller_test.go +++ b/controllers/cloudstackfailuredomain_controller_test.go @@ -17,30 +17,31 @@ limitations under the License. package controllers_test import ( - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/utils/pointer" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" ) -var _ = Describe("CloudStackFailureDomainReconciler", func() { - Context("With k8s like test environment.", func() { - BeforeEach(func() { +var _ = ginkgo.Describe("CloudStackFailureDomainReconciler", func() { + ginkgo.Context("With k8s like test environment.", func() { + ginkgo.BeforeEach(func() { dummies.SetDummyVars() - SetupTestEnvironment() // Must happen before setting up managers/reconcilers. - Ω(FailureDomainReconciler.SetupWithManager(k8sManager)).Should(Succeed()) // Register CloudStack FailureDomainReconciler. + SetupTestEnvironment() // Must happen before setting up managers/reconcilers. + gomega.Ω(FailureDomainReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack FailureDomainReconciler. // Modify failure domain name the same way the cluster controller would. dummies.CSFailureDomain1.Name = dummies.CSFailureDomain1.Name + "-" + dummies.CSCluster.Name - Ω(k8sClient.Create(ctx, dummies.ACSEndpointSecret1)) - Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)) + gomega.Ω(k8sClient.Create(ctx, dummies.ACSEndpointSecret1)) + gomega.Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)) mockCloudClient.EXPECT().ResolveZone(gomock.Any()).MinTimes(1) @@ -51,28 +52,28 @@ var _ = Describe("CloudStackFailureDomainReconciler", func() { }).MinTimes(1) }) - It("Should delete failure domain if no VM under this failure domain.", func() { - Eventually(func() bool { + ginkgo.It("Should delete failure domain if no VM under this failure domain.", func() { + gomega.Eventually(func() bool { return getFailuredomainStatus(dummies.CSFailureDomain1) - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) - Ω(k8sClient.Delete(ctx, dummies.CSFailureDomain1)) + gomega.Ω(k8sClient.Delete(ctx, dummies.CSFailureDomain1)) tempfd := &infrav1.CloudStackFailureDomain{} - Eventually(func() bool { + gomega.Eventually(func() bool { key := client.ObjectKeyFromObject(dummies.CSFailureDomain1) if err := k8sClient.Get(ctx, key, tempfd); err != nil { return errors.IsNotFound(err) } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) - DescribeTable("Should function in different replicas conditions", + ginkgo.DescribeTable("Should function in different replicas conditions", func(shouldDeleteVM bool, specReplicas, statusReplicas, statusReadyReplicas *int32, statusReady *bool, controlPlaneReady bool) { - Eventually(func() bool { + gomega.Eventually(func() bool { return getFailuredomainStatus(dummies.CSFailureDomain1) - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) setCSMachineOwnerCRD(dummies.CSMachineOwner, specReplicas, statusReplicas, statusReadyReplicas, statusReady) setCAPIMachineAndCSMachineCRDs(dummies.CSMachine1, dummies.CAPIMachine) @@ -80,9 +81,9 @@ var _ = Describe("CloudStackFailureDomainReconciler", func() { labelMachineFailuredomain(dummies.CSMachine1, dummies.CSFailureDomain1) if !controlPlaneReady { - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(dummies.CAPICluster, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) dummies.CAPICluster.Status.Conditions = []clusterv1.Condition{ { Type: "Ready", @@ -90,43 +91,43 @@ var _ = Describe("CloudStackFailureDomainReconciler", func() { }, } return ph.Patch(ctx, dummies.CAPICluster, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } - Ω(k8sClient.Delete(ctx, dummies.CSFailureDomain1)) + gomega.Ω(k8sClient.Delete(ctx, dummies.CSFailureDomain1)) CAPIMachine := &clusterv1.Machine{} if shouldDeleteVM { - Eventually(func() bool { + gomega.Eventually(func() bool { key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CAPIMachine.Name} if err := k8sClient.Get(ctx, key, CAPIMachine); err != nil { return errors.IsNotFound(err) } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) } else { - Consistently(func() bool { + gomega.Consistently(func() bool { key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CAPIMachine.Name} if err := k8sClient.Get(ctx, key, CAPIMachine); err == nil { return CAPIMachine.DeletionTimestamp.IsZero() } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) } }, // should delete - simulate owner is kubeadmcontrolplane - Entry("Should delete machine if spec.replicas > 1", true, pointer.Int32(2), pointer.Int32(2), pointer.Int32(2), pointer.Bool(true), true), + ginkgo.Entry("Should delete machine if spec.replicas > 1", true, ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(true), true), // should delete - simulate owner is etcdadmcluster - Entry("Should delete machine if status.readyReplica does not exist", true, pointer.Int32(2), pointer.Int32(2), nil, pointer.Bool(true), true), + ginkgo.Entry("Should delete machine if status.readyReplica does not exist", true, ptr.To(int32(2)), ptr.To(int32(2)), nil, ptr.To(true), true), // should delete - simulate owner is machineset - Entry("Should delete machine if status.ready does not exist", true, pointer.Int32(2), pointer.Int32(2), pointer.Int32(2), nil, true), + ginkgo.Entry("Should delete machine if status.ready does not exist", true, ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(int32(2)), nil, true), // should not delete if condition not met - Entry("Should not delete machine if cluster control plane not ready", false, pointer.Int32(2), pointer.Int32(2), pointer.Int32(2), pointer.Bool(true), false), - Entry("Should not delete machine if status.replicas < spec.replicas", false, pointer.Int32(2), pointer.Int32(1), pointer.Int32(1), pointer.Bool(true), true), - Entry("Should not delete machine if spec.replicas < 2", false, pointer.Int32(1), pointer.Int32(1), pointer.Int32(1), pointer.Bool(true), true), - Entry("Should not delete machine if status.ready is false", false, pointer.Int32(2), pointer.Int32(2), pointer.Int32(2), pointer.Bool(false), true), - Entry("Should not delete machine if status.readyReplicas <> status.replicas", false, pointer.Int32(2), pointer.Int32(2), pointer.Int32(1), pointer.Bool(true), true), + ginkgo.Entry("Should not delete machine if cluster control plane not ready", false, ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(true), false), + ginkgo.Entry("Should not delete machine if status.replicas < spec.replicas", false, ptr.To(int32(2)), ptr.To(int32(1)), ptr.To(int32(1)), ptr.To(true), true), + ginkgo.Entry("Should not delete machine if spec.replicas < 2", false, ptr.To(int32(1)), ptr.To(int32(1)), ptr.To(int32(1)), ptr.To(true), true), + ginkgo.Entry("Should not delete machine if status.ready is false", false, ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(false), true), + ginkgo.Entry("Should not delete machine if status.readyReplicas <> status.replicas", false, ptr.To(int32(2)), ptr.To(int32(2)), ptr.To(int32(1)), ptr.To(true), true), ) }) }) diff --git a/controllers/cloudstackisolatednetwork_controller.go b/controllers/cloudstackisolatednetwork_controller.go index f0fe94ae..77406412 100644 --- a/controllers/cloudstackisolatednetwork_controller.go +++ b/controllers/cloudstackisolatednetwork_controller.go @@ -22,10 +22,11 @@ import ( "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/pkg/errors" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" ) @@ -75,6 +76,9 @@ func (r *CloudStackIsoNetReconciliationRunner) Reconcile() (retRes ctrl.Result, if err != nil { return r.ReturnWrappedError(retErr, "setting up CloudStackCluster patcher") } + if r.FailureDomain.Spec.Zone.ID == "" { + return r.RequeueWithMessage("Zone ID not resolved yet.") + } if err := r.CSUser.GetOrCreateIsolatedNetwork(r.FailureDomain, r.ReconciliationSubject, r.CSCluster); err != nil { return ctrl.Result{}, err } @@ -92,7 +96,7 @@ func (r *CloudStackIsoNetReconciliationRunner) Reconcile() (retRes ctrl.Result, func (r *CloudStackIsoNetReconciliationRunner) ReconcileDelete() (retRes ctrl.Result, retErr error) { r.Log.Info("Deleting IsolatedNetwork.") - if err := r.CSUser.DisposeIsoNetResources(r.FailureDomain, r.ReconciliationSubject, r.CSCluster); err != nil { + if err := r.CSUser.DisposeIsoNetResources(r.ReconciliationSubject, r.CSCluster); err != nil { if !strings.Contains(strings.ToLower(err.Error()), "no match found") { return ctrl.Result{}, err } @@ -102,8 +106,9 @@ func (r *CloudStackIsoNetReconciliationRunner) ReconcileDelete() (retRes ctrl.Re } // SetupWithManager sets up the controller with the Manager. -func (reconciler *CloudStackIsoNetReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (reconciler *CloudStackIsoNetReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error { return ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). For(&infrav1.CloudStackIsolatedNetwork{}). Complete(reconciler) } diff --git a/controllers/cloudstackisolatednetwork_controller_test.go b/controllers/cloudstackisolatednetwork_controller_test.go index da73ba6d..f4cfed2d 100644 --- a/controllers/cloudstackisolatednetwork_controller_test.go +++ b/controllers/cloudstackisolatednetwork_controller_test.go @@ -17,34 +17,63 @@ limitations under the License. package controllers_test import ( - g "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + g "go.uber.org/mock/gomock" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" ) -var _ = Describe("CloudStackIsolatedNetworkReconciler", func() { - Context("With k8s like test environment.", func() { - BeforeEach(func() { - SetupTestEnvironment() // Must happen before setting up managers/reconcilers. - Ω(IsoNetReconciler.SetupWithManager(k8sManager)).Should(Succeed()) // Register CloudStack IsoNetReconciler. +var _ = ginkgo.Describe("CloudStackIsolatedNetworkReconciler", func() { + ginkgo.Context("With k8s like test environment.", func() { + ginkgo.BeforeEach(func() { + SetupTestEnvironment() // Must happen before setting up managers/reconcilers. + dummies.SetDummyVars() + gomega.Ω(IsoNetReconciler.SetupWithManager(k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).Should(gomega.Succeed()) // Register CloudStack IsoNetReconciler. }) - It("Should set itself to ready if there are no errors in calls to CloudStack methods.", func() { + ginkgo.It("Should set itself to ready if there are no errors in calls to CloudStack methods.", func() { mockCloudClient.EXPECT().GetOrCreateIsolatedNetwork(g.Any(), g.Any(), g.Any()).AnyTimes() mockCloudClient.EXPECT().AddClusterTag(g.Any(), g.Any(), g.Any()).AnyTimes() - Ω(k8sClient.Create(ctx, dummies.CSISONet1)).Should(Succeed()) - Eventually(func() bool { + // We use CSFailureDomain2 here because CSFailureDomain1 has an empty Spec.Zone.ID + dummies.CSISONet1.Spec.FailureDomainName = dummies.CSFailureDomain2.Spec.Name + gomega.Ω(k8sClient.Create(ctx, dummies.CSFailureDomain2)).Should(gomega.Succeed()) + gomega.Ω(k8sClient.Create(ctx, dummies.CSISONet1)).Should(gomega.Succeed()) + + gomega.Eventually(func() bool { tempIsoNet := &infrav1.CloudStackIsolatedNetwork{} key := client.ObjectKeyFromObject(dummies.CSISONet1) if err := k8sClient.Get(ctx, key, tempIsoNet); err == nil { - return true + if tempIsoNet.Status.Ready == true { + return true + } } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) + }) + }) + + ginkgo.Context("With a fake ctrlRuntimeClient and no test Env at all.", func() { + ginkgo.BeforeEach(func() { + setupFakeTestClient() + }) + + ginkgo.It("Should requeue in case the zone ID is not resolved yet.", func() { + // We use CSFailureDomain1 here because it has an empty Spec.Zone.ID + dummies.CSISONet1.Spec.FailureDomainName = dummies.CSFailureDomain1.Spec.Name + gomega.Ω(fakeCtrlClient.Create(ctx, dummies.CSFailureDomain1)).Should(gomega.Succeed()) + gomega.Ω(fakeCtrlClient.Create(ctx, dummies.CSISONet1)).Should(gomega.Succeed()) + + requestNamespacedName := types.NamespacedName{Namespace: dummies.ClusterNameSpace, Name: dummies.CSISONet1.Name} + res, err := IsoNetReconciler.Reconcile(ctx, ctrl.Request{NamespacedName: requestNamespacedName}) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(res.RequeueAfter).ShouldNot(gomega.BeZero()) }) }) }) diff --git a/controllers/cloudstackmachine_controller.go b/controllers/cloudstackmachine_controller.go index 8e4ad16b..9c0a1aab 100644 --- a/controllers/cloudstackmachine_controller.go +++ b/controllers/cloudstackmachine_controller.go @@ -19,25 +19,28 @@ package controllers import ( "context" "fmt" - "k8s.io/utils/pointer" "math/rand" "reflect" "regexp" + "time" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "sigs.k8s.io/cluster-api/util" + "sigs.k8s.io/cluster-api/util/predicates" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -105,7 +108,7 @@ func (reconciler *CloudStackMachineReconciler) Reconcile(ctx context.Context, re r := NewCSMachineReconciliationRunner() r.UsingBaseReconciler(reconciler.ReconcilerBase).ForRequest(req).WithRequestCtx(ctx) r.WithAdditionalCommonStages( - r.GetParent(r.ReconciliationSubject, r.CAPIMachine), + r.RunIf(func() bool { return r.ReconciliationSubject.GetDeletionTimestamp().IsZero() }, r.GetParent(r.ReconciliationSubject, r.CAPIMachine)), r.RequeueIfCloudStackClusterNotReady, r.SetFailureDomainOnCSMachine, r.GetFailureDomainByName(func() string { return r.ReconciliationSubject.Spec.FailureDomainName }, r.FailureDomain), @@ -135,18 +138,32 @@ func (r *CloudStackMachineReconciliationRunner) ConsiderAffinity() (ctrl.Result, r.ReconciliationSubject.Spec.Affinity == "" { // No managed affinity. return ctrl.Result{}, nil } + var agName string + var err error - agName, err := utils.GenerateAffinityGroupName(*r.ReconciliationSubject, r.CAPIMachine) - if err != nil { - return ctrl.Result{}, err + if r.ReconciliationSubject.Spec.AffinityGroupRef != nil { + agName = r.ReconciliationSubject.Spec.AffinityGroupRef.Name + } else { + agName, err = utils.GenerateAffinityGroupName(*r.ReconciliationSubject, r.CAPIMachine, r.CAPICluster) + if err != nil { + return ctrl.Result{}, err + } } // Set failure domain name and owners. r.AffinityGroup.Spec.FailureDomainName = r.ReconciliationSubject.Spec.FailureDomainName - if res, err := r.GetOrCreateAffinityGroup( - agName, r.ReconciliationSubject.Spec.Affinity, r.AffinityGroup, r.FailureDomain)(); r.ShouldReturn(res, err) { + res, err := r.GetOrCreateAffinityGroup( + agName, r.ReconciliationSubject.Spec.Affinity, r.AffinityGroup, r.FailureDomain)() + if r.ShouldReturn(res, err) { return res, err } + // Set affinity group reference. + r.ReconciliationSubject.Spec.AffinityGroupRef = &corev1.ObjectReference{ + Kind: r.AffinityGroup.Kind, + UID: r.AffinityGroup.UID, + Name: r.AffinityGroup.Name, + Namespace: r.AffinityGroup.Namespace, + } if !r.AffinityGroup.Status.Ready { return r.RequeueWithMessage("Required affinity group not ready.") } @@ -158,13 +175,16 @@ func (r *CloudStackMachineReconciliationRunner) ConsiderAffinity() (ctrl.Result, func (r *CloudStackMachineReconciliationRunner) SetFailureDomainOnCSMachine() (retRes ctrl.Result, reterr error) { if r.ReconciliationSubject.Spec.FailureDomainName == "" { var name string - if r.CAPIMachine.Spec.FailureDomain != nil && + // CAPIMachine is null if it's been deleted but we're still reconciling the CS machine. + if r.CAPIMachine != nil && r.CAPIMachine.Spec.FailureDomain != nil && (util.IsControlPlaneMachine(r.CAPIMachine) || // Is control plane machine -- CAPI will specify. *r.CAPIMachine.Spec.FailureDomain != "") { // Or potentially another machine controller specified. name = *r.CAPIMachine.Spec.FailureDomain r.ReconciliationSubject.Spec.FailureDomainName = *r.CAPIMachine.Spec.FailureDomain } else { // Not a control plane machine. Place randomly. - randNum := (rand.Int() % len(r.CSCluster.Spec.FailureDomains)) // #nosec G404 -- weak crypt rand doesn't matter here. + // Set a random seed for randomly placing CloudStackMachines in Zones. + randSeed := rand.New(rand.NewSource(time.Now().UnixNano())) // #nosec G404 -- weak crypt rand doesn't matter here. + randNum := (randSeed.Int() % len(r.CSCluster.Spec.FailureDomains)) name = r.CSCluster.Spec.FailureDomains[randNum].Name } r.ReconciliationSubject.Spec.FailureDomainName = name @@ -215,10 +235,19 @@ func (r *CloudStackMachineReconciliationRunner) GetOrCreateVMInstance() (retRes if !present { return ctrl.Result{}, errors.New("bootstrap secret data not yet set") } + // Check if the configmap has the clusterv1.ClusterNameLabel label + if _, ok := secret.Labels[clusterv1.ClusterNameLabel]; !ok { + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + secret.Labels[clusterv1.ClusterNameLabel] = r.CAPICluster.Name + if err := r.K8sClient.Update(context.TODO(), secret); err != nil { + return ctrl.Result{}, err + } + } userData := processCustomMetadata(data, r) err := r.CSUser.GetOrCreateVMInstance(r.ReconciliationSubject, r.CAPIMachine, r.CSCluster, r.FailureDomain, r.AffinityGroup, userData) - if err != nil { r.Recorder.Eventf(r.ReconciliationSubject, "Warning", "Creating", CSMachineCreationFailed, err.Error()) } @@ -266,13 +295,17 @@ func (r *CloudStackMachineReconciliationRunner) RequeueIfInstanceNotRunning() (r // AddToLBIfNeeded adds instance to load balancer if it is a control plane in an isolated network. func (r *CloudStackMachineReconciliationRunner) AddToLBIfNeeded() (retRes ctrl.Result, reterr error) { if util.IsControlPlaneMachine(r.CAPIMachine) && r.FailureDomain.Spec.Zone.Network.Type == cloud.NetworkTypeIsolated { - r.Log.Info("Assigning VM to load balancer rule.") if r.IsoNet.Spec.Name == "" { return r.RequeueWithMessage("Could not get required Isolated Network for VM, requeueing.") } - err := r.CSUser.AssignVMToLoadBalancerRule(r.IsoNet, *r.ReconciliationSubject.Spec.InstanceID) - if err != nil { - return ctrl.Result{}, err + + if r.IsoNet.Status.RoutingMode == "" { + // For non-routed networks, use load balancer + r.Log.Info("Assigning VM to load balancer rule.") + err := r.CSUser.AssignVMToLoadBalancerRule(r.IsoNet, *r.ReconciliationSubject.Spec.InstanceID) + if err != nil { + return ctrl.Result{}, err + } } } return ctrl.Result{}, nil @@ -301,8 +334,8 @@ func (r *CloudStackMachineReconciliationRunner) ReconcileDelete() (retRes ctrl.R // ResolveVMInstanceDetails can get InstanceID by CS machine name err := r.CSClient.ResolveVMInstanceDetails(r.ReconciliationSubject) if err != nil { - r.ReconciliationSubject.Status.Status = pointer.String(metav1.StatusFailure) - r.ReconciliationSubject.Status.Reason = pointer.String(err.Error() + + r.ReconciliationSubject.Status.Status = ptr.To(metav1.StatusFailure) + r.ReconciliationSubject.Status.Reason = ptr.To(err.Error() + fmt.Sprintf(" If this VM has already been deleted, please remove the finalizer named %s from object %s", "cloudstackmachine.infrastructure.cluster.x-k8s.io", r.ReconciliationSubject.Name)) // Cloudstack VM may be not found or more than one found by name @@ -330,84 +363,82 @@ func (r *CloudStackMachineReconciliationRunner) ReconcileDelete() (retRes ctrl.R } // SetupWithManager registers the machine reconciler to the CAPI controller manager. -func (reconciler *CloudStackMachineReconciler) SetupWithManager(mgr ctrl.Manager) error { - controller, err := ctrl.NewControllerManagedBy(mgr). - For(&infrav1.CloudStackMachine{}). - WithEventFilter( - predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - oldMachine := e.ObjectOld.(*infrav1.CloudStackMachine).DeepCopy() - newMachine := e.ObjectNew.(*infrav1.CloudStackMachine).DeepCopy() - // Ignore resource version because they are unique - oldMachine.ObjectMeta.ResourceVersion = "" - newMachine.ObjectMeta.ResourceVersion = "" - // Ignore generation because it's not used in reconcile - oldMachine.ObjectMeta.Generation = 0 - newMachine.ObjectMeta.Generation = 0 - // Ignore finalizers updates - oldMachine.ObjectMeta.Finalizers = nil - newMachine.ObjectMeta.Finalizers = nil - // Ignore ManagedFields because they are mirror of ObjectMeta - oldMachine.ManagedFields = nil - newMachine.ManagedFields = nil - // Ignore incremental status updates - oldMachine.Status = infrav1.CloudStackMachineStatus{} - newMachine.Status = infrav1.CloudStackMachineStatus{} - // Ignore provide ID - oldMachine.Spec.ProviderID = nil - newMachine.Spec.ProviderID = nil - // Ignore instance ID - oldMachine.Spec.InstanceID = nil - newMachine.Spec.InstanceID = nil - - return !reflect.DeepEqual(oldMachine, newMachine) +func (reconciler *CloudStackMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts controller.Options) error { + log := ctrl.LoggerFrom(ctx) + + b := ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). + For(&infrav1.CloudStackMachine{}, + builder.WithPredicates( + predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + oldMachine := e.ObjectOld.(*infrav1.CloudStackMachine).DeepCopy() + newMachine := e.ObjectNew.(*infrav1.CloudStackMachine).DeepCopy() + // Ignore resource version because they are unique + oldMachine.ObjectMeta.ResourceVersion = "" + newMachine.ObjectMeta.ResourceVersion = "" + // Ignore generation because it's not used in reconcile + oldMachine.ObjectMeta.Generation = 0 + newMachine.ObjectMeta.Generation = 0 + // Ignore finalizers updates + oldMachine.ObjectMeta.Finalizers = nil + newMachine.ObjectMeta.Finalizers = nil + // Ignore ManagedFields because they are mirror of ObjectMeta + oldMachine.ManagedFields = nil + newMachine.ManagedFields = nil + // Ignore incremental status updates + oldMachine.Status = infrav1.CloudStackMachineStatus{} + newMachine.Status = infrav1.CloudStackMachineStatus{} + // Ignore provide ID + oldMachine.Spec.ProviderID = nil + newMachine.Spec.ProviderID = nil + // Ignore instance ID + oldMachine.Spec.InstanceID = nil + newMachine.Spec.InstanceID = nil + + return !reflect.DeepEqual(oldMachine, newMachine) + }, }, - }, - ).Build(reconciler) - if err != nil { - return err - } + ), + ) // Watch CAPI machines for changes. // Queues a reconcile request for owned CloudStackMachine on change. // Used to update when bootstrap data becomes available. - if err = controller.Watch( - &source.Kind{Type: &clusterv1.Machine{}}, + b = b.Watches( + &clusterv1.Machine{}, handler.EnqueueRequestsFromMapFunc( util.MachineToInfrastructureMapFunc(infrav1.GroupVersion.WithKind("CloudStackMachine"))), - predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - oldMachine := e.ObjectOld.(*clusterv1.Machine) - newMachine := e.ObjectNew.(*clusterv1.Machine) - - return oldMachine.Spec.Bootstrap.DataSecretName == nil && newMachine.Spec.Bootstrap.DataSecretName != nil - }, - }, - ); err != nil { - return err - } + builder.WithPredicates( + predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + oldMachine := e.ObjectOld.(*clusterv1.Machine) + newMachine := e.ObjectNew.(*clusterv1.Machine) + + return oldMachine.Spec.Bootstrap.DataSecretName == nil && newMachine.Spec.Bootstrap.DataSecretName != nil + }, + }), + ) // Used below, this maps CAPI clusters to CAPC machines - csMachineMapper, err := util.ClusterToObjectsMapper(reconciler.K8sClient, &infrav1.CloudStackMachineList{}, mgr.GetScheme()) + csMachineMapper, err := util.ClusterToTypedObjectsMapper(reconciler.K8sClient, &infrav1.CloudStackMachineList{}, mgr.GetScheme()) if err != nil { return err } reconciler.Recorder = mgr.GetEventRecorderFor("capc-machine-controller") // Add a watch on CAPI Cluster objects for unpause and ready events. - return controller.Watch( - &source.Kind{Type: &clusterv1.Cluster{}}, + b = b.Watches( + &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(csMachineMapper), - predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - oldCluster := e.ObjectOld.(*clusterv1.Cluster) - newCluster := e.ObjectNew.(*clusterv1.Cluster) - return oldCluster.Spec.Paused && !newCluster.Spec.Paused - }, - CreateFunc: func(e event.CreateEvent) bool { - _, ok := e.Object.GetAnnotations()[clusterv1.PausedAnnotation] - return ok - }, - }, + builder.WithPredicates( + predicates.ClusterPausedTransitionsOrInfrastructureReady(mgr.GetScheme(), log), + ), ) + + if err := b.Complete(reconciler); err != nil { + return errors.Wrap(err, "building CloudStackMachine controller") + } + + return nil } diff --git a/controllers/cloudstackmachine_controller_test.go b/controllers/cloudstackmachine_controller_test.go index 36f17416..b8a1e34a 100644 --- a/controllers/cloudstackmachine_controller_test.go +++ b/controllers/cloudstackmachine_controller_test.go @@ -18,43 +18,45 @@ package controllers_test import ( "fmt" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "strings" + + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "strings" ) -var _ = Describe("CloudStackMachineReconciler", func() { - Context("With machine controller running.", func() { - BeforeEach(func() { +var _ = ginkgo.Describe("CloudStackMachineReconciler", func() { + ginkgo.Context("With machine controller running.", func() { + ginkgo.BeforeEach(func() { dummies.SetDummyVars() dummies.CSCluster.Spec.FailureDomains = dummies.CSCluster.Spec.FailureDomains[:1] dummies.CSCluster.Spec.FailureDomains[0].Name = dummies.CSFailureDomain1.Spec.Name - SetupTestEnvironment() // Must happen before setting up managers/reconcilers. - Ω(MachineReconciler.SetupWithManager(k8sManager)).Should(Succeed()) // Register the CloudStack MachineReconciler. + SetupTestEnvironment() // Must happen before setting up managers/reconcilers. + gomega.Expect(MachineReconciler.SetupWithManager(ctx, k8sManager, controller.Options{SkipNameValidation: ptr.To(true)})).To(gomega.Succeed()) // Register the CloudStack MachineReconciler. // Point CAPI machine Bootstrap secret ref to dummy bootstrap secret. dummies.CAPIMachine.Spec.Bootstrap.DataSecretName = &dummies.BootstrapSecret.Name - Ω(k8sClient.Create(ctx, dummies.BootstrapSecret)).Should(Succeed()) + gomega.Expect(k8sClient.Create(ctx, dummies.BootstrapSecret)).To(gomega.Succeed()) // Setup a failure domain for the machine reconciler to find. - Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed()) + gomega.Expect(k8sClient.Create(ctx, dummies.CSFailureDomain1)).To(gomega.Succeed()) setClusterReady(k8sClient) }) - It("Should call GetOrCreateVMInstance and set Status.Ready to true", func() { + ginkgo.It("Should call GetOrCreateVMInstance and set Status.Ready to true", func() { // Mock a call to GetOrCreateVMInstance and set the machine to running. mockCloudClient.EXPECT().GetOrCreateVMInstance( gomock.Any(), gomock.Any(), gomock.Any(), @@ -67,7 +69,7 @@ var _ = Describe("CloudStackMachineReconciler", func() { setupMachineCRDs() // Eventually the machine should set ready to true. - Eventually(func() bool { + gomega.Eventually(func() bool { tempMachine := &infrav1.CloudStackMachine{} key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} if err := k8sClient.Get(ctx, key, tempMachine); err == nil { @@ -76,10 +78,10 @@ var _ = Describe("CloudStackMachineReconciler", func() { } } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) - It("Should call DestroyVMInstance when CS machine deleted", func() { + ginkgo.It("Should call DestroyVMInstance when CS machine deleted", func() { // Mock a call to GetOrCreateVMInstance and set the machine to running. mockCloudClient.EXPECT().GetOrCreateVMInstance( gomock.Any(), gomock.Any(), gomock.Any(), @@ -94,7 +96,7 @@ var _ = Describe("CloudStackMachineReconciler", func() { setupMachineCRDs() // Eventually the machine should set ready to true. - Eventually(func() bool { + gomega.Eventually(func() bool { tempMachine := &infrav1.CloudStackMachine{} key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} if err := k8sClient.Get(ctx, key, tempMachine); err == nil { @@ -103,23 +105,23 @@ var _ = Describe("CloudStackMachineReconciler", func() { } } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) - Ω(k8sClient.Delete(ctx, dummies.CSMachine1)).Should(Succeed()) + gomega.Expect(k8sClient.Delete(ctx, dummies.CSMachine1)).To(gomega.Succeed()) - Eventually(func() bool { + gomega.Eventually(func() bool { tempMachine := &infrav1.CloudStackMachine{} key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} if err := k8sClient.Get(ctx, key, tempMachine); err != nil { return errors.IsNotFound(err) } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) - It("Should call ResolveVMInstanceDetails when CS machine without instanceID deleted", func() { - instanceID := pointer.String("instance-id-123") + ginkgo.It("Should call ResolveVMInstanceDetails when CS machine without instanceID deleted", func() { + instanceID := ptr.To("instance-id-123") // Mock a call to GetOrCreateVMInstance and set the machine to running. mockCloudClient.EXPECT().GetOrCreateVMInstance( gomock.Any(), gomock.Any(), gomock.Any(), @@ -139,7 +141,7 @@ var _ = Describe("CloudStackMachineReconciler", func() { setupMachineCRDs() // Eventually the machine should set ready to true. - Eventually(func() bool { + gomega.Eventually(func() bool { tempMachine := &infrav1.CloudStackMachine{} key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} if err := k8sClient.Get(ctx, key, tempMachine); err == nil { @@ -148,34 +150,34 @@ var _ = Describe("CloudStackMachineReconciler", func() { } } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) // remove instanceID from CS machine ph, err := patch.NewHelper(dummies.CSMachine1, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) dummies.CSMachine1.Spec.InstanceID = nil - Ω(ph.Patch(ctx, dummies.CSMachine1)).Should(Succeed()) - Ω(k8sClient.Delete(ctx, dummies.CSMachine1)).Should(Succeed()) + gomega.Expect(ph.Patch(ctx, dummies.CSMachine1)).To(gomega.Succeed()) + gomega.Expect(k8sClient.Delete(ctx, dummies.CSMachine1)).To(gomega.Succeed()) - Eventually(func() bool { + gomega.Eventually(func() bool { tempMachine := &infrav1.CloudStackMachine{} key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} if err := k8sClient.Get(ctx, key, tempMachine); err != nil { return errors.IsNotFound(err) } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) - It("Should replace ds.meta_data.xxx with proper values.", func() { + ginkgo.It("Should replace ds.meta_data.xxx with proper values.", func() { // Mock a call to GetOrCreateVMInstance and set the machine to running. mockCloudClient.EXPECT().GetOrCreateVMInstance( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do( func(arg1, _, _, _, _, userdata interface{}) { expectedUserdata := fmt.Sprintf("%s{{%s}}", dummies.CAPIMachine.Name, dummies.CSMachine1.Spec.FailureDomainName) - Ω(userdata == expectedUserdata).Should(BeTrue()) + gomega.Expect(userdata == expectedUserdata).To(gomega.BeTrue()) arg1.(*infrav1.CloudStackMachine).Status.InstanceState = "Running" }).AnyTimes() @@ -183,7 +185,7 @@ var _ = Describe("CloudStackMachineReconciler", func() { setupMachineCRDs() // Eventually the machine should set ready to true. - Eventually(func() bool { + gomega.Eventually(func() bool { tempMachine := &infrav1.CloudStackMachine{} key := client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} if err := k8sClient.Get(ctx, key, tempMachine); err == nil { @@ -192,18 +194,18 @@ var _ = Describe("CloudStackMachineReconciler", func() { } } return false - }, timeout).WithPolling(pollInterval).Should(BeTrue()) + }, timeout).WithPolling(pollInterval).Should(gomega.BeTrue()) }) }) - Context("With a fake ctrlRuntimeClient and no test Env at all.", func() { - BeforeEach(func() { + ginkgo.Context("With a fake ctrlRuntimeClient and no test Env at all.", func() { + ginkgo.BeforeEach(func() { setupFakeTestClient() dummies.CSCluster.Spec.FailureDomains = dummies.CSCluster.Spec.FailureDomains[:1] dummies.CSCluster.Spec.FailureDomains[0].Name = dummies.CSFailureDomain1.Spec.Name }) - It("Should exit having not found a failure domain to place the machine in.", func() { + ginkgo.It("Should exit having not found a failure domain to place the machine in.", func() { key := client.ObjectKeyFromObject(dummies.CSCluster) dummies.CAPIMachine.Name = "someMachine" dummies.CSMachine1.OwnerReferences = append(dummies.CSMachine1.OwnerReferences, metav1.OwnerReference{ @@ -212,17 +214,17 @@ var _ = Describe("CloudStackMachineReconciler", func() { Name: dummies.CAPIMachine.Name, UID: "uniqueness", }) - Ω(fakeCtrlClient.Get(ctx, key, dummies.CSCluster)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.CAPIMachine)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) + gomega.Expect(fakeCtrlClient.Get(ctx, key, dummies.CSCluster)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.CAPIMachine)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.CSMachine1)).To(gomega.Succeed()) requestNamespacedName := types.NamespacedName{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} res, err := MachineReconciler.Reconcile(ctx, ctrl.Request{NamespacedName: requestNamespacedName}) - Ω(err).ShouldNot(HaveOccurred()) - Ω(res.RequeueAfter).ShouldNot(BeZero()) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(res.RequeueAfter).ShouldNot(gomega.BeZero()) }) - It("Should create event Machine instance is Running", func() { + ginkgo.It("Should create event Machine instance is Running", func() { key := client.ObjectKeyFromObject(dummies.CSCluster) dummies.CAPIMachine.Name = "someMachine" dummies.CAPIMachine.Spec.Bootstrap.DataSecretName = &dummies.BootstrapSecret.Name @@ -238,29 +240,29 @@ var _ = Describe("CloudStackMachineReconciler", func() { func(arg1, _, _, _, _, _ interface{}) { arg1.(*infrav1.CloudStackMachine).Status.InstanceState = "Running" }).AnyTimes() - Ω(fakeCtrlClient.Get(ctx, key, dummies.CSCluster)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.CAPIMachine)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.ACSEndpointSecret1)).Should(Succeed()) - Ω(fakeCtrlClient.Create(ctx, dummies.BootstrapSecret)).Should(Succeed()) + gomega.Expect(fakeCtrlClient.Get(ctx, key, dummies.CSCluster)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.CAPIMachine)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.CSMachine1)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.CSFailureDomain1)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.ACSEndpointSecret1)).To(gomega.Succeed()) + gomega.Expect(fakeCtrlClient.Create(ctx, dummies.BootstrapSecret)).To(gomega.Succeed()) setClusterReady(fakeCtrlClient) requestNamespacedName := types.NamespacedName{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name} MachineReconciler.AsFailureDomainUser(&dummies.CSFailureDomain1.Spec) res, err := MachineReconciler.Reconcile(ctx, ctrl.Request{NamespacedName: requestNamespacedName}) - Ω(err).ShouldNot(HaveOccurred()) - Ω(res.RequeueAfter).Should(BeZero()) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + gomega.Expect(res.RequeueAfter).Should(gomega.BeZero()) - Eventually(func() bool { + gomega.Eventually(func() bool { for event := range fakeRecorder.Events { return strings.Contains(event, "Normal Created CloudStack instance Created") || strings.Contains(event, "Normal Running Machine instance is Running...") || strings.Contains(event, "Normal Machine State Checker CloudStackMachineStateChecker created") } return false - }, timeout).Should(BeTrue()) + }, timeout).Should(gomega.BeTrue()) }) }) }) diff --git a/controllers/cloudstackmachinestatechecker_controller.go b/controllers/cloudstackmachinestatechecker_controller.go index 66a246a5..d3eba1e0 100644 --- a/controllers/cloudstackmachinestatechecker_controller.go +++ b/controllers/cloudstackmachinestatechecker_controller.go @@ -24,7 +24,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) diff --git a/controllers/controllers_suite_test.go b/controllers/controllers_suite_test.go index b6cec1c4..4082c0d9 100644 --- a/controllers/controllers_suite_test.go +++ b/controllers/controllers_suite_test.go @@ -21,26 +21,26 @@ import ( "flag" "fmt" "go/build" - "k8s.io/client-go/tools/record" "os" "os/exec" "path/filepath" "regexp" - "sigs.k8s.io/cluster-api-provider-cloudstack/test/fakes" "strings" "testing" "time" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/cluster-api-provider-cloudstack/test/fakes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" - "k8s.io/klog/v2/klogr" "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/go-logr/logr" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + ginkgo "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "github.com/pkg/errors" + gomock "go.uber.org/mock/gomock" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -49,20 +49,20 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/manager" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" csReconcilers "sigs.k8s.io/cluster-api-provider-cloudstack/controllers" csCtrlrUtils "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/mocks" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util/patch" //+kubebuilder:scaffold:imports ) func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Controller Suite") + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Controller Suite") } var ( @@ -129,9 +129,13 @@ var ( FailureDomainReconciler *csReconcilers.CloudStackFailureDomainReconciler IsoNetReconciler *csReconcilers.CloudStackIsoNetReconciler AffinityGReconciler *csReconcilers.CloudStackAffinityGroupReconciler + + // CKS Reconcilers + CksClusterReconciler *csReconcilers.CksClusterReconciler + CksMachineReconciler *csReconcilers.CksMachineReconciler ) -var _ = BeforeSuite(func() { +var _ = ginkgo.BeforeSuite(func() { repoRoot := os.Getenv("REPO_ROOT") // Add ginkgo recover statements to controllers. @@ -142,18 +146,18 @@ var _ = BeforeSuite(func() { os.Exit(1) } - By("bootstrapping test environment") + ginkgo.By("bootstrapping test environment") - Ω(infrav1.AddToScheme(scheme.Scheme)).Should(Succeed()) - Ω(clusterv1.AddToScheme(scheme.Scheme)).Should(Succeed()) - Ω(fakes.AddToScheme(scheme.Scheme)).Should(Succeed()) + gomega.Ω(infrav1.AddToScheme(scheme.Scheme)).Should(gomega.Succeed()) + gomega.Ω(clusterv1.AddToScheme(scheme.Scheme)).Should(gomega.Succeed()) + gomega.Ω(fakes.AddToScheme(scheme.Scheme)).Should(gomega.Succeed()) // Increase log verbosity. klog.InitFlags(nil) - Ω(flag.Lookup("v").Value.Set("1")).Should(Succeed()) + gomega.Ω(flag.Lookup("v").Value.Set("1")).Should(gomega.Succeed()) flag.Parse() - logger = klogr.New() + logger = klog.Background() }) // A mock fo the CloudClient interface used in controller utils. @@ -192,21 +196,21 @@ func SetupTestEnvironment() { var err error done := make(chan interface{}) go func() { - defer GinkgoRecover() + defer ginkgo.GinkgoRecover() cfg, err = testEnv.Start() close(done) }() - Eventually(done).WithTimeout(time.Minute).Should(BeClosed()) - Ω(err).ShouldNot(HaveOccurred()) - Ω(cfg).ShouldNot(BeNil()) + gomega.Eventually(done).WithTimeout(time.Minute).Should(gomega.BeClosed()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(cfg).ShouldNot(gomega.BeNil()) //+kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Ω(err).ShouldNot(HaveOccurred()) - Ω(k8sClient).ShouldNot(BeNil()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(k8sClient).ShouldNot(gomega.BeNil()) k8sManager, _ = ctrl.NewManager(cfg, ctrl.Options{Scheme: scheme.Scheme}) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) // Base reconciler shared across reconcilers. base := csCtrlrUtils.ReconcilerBase{ @@ -224,6 +228,9 @@ func SetupTestEnvironment() { IsoNetReconciler = &csReconcilers.CloudStackIsoNetReconciler{ReconcilerBase: base} AffinityGReconciler = &csReconcilers.CloudStackAffinityGroupReconciler{ReconcilerBase: base} + CksClusterReconciler = &csReconcilers.CksClusterReconciler{ReconcilerBase: base} + CksMachineReconciler = &csReconcilers.CksMachineReconciler{ReconcilerBase: base} + ctx, cancel = context.WithCancel(context.TODO()) // Setup mock clients. @@ -237,16 +244,19 @@ func SetupTestEnvironment() { AffinityGReconciler.CSClient = mockCloudClient FailureDomainReconciler.CSClient = mockCloudClient + CksClusterReconciler.CSClient = mockCloudClient + CksMachineReconciler.CSClient = mockCloudClient + setupClusterCRDs() // See reconciliation results. Left commented as it's noisy otherwise. // TODO: find a way to see controller output without the additional setup output. ctrl.SetLogger(logger) - DeferCleanup(func() { + ginkgo.DeferCleanup(func() { // Cancelling the context shuts down any outstanding requests and the test environment. cancel() - Ω(testEnv.Stop()).Should(Succeed()) + gomega.Ω(testEnv.Stop()).Should(gomega.Succeed()) k8sManager = nil }) } @@ -256,7 +266,9 @@ func setupFakeTestClient() { dummies.SetDummyVars() // Make a fake k8s client with CloudStack and CAPI cluster. - fakeCtrlClient = fake.NewClientBuilder().WithObjects(dummies.CSCluster, dummies.CAPICluster).Build() + fakeCtrlClient = fake.NewClientBuilder().WithScheme(scheme.Scheme). + WithObjects(dummies.CSCluster, dummies.CAPICluster). + WithStatusSubresource(dummies.CSCluster, dummies.CSMachine1).Build() fakeRecorder = record.NewFakeRecorder(fakeEventBufferSize) // Setup mock clients. mockCSAPIClient = cloudstack.NewMockClient(mockCtrl) @@ -288,62 +300,62 @@ func setupFakeTestClient() { FailureDomainReconciler.CSClient = mockCloudClient AffinityGReconciler.CSClient = mockCloudClient - DeferCleanup(func() { + ginkgo.DeferCleanup(func() { cancel() }) } // Setup and teardown on a per test basis. -var _ = BeforeEach(func() { +var _ = ginkgo.BeforeEach(func() { dummies.SetDummyVars() - mockCtrl = gomock.NewController(GinkgoT()) + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) }) -var _ = JustBeforeEach(func() { +var _ = ginkgo.JustBeforeEach(func() { if k8sManager != nil { // Allow skipping a test environment for tests that don't need it. // Launch the k8s manager. // Needs to be in JustBeforeEach() so individual contexts can register controllers first. go func() { - defer GinkgoRecover() - Ω(k8sManager.Start(ctx)).Should(Succeed(), "failed to run manager") + defer ginkgo.GinkgoRecover() + gomega.Ω(k8sManager.Start(ctx)).Should(gomega.Succeed(), "failed to run manager") }() } }) -var _ = AfterEach(func() { +var _ = ginkgo.AfterEach(func() { // Finishing mockCtrl checks expected calls on mock objects matched. mockCtrl.Finish() }) -var _ = AfterSuite(func() {}) +var _ = ginkgo.AfterSuite(func() {}) -// setClusterReady patches the clsuter with ready status true. +// setClusterReady patches the cluster with ready status true. func setClusterReady(client client.Client) { - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(dummies.CSCluster, client) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) dummies.CSCluster.Status.Ready = true return ph.Patch(ctx, dummies.CSCluster, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } // setupClusterCRDs creates a CAPI and CloudStack cluster with an appropriate ownership ref between them. func setupClusterCRDs() { // Create them. - Ω(k8sClient.Create(ctx, dummies.CAPICluster)).Should(Succeed()) - Ω(k8sClient.Create(ctx, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(k8sClient.Create(ctx, dummies.CAPICluster)).Should(gomega.Succeed()) + gomega.Ω(k8sClient.Create(ctx, dummies.CSCluster)).Should(gomega.Succeed()) // Fetch the CS Cluster that was created. key := client.ObjectKey{Namespace: dummies.CSCluster.Namespace, Name: dummies.CSCluster.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, dummies.CSCluster) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) // Set owner ref from CAPI cluster to CS Cluster and patch back the CS Cluster. - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(dummies.CSCluster, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) dummies.CSCluster.OwnerReferences = append(dummies.CSCluster.OwnerReferences, metav1.OwnerReference{ Kind: "Cluster", APIVersion: clusterv1.GroupVersion.String(), @@ -351,25 +363,25 @@ func setupClusterCRDs() { UID: "uniqueness", }) return ph.Patch(ctx, dummies.CSCluster, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } // setupMachineCRDs creates a CAPI and CloudStack machine with an appropriate ownership ref between them. func setupMachineCRDs() { // Create them. - Ω(k8sClient.Create(ctx, dummies.CAPIMachine)).Should(Succeed()) - Ω(k8sClient.Create(ctx, dummies.CSMachine1)).Should(Succeed()) + gomega.Ω(k8sClient.Create(ctx, dummies.CAPIMachine)).Should(gomega.Succeed()) + gomega.Ω(k8sClient.Create(ctx, dummies.CSMachine1)).Should(gomega.Succeed()) // Fetch the CS Machine that was created. key := client.ObjectKey{Namespace: dummies.CSCluster.Namespace, Name: dummies.CSMachine1.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, dummies.CSMachine1) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) // Set owner ref from CAPI machine to CS machine and patch back the CS machine. - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(dummies.CSMachine1, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) dummies.CSMachine1.OwnerReferences = append(dummies.CSMachine1.OwnerReferences, metav1.OwnerReference{ Kind: "Machine", APIVersion: clusterv1.GroupVersion.String(), @@ -377,47 +389,47 @@ func setupMachineCRDs() { UID: "uniqueness", }) return ph.Patch(ctx, dummies.CSMachine1, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } func setCSMachineOwnerCRD(owner *fakes.CloudStackMachineOwner, specReplicas, statusReplicas, statusReadyReplicas *int32, statusReady *bool) { owner.Spec.Replicas = specReplicas - Ω(k8sClient.Create(ctx, owner)).Should(Succeed()) + gomega.Ω(k8sClient.Create(ctx, owner)).Should(gomega.Succeed()) key := client.ObjectKey{Namespace: owner.Namespace, Name: owner.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, owner) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) - Eventually(func() error { + gomega.Eventually(func() error { owner.Status.Ready = statusReady owner.Status.Replicas = statusReplicas owner.Status.ReadyReplicas = statusReadyReplicas return k8sClient.Status().Update(ctx, owner) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) } // setCAPIMachineAndCSMachineCRDs creates a CAPI and CloudStack machine with an appropriate ownership ref between them. func setCAPIMachineAndCSMachineCRDs(CSMachine *infrav1.CloudStackMachine, CAPIMachine *clusterv1.Machine) { // Create them. - Ω(k8sClient.Create(ctx, CAPIMachine)).Should(Succeed()) - Ω(k8sClient.Create(ctx, CSMachine)).Should(Succeed()) + gomega.Ω(k8sClient.Create(ctx, CAPIMachine)).Should(gomega.Succeed()) + gomega.Ω(k8sClient.Create(ctx, CSMachine)).Should(gomega.Succeed()) // Fetch the CS Machine that was created. key := client.ObjectKey{Namespace: dummies.CSCluster.Namespace, Name: CSMachine.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, CSMachine) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) // Fetch the CAPI Machine that was created. key = client.ObjectKey{Namespace: dummies.ClusterNameSpace, Name: CAPIMachine.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, CAPIMachine) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) // Set ownerReference to CAPI machine in CS machine and patch back the CS machine. - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(dummies.CSMachine1, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) dummies.CSMachine1.OwnerReferences = append(dummies.CSMachine1.OwnerReferences, metav1.OwnerReference{ Kind: "Machine", APIVersion: clusterv1.GroupVersion.String(), @@ -425,36 +437,36 @@ func setCAPIMachineAndCSMachineCRDs(CSMachine *infrav1.CloudStackMachine, CAPIMa UID: "uniqueness", }) return ph.Patch(ctx, CSMachine, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } func setMachineOwnerReference(CSMachine *infrav1.CloudStackMachine, ownerRef metav1.OwnerReference) { key := client.ObjectKey{Namespace: dummies.CSCluster.Namespace, Name: CSMachine.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, CSMachine) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) // Set ownerReference to CAPI machine in CS machine and patch back the CS machine. - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(CSMachine, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) CSMachine.OwnerReferences = append(CSMachine.OwnerReferences, ownerRef) return ph.Patch(ctx, CSMachine, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } // labelMachineFailuredomain add cloudstackfailuredomain info in the labels. func labelMachineFailuredomain(CSMachine *infrav1.CloudStackMachine, CSFailureDomain1 *infrav1.CloudStackFailureDomain) { key := client.ObjectKey{Namespace: dummies.CSCluster.Namespace, Name: CSMachine.Name} - Eventually(func() error { + gomega.Eventually(func() error { return k8sClient.Get(ctx, key, CSMachine) - }, timeout).Should(BeNil()) + }, timeout).Should(gomega.BeNil()) // set cloudstack failuredomain in machine labels. - Eventually(func() error { + gomega.Eventually(func() error { ph, err := patch.NewHelper(CSMachine, k8sClient) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) CSMachine.Labels["cloudstackfailuredomain.infrastructure.cluster.x-k8s.io/name"] = CSFailureDomain1.Name return ph.Patch(ctx, CSMachine, patch.WithStatusObservedGeneration{}) - }, timeout).Should(Succeed()) + }, timeout).Should(gomega.Succeed()) } diff --git a/controllers/utils/affinity_group.go b/controllers/utils/affinity_group.go index 37a5cfe6..8f117539 100644 --- a/controllers/utils/affinity_group.go +++ b/controllers/utils/affinity_group.go @@ -18,13 +18,14 @@ package utils import ( "fmt" + "strings" + "golang.org/x/text/cases" "golang.org/x/text/language" - "strings" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -88,12 +89,19 @@ func (r *ReconciliationRunner) GetOrCreateAffinityGroup( } // The computed affinity group name relevant to this machine. -func GenerateAffinityGroupName(csm infrav1.CloudStackMachine, capiMachine *clusterv1.Machine) (string, error) { +func GenerateAffinityGroupName(csm infrav1.CloudStackMachine, capiMachine *clusterv1.Machine, capiCluster *clusterv1.Cluster) (string, error) { managerOwnerRef := GetManagementOwnerRef(capiMachine) if managerOwnerRef == nil { return "", errors.Errorf("could not find owner UID for %s/%s", csm.Namespace, csm.Name) } titleCaser := cases.Title(language.English) - return fmt.Sprintf("%sAffinity-%s-%s-%s", - titleCaser.String(csm.Spec.Affinity), managerOwnerRef.Name, managerOwnerRef.UID, csm.Spec.FailureDomainName), nil + + // If the machine's owner is KubeadmControlPlane or EtcdadmCluster, then we don't consider the name and UID of the + // owner, since there will only be one of each of those per cluster. + if managerOwnerRef.Kind == "KubeadmControlPlane" || managerOwnerRef.Kind == "EtcdadmCluster" { + return fmt.Sprintf("%s-%s-%sAffinity-%s-%s", + capiCluster.Name, capiCluster.UID, titleCaser.String(csm.Spec.Affinity), managerOwnerRef.Kind, csm.Spec.FailureDomainName), nil + } + return fmt.Sprintf("%s-%s-%sAffinity-%s-%s-%s", + capiCluster.Name, capiCluster.UID, titleCaser.String(csm.Spec.Affinity), managerOwnerRef.Name, managerOwnerRef.UID, csm.Spec.FailureDomainName), nil } diff --git a/controllers/utils/base_reconciler.go b/controllers/utils/base_reconciler.go index 17eb697b..62dea33c 100644 --- a/controllers/utils/base_reconciler.go +++ b/controllers/utils/base_reconciler.go @@ -19,10 +19,11 @@ package utils import ( "context" "fmt" - "k8s.io/client-go/tools/record" "strings" "time" + "k8s.io/client-go/tools/record" + "github.com/go-logr/logr" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -30,7 +31,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util/annotations" @@ -162,7 +163,7 @@ func (r *ReconciliationRunner) Else(fn CloudStackReconcilerMethod) CloudStackRec // GetCAPICluster gets the CAPI cluster the reconciliation subject belongs to. func (r *ReconciliationRunner) GetCAPICluster() (ctrl.Result, error) { r.Log.V(1).Info("Getting CAPI cluster.") - name := r.ReconciliationSubject.GetLabels()[clusterv1.ClusterLabelName] + name := r.ReconciliationSubject.GetLabels()[clusterv1.ClusterNameLabel] if name == "" { r.Log.V(1).Info("Reconciliation Subject is missing cluster label or cluster does not exist. Skipping CAPI Cluster fetch.", "SubjectKind", r.ReconciliationSubject.GetObjectKind().GroupVersionKind().Kind) @@ -184,7 +185,7 @@ func (r *ReconciliationRunner) GetCAPICluster() (ctrl.Result, error) { // GetCSCluster gets the CAPI cluster the reconciliation subject belongs to. func (r *ReconciliationRunner) GetCSCluster() (ctrl.Result, error) { r.Log.V(1).Info("Getting CloudStackCluster cluster.") - name := r.ReconciliationSubject.GetLabels()[clusterv1.ClusterLabelName] + name := r.ReconciliationSubject.GetLabels()[clusterv1.ClusterNameLabel] if name == "" { r.Log.V(1).Info("Reconciliation Subject is missing cluster label or cluster does not exist. Skipping CloudStackCluster fetch.", "SubjectKind", r.ReconciliationSubject.GetObjectKind().GroupVersionKind().Kind) @@ -230,7 +231,8 @@ func (r *ReconciliationRunner) CheckOwnedCRDsForReadiness(gvks ...schema.GroupVe if ready, found, err := unstructured.NestedBool(owned.Object, "status", "ready"); err != nil { return ctrl.Result{}, errors.Wrapf(err, "parsing ready for object %s", owned) } else if !found || !ready { - if name, found, err := unstructured.NestedString(owned.Object, "metadata", "name"); err != nil { + name, found, err := unstructured.NestedString(owned.Object, "metadata", "name") + if err != nil { return ctrl.Result{}, errors.Wrapf(err, "parsing name for object %s", owned) } else if !found { return r.RequeueWithMessage( @@ -238,10 +240,9 @@ func (r *ReconciliationRunner) CheckOwnedCRDsForReadiness(gvks ...schema.GroupVe "Owned object of kind %s with name %s not found, requeuing.", gvk.Kind, owned.GetName())) - } else { - r.Log.Info(fmt.Sprintf("Owned object %s of kind %s not ready, requeuing", name, gvk.Kind)) - return ctrl.Result{RequeueAfter: RequeueTimeout}, nil } + r.Log.Info(fmt.Sprintf("Owned object %s of kind %s not ready, requeuing", name, gvk.Kind)) + return ctrl.Result{RequeueAfter: RequeueTimeout}, nil } } } @@ -311,7 +312,7 @@ func (r *ReconciliationRunner) CheckOwnedObjectsDeleted(gvks ...schema.GroupVers // RequeueIfCloudStackClusterNotReady requeues the reconciliation request if the CloudStackCluster is not ready. func (r *ReconciliationRunner) RequeueIfCloudStackClusterNotReady() (ctrl.Result, error) { - if !r.CSCluster.Status.Ready { + if r.CSCluster.DeletionTimestamp.IsZero() && !r.CSCluster.Status.Ready { r.Log.Info("CloudStackCluster not ready. Requeuing.") return ctrl.Result{RequeueAfter: RequeueTimeout}, nil } @@ -402,7 +403,7 @@ func (r *ReconciliationRunner) RunBaseReconciliationStages() (res ctrl.Result, r r.SetupPatcher, r.GetCAPICluster, r.GetCSCluster, - r.RequeueIfMissingBaseCRs, + r.RunIf(func() bool { return r.ReconciliationSubject.GetDeletionTimestamp().IsZero() }, r.RequeueIfMissingBaseCRs), r.CheckIfPaused} baseStages = append( append(baseStages, r.additionalCommonStages...), @@ -463,6 +464,9 @@ func (r *ReconcilerBase) InitFromMgr(mgr ctrl.Manager, client cloud.Client) { func (r *ReconciliationRunner) GetParent(child client.Object, parent client.Object) CloudStackReconcilerMethod { return func() (ctrl.Result, error) { err := GetOwnerOfKind(r.RequestCtx, r.K8sClient, child, parent) + if err != nil && strings.Contains(err.Error(), "couldn't find owner of kind") { + return r.RequeueWithMessage(err.Error()) + } return ctrl.Result{}, err } } @@ -481,7 +485,7 @@ func (r *ReconciliationRunner) NewChildObjectMeta(name string) metav1.ObjectMeta return metav1.ObjectMeta{ Name: strings.ToLower(name), Namespace: r.Request.Namespace, - Labels: map[string]string{clusterv1.ClusterLabelName: r.CAPICluster.Name}, + Labels: map[string]string{clusterv1.ClusterNameLabel: r.CAPICluster.Name}, OwnerReferences: []metav1.OwnerReference{ *metav1.NewControllerRef(r.ReconciliationSubject, ownerGVK), }, diff --git a/controllers/utils/base_reconciler_test.go b/controllers/utils/base_reconciler_test.go new file mode 100644 index 00000000..3eb0cf52 --- /dev/null +++ b/controllers/utils/base_reconciler_test.go @@ -0,0 +1,209 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils_test + +import ( + "context" + + "github.com/go-logr/logr" + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/cluster-api-provider-cloudstack/controllers/utils" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +// mockConcreteRunner is a minimal implementation of the runner interface +type mockConcreteRunner struct{} + +func (m *mockConcreteRunner) ReconcileDelete() (ctrl.Result, error) { + return ctrl.Result{}, nil +} + +func (m *mockConcreteRunner) Reconcile() (ctrl.Result, error) { + return ctrl.Result{}, nil +} + +var _ = ginkgo.Describe("ReconciliationRunner", func() { + var ( + mockCtrl *gomock.Controller + k8sClient client.Client + scheme *runtime.Scheme + baseRunner *utils.ReconciliationRunner + ctx context.Context + mockRunner *mockConcreteRunner + ) + + ginkgo.BeforeEach(func() { + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) + scheme = runtime.NewScheme() + gomega.Expect(infrav1.AddToScheme(scheme)).To(gomega.Succeed()) + gomega.Expect(clusterv1.AddToScheme(scheme)).To(gomega.Succeed()) + + k8sClient = fake.NewClientBuilder().WithScheme(scheme).Build() + ctx = context.Background() + mockRunner = &mockConcreteRunner{} + + // Create the base reconciler + base := utils.ReconcilerBase{ + K8sClient: k8sClient, + Scheme: scheme, + BaseLogger: logr.Discard(), + Recorder: record.NewFakeRecorder(10), + } + + // Create a reconciliation runner with our mock concrete runner + baseRunner = utils.NewRunner(mockRunner, &infrav1.CloudStackMachine{}, "TestController") + baseRunner.UsingBaseReconciler(base) + baseRunner.WithRequestCtx(ctx) + + // Setup a fake Request + baseRunner.ForRequest(ctrl.Request{ + NamespacedName: client.ObjectKey{ + Namespace: "default", + Name: "test-machine", + }, + }) + }) + + ginkgo.AfterEach(func() { + mockCtrl.Finish() + }) + + ginkgo.Describe("GetParent", func() { + var ( + child *infrav1.CloudStackMachine + ) + + ginkgo.BeforeEach(func() { + dummies.SetDummyVars() + + // Set up child object + child = &infrav1.CloudStackMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-child", + Namespace: "default", + }, + } + }) + + ginkgo.Context("when parent exists and is correctly referenced", func() { + var parent *clusterv1.Machine + ginkgo.BeforeEach(func() { + parent = &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-parent", + Namespace: "default", + }, + } + // Set up owner reference + child.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Machine", + Name: parent.Name, + UID: "test-uid", + }, + } + + // Create the objects in the fake client + gomega.Expect(k8sClient.Create(ctx, parent)).To(gomega.Succeed()) + gomega.Expect(k8sClient.Create(ctx, child)).To(gomega.Succeed()) + }) + + ginkgo.It("should find the parent successfully", func() { + // Create an empty parent object to be filled + parentToFind := &clusterv1.Machine{} + + // Call GetParent + result, err := baseRunner.GetParent(child, parentToFind)() + + // Check results + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(result).To(gomega.Equal(ctrl.Result{})) + gomega.Expect(parentToFind.Name).To(gomega.Equal(parent.Name)) + gomega.Expect(parentToFind.Namespace).To(gomega.Equal(parent.Namespace)) + }) + }) + + ginkgo.Context("when parent doesn't exist", func() { + ginkgo.BeforeEach(func() { + // Set up owner reference to non-existent parent + child.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: clusterv1.GroupVersion.String(), + Kind: "Machine", + Name: "non-existent-parent", + UID: "test-uid", + }, + } + + // Create only the child in the fake client + gomega.Expect(k8sClient.Create(ctx, child)).To(gomega.Succeed()) + }) + + ginkgo.It("should return an error", func() { + // Create an empty parent object to be filled + parentToFind := &clusterv1.Machine{} + + // Call GetParent + _, err := baseRunner.GetParent(child, parentToFind)() + + // Check results + gomega.Expect(err).To(gomega.HaveOccurred()) + gomega.Expect(err.Error()).To(gomega.ContainSubstring("not found")) + }) + }) + + ginkgo.Context("when no owner reference of requested kind exists", func() { + ginkgo.BeforeEach(func() { + // Set up owner reference to different kind + child.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: "different.api/v1", + Kind: "DifferentKind", + Name: "different-name", + UID: "test-uid", + }, + } + + // Create only the child in the fake client + gomega.Expect(k8sClient.Create(ctx, child)).To(gomega.Succeed()) + }) + + ginkgo.It("should requeue with error message", func() { + // Create an empty parent object to be filled + parentToFind := &clusterv1.Machine{} + + // Call GetParent + result, err := baseRunner.GetParent(child, parentToFind)() + + // Check results + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(result.RequeueAfter).To(gomega.Equal(utils.RequeueTimeout)) + }) + }) + }) +}) diff --git a/controllers/utils/failuredomains.go b/controllers/utils/failuredomains.go index dc6ee77d..bf66c259 100644 --- a/controllers/utils/failuredomains.go +++ b/controllers/utils/failuredomains.go @@ -21,7 +21,7 @@ import ( "strings" corev1 "k8s.io/api/core/v1" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" "github.com/pkg/errors" @@ -58,7 +58,7 @@ func (r *ReconciliationRunner) CreateFailureDomains(fdSpecs []infrav1.CloudStack // GetFailureDomains gets CloudStackFailureDomains owned by a CloudStackCluster. func (r *ReconciliationRunner) GetFailureDomains(fds *infrav1.CloudStackFailureDomainList) CloudStackReconcilerMethod { return func() (ctrl.Result, error) { - capiClusterLabel := map[string]string{clusterv1.ClusterLabelName: r.CSCluster.GetLabels()[clusterv1.ClusterLabelName]} + capiClusterLabel := map[string]string{clusterv1.ClusterNameLabel: r.CSCluster.GetLabels()[clusterv1.ClusterNameLabel]} if err := r.K8sClient.List( r.RequestCtx, fds, @@ -147,12 +147,12 @@ func (c *CloudClientImplementation) AsFailureDomainUser(fdSpec *infrav1.CloudSta _ = c.K8sClient.Get(c.RequestCtx, key, clientConfig) var err error - if c.CSClient, err = cloud.NewClientFromK8sSecret(endpointCredentials, clientConfig); err != nil { + if c.CSClient, err = cloud.NewClientFromK8sSecret(endpointCredentials, clientConfig, fdSpec.Project); err != nil { return ctrl.Result{}, errors.Wrapf(err, "parsing ACSEndpoint secret with ref: %v", fdSpec.ACSEndpoint) } if fdSpec.Account != "" { // Set r.CSUser CloudStack Client per Account and Domain. - client, err := c.CSClient.NewClientInDomainAndAccount(fdSpec.Domain, fdSpec.Account) + client, err := c.CSClient.NewClientInDomainAndAccount(fdSpec.Domain, fdSpec.Account, fdSpec.Project) if err != nil { return ctrl.Result{}, err } diff --git a/controllers/utils/isolated_network.go b/controllers/utils/isolated_network.go index 8a92acc0..05073c6a 100644 --- a/controllers/utils/isolated_network.go +++ b/controllers/utils/isolated_network.go @@ -18,27 +18,38 @@ package utils import ( "fmt" + "regexp" "strings" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ctrl "sigs.k8s.io/controller-runtime" ) +var metaNameRegex = regexp.MustCompile(`[^a-z0-9-]+`) + func (r *ReconciliationRunner) IsoNetMetaName(name string) string { - return fmt.Sprintf("%s-%s", r.CSCluster.Name, strings.ToLower(name)) + str := metaNameRegex.ReplaceAllString(fmt.Sprintf("%s-%s", r.CSCluster.Name, strings.ToLower(name)), "-") + return strings.TrimSuffix(str, "-") } // GenerateIsolatedNetwork of the passed name that's owned by the ReconciliationSubject. -func (r *ReconciliationRunner) GenerateIsolatedNetwork(name string, fdNameFunc func() string) CloudStackReconcilerMethod { +func (r *ReconciliationRunner) GenerateIsolatedNetwork(name string, fdNameFunc func() string, network infrav1.Network) CloudStackReconcilerMethod { return func() (ctrl.Result, error) { lowerName := strings.ToLower(name) - metaName := fmt.Sprintf("%s-%s", r.CSCluster.Name, lowerName) + metaName := r.IsoNetMetaName(lowerName) csIsoNet := &infrav1.CloudStackIsolatedNetwork{} csIsoNet.ObjectMeta = r.NewChildObjectMeta(metaName) csIsoNet.Spec.Name = lowerName csIsoNet.Spec.FailureDomainName = fdNameFunc() csIsoNet.Spec.ControlPlaneEndpoint.Host = r.CSCluster.Spec.ControlPlaneEndpoint.Host csIsoNet.Spec.ControlPlaneEndpoint.Port = r.CSCluster.Spec.ControlPlaneEndpoint.Port + csIsoNet.Spec.Gateway = network.Gateway + csIsoNet.Spec.Netmask = network.Netmask + csIsoNet.Spec.Offering = network.Offering + + if network.VPC != nil { + csIsoNet.Spec.VPC = network.VPC + } if err := r.K8sClient.Create(r.RequestCtx, csIsoNet); err != nil && !ContainsAlreadyExistsSubstring(err) { return r.ReturnWrappedError(err, "creating isolated network CRD") diff --git a/controllers/utils/utils.go b/controllers/utils/utils.go index 435e86e1..01a2f986 100644 --- a/controllers/utils/utils.go +++ b/controllers/utils/utils.go @@ -128,10 +128,10 @@ func fetchOwnerRef(refList []meta.OwnerReference, kind string) *meta.OwnerRefere func GetManagementOwnerRef(capiMachine *clusterv1.Machine) *meta.OwnerReference { if util.IsControlPlaneMachine(capiMachine) { return fetchOwnerRef(capiMachine.OwnerReferences, "KubeadmControlPlane") - } else if ref := fetchOwnerRef(capiMachine.OwnerReferences, "MachineSet"); ref != nil { + } else if ref := fetchOwnerRef(capiMachine.OwnerReferences, "EtcdadmCluster"); ref != nil { return ref } - return fetchOwnerRef(capiMachine.OwnerReferences, "EtcdadmCluster") + return fetchOwnerRef(capiMachine.OwnerReferences, "MachineSet") } // GetOwnerOfKind returns the Cluster object owning the current resource of passed kind. diff --git a/controllers/utils/utils_suite_test.go b/controllers/utils/utils_suite_test.go new file mode 100644 index 00000000..4e4db759 --- /dev/null +++ b/controllers/utils/utils_suite_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils_test + +import ( + "testing" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +func TestUtils(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Utils Suite") +} diff --git a/docs/book/src/clustercloudstack/configuration.md b/docs/book/src/clustercloudstack/configuration.md index ffff8330..1c8b0c6a 100644 --- a/docs/book/src/clustercloudstack/configuration.md +++ b/docs/book/src/clustercloudstack/configuration.md @@ -4,8 +4,6 @@ The cluster configuration file can be generated by using [`clusterctl generate c This command actually uses [a template file][template-file] and replaces the values surrounded by `${}` with environment variables. You have to set all required environment variables in advance. The following sections explain some more details about what should be configured. -Note: You can also use [template files][template-file] by manually replacing values in copies of the template file. - ```bash clusterctl generate cluster capi-quickstart \ --kubernetes-version v1.21.3 \ @@ -14,9 +12,25 @@ clusterctl generate cluster capi-quickstart \ > capi-quickstart.yaml ``` -Note: additional template files are provided, offering capabilities beyond the default template file. These can be -utilized via the *clusterctl --flavor* parameter. Additional environment variables are often required by these templates. -See clusterctl documentation for further details about *flavors*. +You can also use [template files][template-file] by manually replacing values in copies of the template file. + + +> **Note** +> +> Additional template files are provided, offering capabilities beyond the default template file. These can be +> utilized via the *clusterctl --flavor* parameter. Additional environment variables are often required by these templates. +> The following flavors are supported as of now: +> - *managed-ssh* +> - *ssh-material* +> - *with-disk-offering* +> - *with-existing-vpc-network* +> - *with-kube-vip* +> +> To check the available variables for a flavor, execute the following command: +> ```bash +> clusterctl generate cluster capi-quickstart --flavor --list-variables +> ``` +> Only one flavor can be specified at a time. See clusterctl documentation for further details about *flavors*. In order to fetch the configuration parameters via the terminal, please install [cmk][cmk-download] and [jq][jq-download] @@ -60,14 +74,114 @@ cmk list zones listall=true | jq '.zone[] | {name, id}' #### Network The network must be declared as an environment variable `CLOUDSTACK_NETWORK_NAME` and is a mandatory parameter. -As of now, only isolated and shared networks are supported. -If the specified network does not exist, a new isolated network will be created. +As of now, only isolated and shared networks are supported. The isolated network can also be part of a VPC. + +If the specified network does not exist, a new isolated network will be created. The newly created network will have a default egress firewall policy that allows all TCP, UDP and ICMP traffic from the cluster to the outside world. If the network is part of a VPC, the VPC will also be created if it does not exist. + +If the offerings are not specified, the default offerings will be used. The list of networks for the specific zone can be fetched using the cmk cli as follows : ``` cmk list networks listall=true zoneid= | jq '.network[] | {name, id, type}' ``` +The list of VPCs for the specific zone can be fetched using the cmk cli as follows : +``` +cmk list vpcs listall=true zoneid= | jq '.vpc[] | {name, id}' +``` + +The user can configure the network offering and VPC offering for the isolated network as follows: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: capc-cluster + namespace: default +spec: + controlPlaneEndpoint: + host: 10.0.58.19 + port: 6443 + failureDomains: + - acsEndpoint: + name: secret1 + namespace: default + name: fd1 + zone: + name: cloudstack-zone + network: + name: cloudstack-network + offering: custom-network-offering + gateway: 10.0.0.1 + netmask: 255.255.255.0 + vpc: + name: cloudstack-vpc + offering: custom-vpc-offering + cidr: 10.0.0.0/16 +``` + +If the network already exists, offering, gateway and netmask will be ignored. +Similarly, if the VPC already exists, offering and cidr will be ignored. + +##### VPC + +If you want to use an existing network inside a VPC, you can specify the flavor as `with-existing-vpc-network` while +generating the cluster configuration file and set the `CLOUDSTACK_VPC_NAME` environment variable to the name of the VPC. +After setting the environment variables, execute the following command to generate the cluster configuration file: + +```bash +clusterctl generate cluster capc-cluster --flavor with-existing-vpc-network > capc-cluster-spec.yaml +``` + +##### Shared or Routed Networks + +For shared or routed networks, the user will need to use [kube-vip][kube-vip] to configure the VIP on the nodes as part of the +KubeadmControlPlane spec. You can specify the flavor as `with-kube-vip` while generating the cluster configuration file. +After setting the environment variables, execute the following command to generate the cluster configuration file: + +```bash +clusterctl generate cluster capc-cluster --flavor with-kube-vip > capc-cluster-spec.yaml +``` + +##### Option for Multiple Networks + +Multiple networks can be specified at each node configuration in CloudStackMachineTemplate. +This is configured under `spec.template.spec.networks`, where you can list one or more networks by name or id, +and optionally assign static IP addresses. + +When defining multiple networks for a VM in CAPC, the first network listed under `spec.template.spec.networks` is treated as +the primary network. This primary network must match the network defined in the failure domain’s zone (`failureDomains[].zone.network`), +either by name or by ID. It is used as the default NIC and is critical for VM boot and cluster communication. + +Any networks listed after the primary are considered extra networks. These extra networks are attached as secondary NICs +on the VM and can be used for purposes such as service segmentation or additional routing. Each network entry, primary or +extra can optionally include a static IP address. If an IP is not specified, CloudStack will dynamically allocate one. + +For example: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: capc-cluster-control-plane + namespace: default +spec: + template: + spec: + offering: + name: Large Instance + networks: + - name: cloudstack-network # (optional) default primary network; must match with network at failureDomains.zone.network.name + ip: 10.1.1.21 # (optional) static IP in the primary network + + # Additional (extra) networks can be specified below. Use either 'name' or 'id', and optionally an 'ip'. + - name: cloudstack-network-2 # (optional) extra network by name + ip: 10.1.1.31 # (optional) static IP in this network + + - id: a1b2c3d4-5678-90ef-gh12-3456789ijklm # (optional) extra network by ID + ip: 10.1.1.41 # (optional) static IP in this network +``` + #### CloudStack Endpoint Credentials Secret (*optional for provided templates when used with provided getting-started process*) A reference to a Kubernetes Secret containing a YAML object containing credentials for accessing a particular CloudStack @@ -109,6 +223,14 @@ The list of Public IPs for the specific zone can be fetched using the cmk cli as cmk list publicipaddresses listall=true zoneid= forvirtualnetwork=true allocatedonly=false | jq '.publicipaddress[] | select(.state == "Free" or .state == "Reserved") | .ipaddress' ``` +> **Note** +> +> When using shared or routed networks, you must configure a Virtual IP (VIP) on the nodes using [kube-vip][kube-vip]. +> You can specify the flavor as `with-kube-vip` while generating the cluster configuration file. The +> `CLUSTER_ENDPOINT_IP` value must be either: +> - An IP address within the shared/routed network range +> - A DNS name pointing to a VIP or a load balancer in front of the control plane nodes + ## Machine Level Configurations These configurations are passed while defining the `CloudStackMachine`. They can differ based on the MachineSet mapped to it. @@ -144,6 +266,22 @@ cmk list templates zoneid= templatefilter=executable | jq '.template[] # Optional Configurations +## Integration with CloudStack CKS + +CAPC cluster can be integrated with CloudStack CKS to provide a comprehensive view of the cluster resources on CloudStack. +The integration can be enabled by setting the `CLOUDSTACK_SYNC_WITH_ACS` environment variable to `true` before +generating the cluster or by setting CloudStackCluster.spec.syncWithACS to `true` in the cluster definition yaml. + +## Project + +CAPC cluster can be deployed within a specific project in CloudStack. +The project name can be specified by adding the `CloudStackCluster.spec.project` field in the yaml specification. + +The list of projects can be fetched using the cmk cli as follows : +``` +cmk list projects listall=true | jq '.project[] | {name, id}' +``` + ## Cluster Level Configurations These configurations are passed while defining the `CloudStackCluster` and apply to the entire cluster. @@ -276,4 +414,5 @@ TODO / Add feature [jq-download]: https://stedolan.github.io/jq/ [prebuilt-images]: http://packages.shapeblue.com/cluster-api-provider-cloudstack/images/ [template-file]: https://github.com/kubernetes-sigs/cluster-api-provider-cloudstack/blob/main/templates/cluster-template.yaml -[failure-domain-api]: https://github.com/kubernetes-sigs/cluster-api-provider-cloudstack/blob/main/api/v1beta2/cloudstackfailuredomain_types.go \ No newline at end of file +[failure-domain-api]: https://github.com/kubernetes-sigs/cluster-api-provider-cloudstack/blob/main/api/v1beta2/cloudstackfailuredomain_types.go +[kube-vip]: https://kube-vip.io/ diff --git a/docs/book/src/development/common.md b/docs/book/src/development/common.md index 043e5e7f..cf1462ff 100644 --- a/docs/book/src/development/common.md +++ b/docs/book/src/development/common.md @@ -33,6 +33,11 @@ # The SSH KeyPair to log into the VM (Optional: you must use clusterctl --flavor *managed-ssh*) export CLOUDSTACK_SSH_KEY_NAME=CAPCKeyPair6 + + # Sync resources created by CAPC in Apache Cloudstack CKS. Default is false. + # Requires setting CAPC_CLOUDSTACKMACHINE_CKS_SYNC=true before initialising the cloudstack provider. + # Or set enable-cloudstack-cks-sync to true in the deployment for capc-controller. + export CLOUDSTACK_SYNC_WITH_ACS=true ``` 2. Generate the CAPC cluster spec yaml file diff --git a/docs/book/src/development/releasing.md b/docs/book/src/development/releasing.md index 0cfbc701..0eb4a74f 100644 --- a/docs/book/src/development/releasing.md +++ b/docs/book/src/development/releasing.md @@ -8,12 +8,14 @@ - [gcloud][gcloud-install] 2. Set up and log in to gcloud by running `gcloud init` - > In order to publish any artifact, you need to be a member of the [k8s-infra-staging-capi-cloudstack][k8s-infra-staging-capi-cloudstack] group +> **Note** +> +> In order to publish any artifact, you need to be a member of the [k8s-infra-staging-capi-cloudstack][k8s-infra-staging-capi-cloudstack] group ## Creating only the docker container If you would just like to build only the docker container and upload it rather than creating a release, you can run the following command : -``` +```bash REGISTRY= IMAGE_NAME= TAG= make docker-build ``` It defaults to `gcr.io/k8s-staging-capi-cloudstack/capi-cloudstack-controller:dev` @@ -21,8 +23,8 @@ It defaults to `gcr.io/k8s-staging-capi-cloudstack/capi-cloudstack-controller:de ## Creating a new release -Run the following command to create the new release artifacts as well as publish them to the upstream gcr.io repository : -``` +Run the following command to create the new release artifacts as well as publish them to the upstream gcr.io repository: +```bash RELEASE_TAG= make release-staging ``` @@ -31,9 +33,14 @@ Create the necessary release in GitHub along with the following artifacts ( foun - infrastructure-components.yaml - cluster-template*.yaml +> **Note** +> +> - The `RELEASE_TAG` should be in the format of `v..`. For example, `v0.6.0` +> - For RC releases, the `RELEASE_TAG` should be in the format of `v..-rc`. For example, `v0.6.0-rc1` +> - Before creating the release, ensure that the `metadata.yaml` file is updated with the latest release information. [docker-install]: https://www.docker.com/ [go]: https://golang.org/doc/install [gcloud-install]: https://cloud.google.com/sdk/docs/install -[k8s-infra-staging-capi-cloudstack]: https://github.com/kubernetes/k8s.io/blob/main/groups/sig-cluster-lifecycle/groups.yaml#L106 \ No newline at end of file +[k8s-infra-staging-capi-cloudstack]: https://github.com/kubernetes/k8s.io/blob/main/groups/sig-cluster-lifecycle/groups.yaml#L106 diff --git a/docs/book/src/development/tilt.md b/docs/book/src/development/tilt.md index fce2a04c..991c9e87 100644 --- a/docs/book/src/development/tilt.md +++ b/docs/book/src/development/tilt.md @@ -35,13 +35,23 @@ Next, create a `tilt-settings.json` file and place it in your local copy of `clu **Example `tilt-settings.json` for CAPC clusters:** +```json +{ + "default_registry": "gcr.io/your-project-name-here", + "provider_repos": ["../cluster-api-provider-cloudstack"], + "enable_providers": ["kubeadm-bootstrap", "kubeadm-control-plane", "cloudstack"] +} +``` + +**Example `tilt-settings.json` for CAPC clusters with experimental feature gate:** + ```json { "default_registry": "gcr.io/your-project-name-here", "provider_repos": ["../cluster-api-provider-cloudstack"], "enable_providers": ["kubeadm-bootstrap", "kubeadm-control-plane", "cloudstack"], "kustomize_substitutions": { - "CLOUDSTACK_B64ENCODED_CREDENTIALS": "RANDOM_STRING==", + "EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION": "true", } } ``` diff --git a/docs/book/src/getting-started.md b/docs/book/src/getting-started.md index 5c10878e..6d20cb4e 100644 --- a/docs/book/src/getting-started.md +++ b/docs/book/src/getting-started.md @@ -49,7 +49,12 @@ Run the following command to turn your cluster into a management cluster and load the Apache CloudStack components into it. - clusterctl init --infrastructure cloudstack + CAPC_CLOUDSTACKMACHINE_CKS_SYNC=true clusterctl init --infrastructure cloudstack + +> Integration of CAPC with CKS is supported for Apache CloudStack version 4.19 and above. +> If you wish to disable syncing of CAPC resources with cloudstack, set the environment variable +> `CAPC_CLOUDSTACKMACHINE_CKS_SYNC=false` before initializing the cloudstack provider. Or set +> `enable-cloudstack-cks-sync` to `false` in the deployment spec for capc-controller-manager. diff --git a/docs/book/src/images/ssh-step-1-public-ip-vpc.png b/docs/book/src/images/ssh-step-1-public-ip-vpc.png new file mode 100644 index 00000000..d5505641 Binary files /dev/null and b/docs/book/src/images/ssh-step-1-public-ip-vpc.png differ diff --git a/docs/book/src/topics/cloudstack-permissions.md b/docs/book/src/topics/cloudstack-permissions.md index 73d20dc9..475522ae 100644 --- a/docs/book/src/topics/cloudstack-permissions.md +++ b/docs/book/src/topics/cloudstack-permissions.md @@ -1,6 +1,6 @@ # CloudStack Permissions for CAPC -The account that CAPC runs under must minimally be a Domain Admin type account with a role offering the following permissions +The account that CAPC runs under must minimally be a User type account with a role offering the following permissions * assignToLoadBalancerRule * associateIpAddress @@ -19,7 +19,6 @@ The account that CAPC runs under must minimally be a Domain Admin type account w * listAccounts * listAffinityGroups * listDiskOfferings -* listDomains * listLoadBalancerRuleInstances * listLoadBalancerRules * listNetworkOfferings @@ -39,4 +38,6 @@ The account that CAPC runs under must minimally be a Domain Admin type account w * stopVirtualMachine * updateVMAffinityGroup +> Note: If the user doesn't have permissions to expunge the VM, it will be left in a destroyed state. The user will need to manually expunge the VM. + This permission set has been verified to successfully run the CAPC E2E test suite (Oct 11, 2022). \ No newline at end of file diff --git a/docs/book/src/topics/index.md b/docs/book/src/topics/index.md index 8a13ee1f..7711c91d 100644 --- a/docs/book/src/topics/index.md +++ b/docs/book/src/topics/index.md @@ -9,7 +9,6 @@ ## TODO : -- Registering External Clusters in CloudStack - Data Disks - Diff between CKS and CAPC - E2E Tests diff --git a/docs/book/src/topics/ssh-access.md b/docs/book/src/topics/ssh-access.md index 4d318ee4..0470f530 100644 --- a/docs/book/src/topics/ssh-access.md +++ b/docs/book/src/topics/ssh-access.md @@ -15,10 +15,12 @@ To see how to pass a key pair to the node, checkout the [keypair configuration]( In order to access the nodes, the following changes need to be made in Apache CloudStack 1. Select the Public IP belonging to the network via which users can access the VM. -Either use the exiting IP or acquire a new IP. +Either use the exiting IP or acquire a new IP. If the network is part of a VPC, the public IP will be the one assigned to the VPC. ![Alt text](../images/ssh-step-1-public-ip.png) + ![Alt text](../images/ssh-step-1-public-ip-vpc.png) + 2. Add a firewall rule to allow access on the desired port ![Alt text](../images/ssh-step-2-firewall.png) @@ -36,7 +38,7 @@ Either use the exiting IP or acquire a new IP. ## SSH Into The Node -Now access the node via the Public IP using the corresponding SSH Keypair +Now access the node via the Public IP using the corresponding SSH Keypair. The username is `ubuntu` for ubuntu images and `cloud-user` for rockylinux8 images. ``` $ ssh ubuntu@10.0.53.123 -i path/to/key diff --git a/go.mod b/go.mod index 075d13b1..8f24170f 100644 --- a/go.mod +++ b/go.mod @@ -1,107 +1,110 @@ module sigs.k8s.io/cluster-api-provider-cloudstack -go 1.19 +go 1.23.0 + +toolchain go1.23.2 require ( - github.com/ReneKroon/ttlcache v1.7.0 - github.com/apache/cloudstack-go/v2 v2.13.0 - github.com/go-logr/logr v1.2.3 - github.com/golang/mock v1.6.0 + github.com/apache/cloudstack-go/v2 v2.17.1 + github.com/go-logr/logr v1.4.2 github.com/hashicorp/go-multierror v1.1.1 - github.com/onsi/ginkgo/v2 v2.4.0 - github.com/onsi/gomega v1.24.0 + github.com/jellydator/ttlcache/v3 v3.2.0 + github.com/onsi/ginkgo/v2 v2.22.2 + github.com/onsi/gomega v1.36.2 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.19.1 github.com/smallfish/simpleyaml v0.1.0 github.com/spf13/pflag v1.0.5 - golang.org/x/text v0.9.0 + go.uber.org/mock v0.5.1 + golang.org/x/text v0.23.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.25.3 - k8s.io/apimachinery v0.25.3 - k8s.io/client-go v0.25.3 - k8s.io/klog/v2 v2.80.1 - k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 - sigs.k8s.io/cluster-api v1.2.12 - sigs.k8s.io/controller-runtime v0.12.3 + k8s.io/api v0.31.3 + k8s.io/apimachinery v0.31.3 + k8s.io/client-go v0.31.3 + k8s.io/component-base v0.31.3 + k8s.io/klog/v2 v2.130.1 + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + sigs.k8s.io/cluster-api v1.9.6 + sigs.k8s.io/controller-runtime v0.19.6 ) require ( - cloud.google.com/go/compute v1.7.0 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.27 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect + github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/coredns/caddy v1.1.1 // indirect - github.com/coredns/corefile-migration v1.0.20 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/gobuffalo/flect v0.3.0 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gobuffalo/flect v1.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.2.0 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/imdario/mergo v0.3.13 // 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 - github.com/kr/pretty v0.3.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // 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 - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/stretchr/testify v1.8.1 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.23.0 // indirect - go4.org/intern v0.0.0-20220617035311-6925f38cc365 // indirect - golang.org/x/crypto v0.3.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/oauth2 v0.1.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/term v0.7.0 // indirect - golang.org/x/time v0.2.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221107162902-2d387536bcdd // indirect - google.golang.org/protobuf v1.28.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.28.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.1 // indirect + google.golang.org/protobuf v1.36.1 // 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 - inet.af/netaddr v0.0.0-20220811202034-502d2d690317 // indirect - k8s.io/apiextensions-apiserver v0.25.3 // indirect - k8s.io/cluster-bootstrap v0.25.3 // indirect - k8s.io/component-base v0.25.3 // indirect - k8s.io/kube-openapi v0.0.0-20221106113015-f73e7dbcfe29 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/apiextensions-apiserver v0.31.3 // indirect + k8s.io/apiserver v0.31.3 // indirect + k8s.io/cluster-bootstrap v0.31.3 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) replace github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.0.0 // Indirect upgrade to address https://github.com/advisories/GHSA-w73w-5m7g-f7qc - -replace sigs.k8s.io/cluster-api/test => sigs.k8s.io/cluster-api/test v1.2.12 - -replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.2.12 diff --git a/go.sum b/go.sum index e54396df..60502b2f 100644 --- a/go.sum +++ b/go.sum @@ -1,1059 +1,354 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ReneKroon/ttlcache v1.7.0 h1:8BkjFfrzVFXyrqnMtezAaJ6AHPSsVV10m6w28N/Fgkk= -github.com/ReneKroon/ttlcache v1.7.0/go.mod h1:8BGGzdumrIjWxdRx8zpK6L3oGMWvIXdvB2GD1cfvd+I= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= -github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/apache/cloudstack-go/v2 v2.13.0 h1:t0uj7QxQpnzD/LSTP6a4w2NTuZXisxIM/mIDNkF44lc= -github.com/apache/cloudstack-go/v2 v2.13.0/go.mod h1:aosD8Svfu5nhH5Sp4zcsVV1hT5UGt3mTgRXM8YqTKe0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +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/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +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/apache/cloudstack-go/v2 v2.17.1 h1:XD0bGDOv+MCavXJfc/qxILgJh+cHJbudpqQ1FzA2sDI= +github.com/apache/cloudstack-go/v2 v2.17.1/go.mod h1:p/YBUwIEkQN6CQxFhw8Ff0wzf1MY0qRRRuGYNbcb1F8= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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.20 h1:MdOkT6F3ehju/n9tgxlGct8XAajOX2vN+wG7To4BWSI= -github.com/coredns/corefile-migration v1.0.20/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/coredns/corefile-migration v1.0.25 h1:/XexFhM8FFlFLTS/zKNEWgIZ8Gl5GaWrHsMarGj/PRQ= +github.com/coredns/corefile-migration v1.0.25/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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +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/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= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk= -github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +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.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +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 v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE= +github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= 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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= 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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +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/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smallfish/simpleyaml v0.1.0 h1:5uAZdLAiHxS9cmzkOxg7lH0dILXKTD7uRZbAhyHmyU0= github.com/smallfish/simpleyaml v0.1.0/go.mod h1:gU3WdNn44dQVAbVHD2SrSqKKCvmzFApWD2UURhgEj1M= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= -go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= -go4.org/intern v0.0.0-20220617035311-6925f38cc365 h1:t9hFvR102YlOqU0fQn1wgwhNvSbHGBbbJxX9JKfU3l0= -go4.org/intern v0.0.0-20220617035311-6925f38cc365/go.mod h1:WXRv3p7T6gzt0CcJm43AAKdKVZmcQbwwC7EwquU5BZU= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w= +go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4= +go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw= +go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w= +go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8= +go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg= +go.etcd.io/etcd/client/v3 v3.5.17 h1:o48sINNeWz5+pjy/Z0+HKpj/xSnBkuVhVvXkjEXbqZY= +go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo= +go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M= +go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0= +go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA= +go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw= +go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok= +go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +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.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs= +go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= -golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= -golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20221107162902-2d387536bcdd h1:1eV6KuDTxraYYsYGWksp1thEGP+8dtX/TINL9h+ppiI= -google.golang.org/genproto v0.0.0-20221107162902-2d387536bcdd/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.1 h1:toSN4j5/Xju+HVovfaY5g1YZVuJeHzQZhP8eJ0L0f1I= +google.golang.org/grpc v1.65.1/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -inet.af/netaddr v0.0.0-20220811202034-502d2d690317 h1:U2fwK6P2EqmopP/hFLTOAjWTki0qgd4GMJn5X8wOleU= -inet.af/netaddr v0.0.0-20220811202034-502d2d690317/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= -k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= -k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= -k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= -k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI= -k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= -k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= -k8s.io/cluster-bootstrap v0.25.3 h1:Rwi4SLbpsRYa4n+dPlvyl+VpZH6idHzH5izRQrrFW1s= -k8s.io/cluster-bootstrap v0.25.3/go.mod h1:C5NZX+WE7v/hEyUfMj2sjQfKHsOVAYLrSFLtPspVljM= -k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= -k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221106113015-f73e7dbcfe29 h1:tya1+VIpw4iOtih5oB7B7nMINohQcyedmlTzdDwSHPA= -k8s.io/kube-openapi v0.0.0-20221106113015-f73e7dbcfe29/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= -k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/cluster-api v1.2.12 h1:/1iFgWUR3Z+KZU9B4PJegmlQQo6uzQXBZNgOk63SLu4= -sigs.k8s.io/cluster-api v1.2.12/go.mod h1:9+fLUuyRsKediKJYrbsyFj6Bmk59oopXpzj3g6/REM8= -sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio= -sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= +k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= +k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= +k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= +k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= +k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= +k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= +k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/cluster-bootstrap v0.31.3 h1:O1Yxk1bLaxZvmQCXLaJjj5iJD+lVMfJdRUuKgbUHPlA= +k8s.io/cluster-bootstrap v0.31.3/go.mod h1:TI6TCsQQB4FfcryWgNO3SLXSKWBqHjx4DfyqSFwixj8= +k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= +k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +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-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/cluster-api v1.9.6 h1:2jZ434qC0bzrPQzmRDm4/b0PZWVMnOocoCjsAonQN5Q= +sigs.k8s.io/cluster-api v1.9.6/go.mod h1:DyqyZ4jRvKGKewDRn1Q4OCHaVjsdTogymbO6mrgHEDI= +sigs.k8s.io/controller-runtime v0.19.6 h1:fuq53qTLQ7aJTA7aNsklNnu7eQtSFqJUomOyM+phPLk= +sigs.k8s.io/controller-runtime v0.19.6/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/cleanup-affinity-groups.sh b/hack/cleanup-affinity-groups.sh new file mode 100755 index 00000000..abb118d0 --- /dev/null +++ b/hack/cleanup-affinity-groups.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +# +# Copyright 2023 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires: jq, kubectl, cmk (get https://github.com/apache/cloudstack-cloudmonkey/releases/tag/6.4.0-rc1 or later) +# +# About: this tool helps to remove CloudStack affinity groups from CAPC +# management cluster which are not assigned to any instances. +# +# Usage and help: +# chmod +x cleanup-affinity-groups.sh +# ./cleanup-affinity-groups.sh -h + +set -o errexit +set -o nounset +set -o pipefail + +# script params +DRY_RUN=false +VERBOSE=false + +# k8s params +NAMESPACE=default +KUBECONFIG=$HOME/.kube/config + +# cmk params +CS_URL= +CS_APIKEY= +CS_SECRETKEY= + +debug() { + if [[ "$VERBOSE" == "true" ]]; then + echo -e "[debug] $@" + fi +} + +_kubectl() { + KUBECONFIG=$KUBECONFIG kubectl -n $NAMESPACE -o json $@ +} + +_cmk() { + cmk -u $CS_URL -k $CS_APIKEY -s $CS_SECRETKEY -o json $@ +} + +get_affinity_groups() { + _kubectl get cloudstackaffinitygroups | jq -r '.items[].metadata.name' +} + +get_cluster() { + local affinitygroup=$1 + _kubectl get cloudstackaffinitygroup $affinitygroup | jq -r '.metadata.labels."cluster.x-k8s.io/cluster-name"' +} + +get_cluster_credentials() { + local cluster=$1 + _kubectl get cloudstackcluster $cluster | jq -r '.spec.failureDomains[].acsEndpoint.name' | uniq +} + +setup_acs_credentials() { + local credential=$1 + CS_URL=$(_kubectl get secret $credential | jq -r '.data."api-url"' | base64 -D) + CS_APIKEY=$(_kubectl get secret $credential | jq -r '.data."api-key"' | base64 -D) + CS_SECRETKEY=$(_kubectl get secret $credential | jq -r '.data."secret-key"' | base64 -D) + debug "Using CloudStack Control Plane URL: $CS_URL and CloudStack Account: $(_cmk list users | jq -r '.user[] | .account + " and User: " + .username')" +} + +main() { + local ags=$(get_affinity_groups) + debug "Affinity groups in the namespace $NAMESPACE:\n$ags" + for ag in $ags; do + echo -e "\033[0;32m[info]\033[0m Checking CloudStack Affinity Group: $ag" + local cluster=$(get_cluster $ag) + for credential in $(get_cluster_credentials $cluster); do + setup_acs_credentials $credential + local ag_uuid=$(_kubectl get cloudstackaffinitygroup $ag | jq -r '.spec.id') + local ag_instances=$(_cmk list affinitygroups id=$ag_uuid | jq -r '.affinitygroup[0].virtualmachineIds') + if [[ "$ag_instances" == "null" ]]; then + echo -e "\033[0;35m[info]\033[0m Found Affinity Group ($ag_uuid) with no instances assigned: $ag" + if [[ "$DRY_RUN" == "false" ]]; then + kubectl -n $NAMESPACE delete cloudstackaffinitygroup $ag + echo -e "\033[0;31m[info]\033[0m Affinity Group ($ag_uuid) $ag has been removed" + else + echo -e "\033[0;35m[info]\033[0m [dryrun] Affinity Group ($ag_uuid) $ag has been removed" + fi + fi + done + done +} + +help() { + echo "Usage: $0 [-d|k|h|v]" + echo + echo "This cleanup tool helps to remove CloudStack affinity groups from CAPC" + echo "management cluster which are not assigned to any instances, which may" + echo "have been created as a side effect of other operations. This tool checks" + echo "all the cloudstackaffinitygroups using its CloudStack cluster specific" + echo "credential(s) and uses cmk to check if the affinity group have no" + echo "instances assigned. In dry-run, it outputs such affinity groups" + echo "otherwise it deletes them." + echo + echo "Options:" + echo "-d Runs the tools in dry-run mode" + echo "-k Pass custom kube config, default: \$HOME/.kube/config" + echo "-n Kubernetes namespace, default: default" + echo "-h Print this help" + echo "-v Verbose mode" + echo +} + +while getopts ":dk:vn:h" option; do + case $option in + d) + DRY_RUN=true;; + k) + KUBECONFIG=$OPTARG;; + v) + VERBOSE=true;; + n) + NAMESPACE=$OPTARG;; + h) + help + exit;; + \?) + echo "Error: Invalid option provided, please see help docs" + help + exit;; + esac +done + +if ! command -v jq &> /dev/null +then + echo "[error] jq could not be found, please install first" + exit 1 +fi + +if ! command -v kubectl &> /dev/null +then + echo "[error] kubectl could not be found, please install first" + exit 1 +fi + +if ! command -v cmk &> /dev/null +then + echo "[error] cmk could not be found, please install https://github.com/apache/cloudstack-cloudmonkey/releases/tag/6.4.0-rc1 or newer" + exit 1 +fi + +debug "[options] DRY_RUN=$DRY_RUN" +debug "[options] VERBOSE=$VERBOSE" +debug "[options] NAMESPACE=$NAMESPACE" +debug "[options] KUBECONFIG=$KUBECONFIG" +main diff --git a/hack/ensure-golangci-lint.sh b/hack/ensure-golangci-lint.sh deleted file mode 100755 index 7e9c9896..00000000 --- a/hack/ensure-golangci-lint.sh +++ /dev/null @@ -1,429 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2023 The Kubernetes Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# NOTE: This script is copied from https://raw.githubusercontent.com/golangci/golangci-lint/main/install.sh. - -set -e - -usage() { - this=$1 - cat </dev/null -} -echoerr() { - echo "$@" 1>&2 -} -log_prefix() { - echo "$0" -} -_logp=6 -log_set_priority() { - _logp="$1" -} -log_priority() { - if test -z "$1"; then - echo "$_logp" - return - fi - [ "$1" -le "$_logp" ] -} -log_tag() { - case $1 in - 0) echo "emerg" ;; - 1) echo "alert" ;; - 2) echo "crit" ;; - 3) echo "err" ;; - 4) echo "warning" ;; - 5) echo "notice" ;; - 6) echo "info" ;; - 7) echo "debug" ;; - *) echo "$1" ;; - esac -} -log_debug() { - log_priority 7 || return 0 - echoerr "$(log_prefix)" "$(log_tag 7)" "$@" -} -log_info() { - log_priority 6 || return 0 - echoerr "$(log_prefix)" "$(log_tag 6)" "$@" -} -log_err() { - log_priority 3 || return 0 - echoerr "$(log_prefix)" "$(log_tag 3)" "$@" -} -log_crit() { - log_priority 2 || return 0 - echoerr "$(log_prefix)" "$(log_tag 2)" "$@" -} -uname_os() { - os=$(uname -s | tr '[:upper:]' '[:lower:]') - case "$os" in - msys*) os="windows" ;; - mingw*) os="windows" ;; - cygwin*) os="windows" ;; - win*) os="windows" ;; - esac - echo "$os" -} -uname_arch() { - arch=$(uname -m) - case $arch in - x86_64) arch="amd64" ;; - x86) arch="386" ;; - i686) arch="386" ;; - i386) arch="386" ;; - aarch64) arch="arm64" ;; - armv5*) arch="armv5" ;; - armv6*) arch="armv6" ;; - armv7*) arch="armv7" ;; - esac - echo ${arch} -} -uname_os_check() { - os=$(uname_os) - case "$os" in - darwin) return 0 ;; - dragonfly) return 0 ;; - freebsd) return 0 ;; - linux) return 0 ;; - android) return 0 ;; - nacl) return 0 ;; - netbsd) return 0 ;; - openbsd) return 0 ;; - plan9) return 0 ;; - solaris) return 0 ;; - windows) return 0 ;; - esac - log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value." - return 1 -} -uname_arch_check() { - arch=$(uname_arch) - case "$arch" in - 386) return 0 ;; - amd64) return 0 ;; - arm64) return 0 ;; - armv5) return 0 ;; - armv6) return 0 ;; - armv7) return 0 ;; - ppc64) return 0 ;; - ppc64le) return 0 ;; - mips) return 0 ;; - mipsle) return 0 ;; - mips64) return 0 ;; - mips64le) return 0 ;; - s390x) return 0 ;; - riscv64) return 0 ;; - amd64p32) return 0 ;; - esac - log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value." - return 1 -} -untar() { - tarball=$1 - case "${tarball}" in - *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;; - *.tar) tar --no-same-owner -xf "${tarball}" ;; - *.zip) unzip "${tarball}" ;; - *) - log_err "untar unknown archive format for ${tarball}" - return 1 - ;; - esac -} -http_download_curl() { - local_file=$1 - source_url=$2 - header=$3 - if [ -z "$header" ]; then - code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") - else - code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") - fi - if [ "$code" != "200" ]; then - log_debug "http_download_curl received HTTP status $code" - return 1 - fi - return 0 -} -http_download_wget() { - local_file=$1 - source_url=$2 - header=$3 - if [ -z "$header" ]; then - wget -q -O "$local_file" "$source_url" - else - wget -q --header "$header" -O "$local_file" "$source_url" - fi -} -http_download() { - log_debug "http_download $2" - if is_command curl; then - http_download_curl "$@" - return - elif is_command wget; then - http_download_wget "$@" - return - fi - log_crit "http_download unable to find wget or curl" - return 1 -} -http_copy() { - tmp=$(mktemp) - http_download "${tmp}" "$1" "$2" || return 1 - body=$(cat "$tmp") - rm -f "${tmp}" - echo "$body" -} -github_release() { - owner_repo=$1 - version=$2 - test -z "$version" && version="latest" - giturl="https://github.com/$%7Bowner_repo%7D/releases/$%7Bversion%7D" - json=$(http_copy "$giturl" "Accept:application/json") - test -z "$json" && return 1 - version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') - test -z "$version" && return 1 - echo "$version" -} -hash_sha256() { - TARGET=${1:-/dev/stdin} - if is_command gsha256sum; then - hash=$(gsha256sum "$TARGET") || return 1 - echo "$hash" | cut -d ' ' -f 1 - elif is_command sha256sum; then - hash=$(sha256sum "$TARGET") || return 1 - echo "$hash" | cut -d ' ' -f 1 - elif is_command shasum; then - hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 - echo "$hash" | cut -d ' ' -f 1 - elif is_command openssl; then - hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 - echo "$hash" | cut -d ' ' -f a - else - log_crit "hash_sha256 unable to find command to compute sha-256 hash" - return 1 - fi -} -hash_sha256_verify() { - TARGET=$1 - checksums=$2 - if [ -z "$checksums" ]; then - log_err "hash_sha256_verify checksum file not specified in arg2" - return 1 - fi - BASENAME=${TARGET##*/} - want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) - if [ -z "$want" ]; then - log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" - return 1 - fi - got=$(hash_sha256 "$TARGET") - if [ "$want" != "$got" ]; then - log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" - return 1 - fi -} -cat /dev/null < 1 { // handle via creating a new error. return errors.New("count bad") - } else { - group.Name = affinityGroup.Name - group.Type = affinityGroup.Type - return nil } + group.Name = affinityGroup.Name + group.Type = affinityGroup.Type + return nil } if group.Name != "" { - affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByName(group.Name) + affinityGroup, count, err := c.cs.AffinityGroup.GetAffinityGroupByName(group.Name, cloudstack.WithProject(c.user.Project.ID)) if err != nil { - // handle via multierr c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err } else if count > 1 { // handle via creating a new error. return errors.New("count bad") - } else { - group.ID = affinityGroup.Id - group.Type = affinityGroup.Type - return nil } + group.ID = affinityGroup.Id + group.Type = affinityGroup.Type + return nil } return errors.Errorf(`could not fetch AffinityGroup by name "%s" or id "%s"`, group.Name, group.ID) } @@ -78,6 +75,7 @@ func (c *client) GetOrCreateAffinityGroup(group *AffinityGroup) (retErr error) { if err := c.FetchAffinityGroup(group); err != nil { // Group not found? p := c.cs.AffinityGroup.NewCreateAffinityGroupParams(group.Name, group.Type) p.SetName(group.Name) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) resp, err := c.cs.AffinityGroup.CreateAffinityGroup(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -92,6 +90,7 @@ func (c *client) DeleteAffinityGroup(group *AffinityGroup) (retErr error) { p := c.cs.AffinityGroup.NewDeleteAffinityGroupParams() setIfNotEmpty(group.ID, p.SetId) setIfNotEmpty(group.Name, p.SetName) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) _, retErr = c.cs.AffinityGroup.DeleteAffinityGroup(p) c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) return retErr @@ -101,18 +100,18 @@ type affinityGroups []AffinityGroup func (c *client) getCurrentAffinityGroups(csMachine *infrav1.CloudStackMachine) (affinityGroups, error) { // Start by fetching VM details which includes an array of currently associated affinity groups. - if virtM, count, err := c.cs.VirtualMachine.GetVirtualMachineByID(*csMachine.Spec.InstanceID); err != nil { + virtM, count, err := c.cs.VirtualMachine.GetVirtualMachineByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.user.Project.ID)) + if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return nil, err } else if count > 1 { return nil, errors.Errorf("found more than one VM for ID: %s", *csMachine.Spec.InstanceID) - } else { - groups := make([]AffinityGroup, 0, len(virtM.Affinitygroup)) - for _, v := range virtM.Affinitygroup { - groups = append(groups, AffinityGroup{Name: v.Name, Type: v.Type, ID: v.Id}) - } - return groups, nil } + groups := make([]AffinityGroup, 0, len(virtM.Affinitygroup)) + for _, v := range virtM.Affinitygroup { + groups = append(groups, AffinityGroup{Name: v.Name, Type: v.Type, ID: v.Id}) + } + return groups, nil } func (ags *affinityGroups) toArrayOfIDs() []string { diff --git a/pkg/cloud/affinity_groups_test.go b/pkg/cloud/affinity_groups_test.go index 13dda7ee..1d8d2223 100644 --- a/pkg/cloud/affinity_groups_test.go +++ b/pkg/cloud/affinity_groups_test.go @@ -20,14 +20,14 @@ import ( "errors" "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" ) -var _ = Describe("AffinityGroup Unit Tests", func() { +var _ = ginkgo.Describe("AffinityGroup Unit Tests", func() { const ( errorMessage = "Fake Error" ) @@ -41,149 +41,149 @@ var _ = Describe("AffinityGroup Unit Tests", func() { client cloud.Client ) - BeforeEach(func() { + ginkgo.BeforeEach(func() { // Setup new mock services. - mockCtrl = gomock.NewController(GinkgoT()) + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = cloudstack.NewMockClient(mockCtrl) ags = mockClient.AffinityGroup.(*cloudstack.MockAffinityGroupServiceIface) vms = mockClient.VirtualMachine.(*cloudstack.MockVirtualMachineServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) dummies.SetDummyVars() }) - AfterEach(func() { + ginkgo.AfterEach(func() { mockCtrl.Finish() }) - Context("Fetch or Create Affinity group", func() { - It("fetches an affinity group by Name", func() { + ginkgo.Context("Fetch or Create Affinity group", func() { + ginkgo.It("fetches an affinity group by Name", func() { dummies.AffinityGroup.ID = "" // Force name fetching. - ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(&cloudstack.AffinityGroup{}, 1, nil) + ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 1, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("fetches an affinity group by ID", func() { - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(&cloudstack.AffinityGroup{}, 1, nil) + ginkgo.It("fetches an affinity group by ID", func() { + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 1, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("creates an affinity group", func() { + ginkgo.It("creates an affinity group", func() { // dummies.SetDummyDomainAndAccount() // dummies.SetDummyDomainID() - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(nil, -1, fakeError) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(nil, -1, fakeError) ags.EXPECT().NewCreateAffinityGroupParams(dummies.AffinityGroup.Name, dummies.AffinityGroup.Type). Return(&cloudstack.CreateAffinityGroupParams{}) - ags.EXPECT().CreateAffinityGroup(ParamMatch(And(NameEquals(dummies.AffinityGroup.Name)))). + ags.EXPECT().CreateAffinityGroup(ParamMatch(gomega.And(NameEquals(dummies.AffinityGroup.Name)))). Return(&cloudstack.CreateAffinityGroupResponse{}, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("creates an affinity group if Name provided returns more than one affinity group", func() { + ginkgo.It("creates an affinity group if Name provided returns more than one affinity group", func() { dummies.AffinityGroup.ID = "" // Force name fetching. agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(&cloudstack.AffinityGroup{}, 2, nil) + ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 2, nil) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("creates an affinity group if getting affinity group by name fails", func() { + ginkgo.It("creates an affinity group if getting affinity group by name fails", func() { dummies.AffinityGroup.ID = "" // Force name fetching. agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name).Return(nil, -1, fakeError) + ags.EXPECT().GetAffinityGroupByName(dummies.AffinityGroup.Name, gomock.Any()).Return(nil, -1, fakeError) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("creates an affinity group if ID provided returns more than one affinity group", func() { + ginkgo.It("creates an affinity group if ID provided returns more than one affinity group", func() { agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(&cloudstack.AffinityGroup{}, 2, nil) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(&cloudstack.AffinityGroup{}, 2, nil) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("creates an affinity group if getting affinity group by ID fails", func() { + ginkgo.It("creates an affinity group if getting affinity group by ID fails", func() { agp := &cloudstack.CreateAffinityGroupParams{} - ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID).Return(nil, -1, fakeError) + ags.EXPECT().GetAffinityGroupByID(dummies.AffinityGroup.ID, gomock.Any()).Return(nil, -1, fakeError) ags.EXPECT().NewCreateAffinityGroupParams(gomock.Any(), gomock.Any()).Return(agp) ags.EXPECT().CreateAffinityGroup(agp).Return(&cloudstack.CreateAffinityGroupResponse{}, nil) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) }) - Context("Delete Affinity group in CloudStack", func() { - It("delete affinity group", func() { + ginkgo.Context("Delete Affinity group in CloudStack", func() { + ginkgo.It("delete affinity group", func() { agp := &cloudstack.DeleteAffinityGroupParams{} ags.EXPECT().NewDeleteAffinityGroupParams().Return(agp) ags.EXPECT().DeleteAffinityGroup(agp).Return(&cloudstack.DeleteAffinityGroupResponse{}, nil) - Ω(client.DeleteAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.DeleteAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) }) }) - Context("AffinityGroup Integ Tests", Label("integ"), func() { + ginkgo.Context("AffinityGroup Integ Tests", ginkgo.Label("integ"), func() { - BeforeEach(func() { + ginkgo.BeforeEach(func() { client = realCloudClient dummies.AffinityGroup.ID = "" // Force name fetching. }) - It("Associates an affinity group.", func() { - Ω(client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(Succeed()) + ginkgo.It("Associates an affinity group.", func() { + gomega.Ω(client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(gomega.Succeed()) dummies.CSMachine1.Spec.DiskOffering.Name = "" - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "", - )).Should(Succeed()) + )).Should(gomega.Succeed()) - Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) - Ω(client.AssociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.GetOrCreateAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) + gomega.Ω(client.AssociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(gomega.Succeed()) // Make the created VM go away quickly by force stopping it. p := realCSClient.VirtualMachine.NewStopVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID) p.SetForced(true) _, err := realCSClient.VirtualMachine.StopVirtualMachine(p) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) }) - It("Creates and deletes an affinity group.", func() { - Ω(client.DeleteAffinityGroup(dummies.AffinityGroup)).Should(Succeed()) - Ω(client.FetchAffinityGroup(dummies.AffinityGroup)).ShouldNot(Succeed()) + ginkgo.It("Creates and deletes an affinity group.", func() { + gomega.Ω(client.DeleteAffinityGroup(dummies.AffinityGroup)).Should(gomega.Succeed()) + gomega.Ω(client.FetchAffinityGroup(dummies.AffinityGroup)).ShouldNot(gomega.Succeed()) }) }) - It("Associate affinity group", func() { + ginkgo.It("Associate affinity group", func() { uagp := &cloudstack.UpdateVMAffinityGroupParams{} vmp := &cloudstack.StartVirtualMachineParams{} - vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.VirtualMachine{}, 1, nil) + vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(&cloudstack.VirtualMachine{}, 1, nil) ags.EXPECT().NewUpdateVMAffinityGroupParams(*dummies.CSMachine1.Spec.InstanceID).Return(uagp) vms.EXPECT().NewStopVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.StopVirtualMachineParams{}) vms.EXPECT().StopVirtualMachine(&cloudstack.StopVirtualMachineParams{}).Return(&cloudstack.StopVirtualMachineResponse{State: "Stopping"}, nil) ags.EXPECT().UpdateVMAffinityGroup(uagp).Return(&cloudstack.UpdateVMAffinityGroupResponse{}, nil) vms.EXPECT().NewStartVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID).Return(vmp) vms.EXPECT().StartVirtualMachine(vmp).Return(&cloudstack.StartVirtualMachineResponse{}, nil) - Ω(client.AssociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.AssociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(gomega.Succeed()) }) - It("Disassociate affinity group", func() { + ginkgo.It("Disassociate affinity group", func() { uagp := &cloudstack.UpdateVMAffinityGroupParams{} vmp := &cloudstack.StartVirtualMachineParams{} - vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.VirtualMachine{}, 1, nil) + vms.EXPECT().GetVirtualMachineByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(&cloudstack.VirtualMachine{}, 1, nil) ags.EXPECT().NewUpdateVMAffinityGroupParams(*dummies.CSMachine1.Spec.InstanceID).Return(uagp) vms.EXPECT().NewStopVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID).Return(&cloudstack.StopVirtualMachineParams{}) vms.EXPECT().StopVirtualMachine(&cloudstack.StopVirtualMachineParams{}).Return(&cloudstack.StopVirtualMachineResponse{State: "Stopping"}, nil) ags.EXPECT().UpdateVMAffinityGroup(uagp).Return(&cloudstack.UpdateVMAffinityGroupResponse{}, nil) vms.EXPECT().NewStartVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID).Return(vmp) vms.EXPECT().StartVirtualMachine(vmp).Return(&cloudstack.StartVirtualMachineResponse{}, nil) - Ω(client.DisassociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(Succeed()) + gomega.Ω(client.DisassociateAffinityGroup(dummies.CSMachine1, *dummies.AffinityGroup)).Should(gomega.Succeed()) }) }) diff --git a/pkg/cloud/cks_cluster.go b/pkg/cloud/cks_cluster.go new file mode 100644 index 00000000..acf870e0 --- /dev/null +++ b/pkg/cloud/cks_cluster.go @@ -0,0 +1,144 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloud + +import ( + "fmt" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/pkg/errors" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +type ClusterIface interface { + GetOrCreateCksCluster(*clusterv1.Cluster, *infrav1.CloudStackCluster, *infrav1.CloudStackFailureDomainSpec) error + DeleteCksCluster(*infrav1.CloudStackCluster) error + AddVMToCksCluster(*infrav1.CloudStackCluster, *infrav1.CloudStackMachine) error + RemoveVMFromCksCluster(*infrav1.CloudStackCluster, *infrav1.CloudStackMachine) error +} + +type ClustertypeSetter interface { + SetClustertype(string) +} + +func withExternalManaged() cloudstack.OptionFunc { + return func(_ *cloudstack.CloudStackClient, p interface{}) error { + ps, ok := p.(ClustertypeSetter) + if !ok { + return errors.New("invalid params type") + } + ps.SetClustertype("ExternalManaged") + return nil + } +} + +func (c *client) GetOrCreateCksCluster(cluster *clusterv1.Cluster, csCluster *infrav1.CloudStackCluster, fd *infrav1.CloudStackFailureDomainSpec) error { + // Get cluster + if csCluster.Status.CloudStackClusterID != "" { + externalManagedCluster, count, err := c.cs.Kubernetes.GetKubernetesClusterByID(csCluster.Status.CloudStackClusterID, withExternalManaged(), cloudstack.WithProject(c.user.Project.ID)) + if err != nil { + return err + } else if count > 0 { + csCluster.Status.CloudStackClusterID = externalManagedCluster.Id + return nil + } + } + + // Check if a cluster exists with the same name + clusterName := fmt.Sprintf("%s - %s - %s", cluster.GetName(), csCluster.GetName(), csCluster.GetUID()) + externalManagedCluster, count, err := c.cs.Kubernetes.GetKubernetesClusterByName(clusterName, withExternalManaged(), cloudstack.WithProject(c.user.Project.ID)) + if err != nil && !strings.Contains(err.Error(), "No match found for ") { + return err + } + if count > 0 { + csCluster.Status.CloudStackClusterID = externalManagedCluster.Id + } else if err == nil || (err != nil && strings.Contains(err.Error(), "No match found for ")) { + // Create cluster + accountName := csCluster.Spec.FailureDomains[0].Account + if accountName == "" { + userParams := c.cs.User.NewGetUserParams(c.config.APIKey) + user, err := c.cs.User.GetUser(userParams) + if err != nil && !strings.Contains(err.Error(), "does not exist or is not available for the account") { + return err + } else if err == nil { + accountName = user.Account + } + } + // NewCreateKubernetesClusterParams(description string, kubernetesversionid string, name string, serviceofferingid string, size int64, zoneid string) *CreateKubernetesClusterParams + params := c.cs.Kubernetes.NewCreateKubernetesClusterParams(fmt.Sprintf("%s managed by CAPC", clusterName), "", clusterName, "", 0, fd.Zone.ID) + + setIfNotEmpty(c.user.Project.ID, params.SetProjectid) + setIfNotEmpty(accountName, params.SetAccount) + setIfNotEmpty(c.user.Domain.ID, params.SetDomainid) + setIfNotEmpty(fd.Zone.Network.ID, params.SetNetworkid) + setIfNotEmpty(csCluster.Spec.ControlPlaneEndpoint.Host, params.SetExternalloadbalanceripaddress) + params.ResetKubernetesversionid() + params.ResetServiceofferingid() + params.SetClustertype("ExternalManaged") + + cloudStackCKSCluster, err := c.cs.Kubernetes.CreateKubernetesCluster(params) + if err != nil { + return err + } + csCluster.Status.CloudStackClusterID = cloudStackCKSCluster.Id + } + return nil +} + +func (c *client) DeleteCksCluster(csCluster *infrav1.CloudStackCluster) error { + if csCluster.Status.CloudStackClusterID != "" { + csCksCluster, count, err := c.cs.Kubernetes.GetKubernetesClusterByID( + csCluster.Status.CloudStackClusterID, withExternalManaged(), cloudstack.WithProject(c.user.Project.ID)) + if err != nil && strings.Contains(err.Error(), " not found") { + return nil + } + if count != 0 { + params := c.cs.Kubernetes.NewDeleteKubernetesClusterParams(csCksCluster.Id) + _, err = c.cs.Kubernetes.DeleteKubernetesCluster(params) + if err != nil { + return err + } + } + csCluster.Status.CloudStackClusterID = "" + } + return nil +} + +func (c *client) AddVMToCksCluster(csCluster *infrav1.CloudStackCluster, csMachine *infrav1.CloudStackMachine) error { + if csCluster.Status.CloudStackClusterID != "" { + params := c.cs.Kubernetes.NewAddVirtualMachinesToKubernetesClusterParams(csCluster.Status.CloudStackClusterID, []string{*csMachine.Spec.InstanceID}) + if csMachine.Labels != nil { + _, ok := csMachine.Labels[clusterv1.MachineControlPlaneLabel] + params.SetIscontrolnode(ok) + } + + _, err := c.cs.Kubernetes.AddVirtualMachinesToKubernetesCluster(params) + return err + } + return nil +} + +func (c *client) RemoveVMFromCksCluster(csCluster *infrav1.CloudStackCluster, csMachine *infrav1.CloudStackMachine) error { + if csCluster.Status.CloudStackClusterID != "" { + params := c.cs.Kubernetes.NewRemoveVirtualMachinesFromKubernetesClusterParams(csCluster.Status.CloudStackClusterID, []string{*csMachine.Spec.InstanceID}) + _, err := c.cs.Kubernetes.RemoveVirtualMachinesFromKubernetesCluster(params) + return err + } + return nil +} diff --git a/pkg/cloud/client.go b/pkg/cloud/client.go index 0f743025..979fd820 100644 --- a/pkg/cloud/client.go +++ b/pkg/cloud/client.go @@ -29,14 +29,15 @@ import ( "gopkg.in/yaml.v3" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/metrics" - "github.com/ReneKroon/ttlcache" "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/jellydator/ttlcache/v3" "github.com/pkg/errors" ) //go:generate ../../hack/tools/bin/mockgen -destination=../mocks/mock_client.go -package=mocks sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud Client type Client interface { + ClusterIface VMIface NetworkIface AffinityGroupIface @@ -44,7 +45,8 @@ type Client interface { ZoneIFace IsoNetworkIface UserCredIFace - NewClientInDomainAndAccount(string, string) (Client, error) + VPCIface + NewClientInDomainAndAccount(string, string, string) (Client, error) } // cloud-config ini structure. @@ -59,6 +61,7 @@ type client struct { cs *cloudstack.CloudStackClient csAsync *cloudstack.CloudStackClient config Config + user *User customMetrics metrics.ACSCustomMetrics } @@ -70,9 +73,12 @@ type SecretConfig struct { StringData Config `yaml:"stringData"` } -var clientCache *ttlcache.Cache +var clientCache *ttlcache.Cache[string, *client] var cacheMutex sync.Mutex +var NewAsyncClient = cloudstack.NewAsyncClient +var NewClient = cloudstack.NewClient + const ClientConfigMapName = "capc-client-config" const ClientConfigMapNamespace = "capc-system" const ClientCacheTTLKey = "client-cache-ttl" @@ -97,7 +103,7 @@ func UnmarshalAllSecretConfigs(in []byte, out *[]SecretConfig) error { } // NewClientFromK8sSecret returns a client from a k8s secret -func NewClientFromK8sSecret(endpointSecret *corev1.Secret, clientConfig *corev1.ConfigMap) (Client, error) { +func NewClientFromK8sSecret(endpointSecret *corev1.Secret, clientConfig *corev1.ConfigMap, project string) (Client, error) { endpointSecretStrings := map[string]string{} for k, v := range endpointSecret.Data { endpointSecretStrings[k] = string(v) @@ -106,11 +112,11 @@ func NewClientFromK8sSecret(endpointSecret *corev1.Secret, clientConfig *corev1. if err != nil { return nil, err } - return NewClientFromBytesConfig(bytes, clientConfig) + return NewClientFromBytesConfig(bytes, clientConfig, project) } // NewClientFromBytesConfig returns a client from a bytes array that unmarshals to a yaml config. -func NewClientFromBytesConfig(conf []byte, clientConfig *corev1.ConfigMap) (Client, error) { +func NewClientFromBytesConfig(conf []byte, clientConfig *corev1.ConfigMap, project string) (Client, error) { r := bytes.NewReader(conf) dec := yaml.NewDecoder(r) var config Config @@ -118,7 +124,7 @@ func NewClientFromBytesConfig(conf []byte, clientConfig *corev1.ConfigMap) (Clie return nil, err } - return NewClientFromConf(config, clientConfig) + return NewClientFromConf(config, clientConfig, project) } // NewClientFromYamlPath returns a client from a yaml config at path. @@ -142,11 +148,11 @@ func NewClientFromYamlPath(confPath string, secretName string) (Client, error) { return nil, errors.Errorf("config with secret name %s not found", secretName) } - return NewClientFromConf(conf, nil) + return NewClientFromConf(conf, nil, "") } // NewClientFromConf creates a new Cloud Client form a map of strings to strings. -func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, error) { +func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap, project string) (Client, error) { cacheMutex.Lock() defer cacheMutex.Unlock() @@ -154,9 +160,9 @@ func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, err clientCache = newClientCache(clientConfig) } - clientCacheKey := generateClientCacheKey(conf) - if client, exists := clientCache.Get(clientCacheKey); exists { - return client.(Client), nil + clientCacheKey := generateClientCacheKey(conf, project) + if item := clientCache.Get(clientCacheKey); item != nil { + return item.Value(), nil } verifySSL := true @@ -168,19 +174,45 @@ func NewClientFromConf(conf Config, clientConfig *corev1.ConfigMap) (Client, err // a client returned from NewClient works in an asynchronous way. Dive into the constructor definition // comments for more details c := &client{config: conf} - c.cs = cloudstack.NewAsyncClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL) - c.csAsync = cloudstack.NewClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL) + c.cs = NewClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL) + c.csAsync = NewAsyncClient(conf.APIUrl, conf.APIKey, conf.SecretKey, verifySSL) c.customMetrics = metrics.NewCustomMetrics() - clientCache.Set(clientCacheKey, c) + + p := c.cs.User.NewListUsersParams() + userResponse, err := c.cs.User.ListUsers(p) + if err != nil { + return c, err + } + user := &User{ + ID: userResponse.Users[0].Id, + Account: Account{ + Name: userResponse.Users[0].Account, + Domain: Domain{ + ID: userResponse.Users[0].Domainid, + }, + }, + Project: Project{ + Name: project, + }, + } + if found, err := c.GetUserWithKeys(user); err != nil { + return nil, err + } else if !found { + return nil, errors.Errorf( + "could not find sufficient user (with API keys) in domain/account %s/%s", userResponse.Users[0].Domain, userResponse.Users[0].Account) + } + c.user = user + clientCache.Set(clientCacheKey, c, ttlcache.DefaultTTL) return c, nil } // NewClientInDomainAndAccount returns a new client in the specified domain and account. -func (c *client) NewClientInDomainAndAccount(domain string, account string) (Client, error) { +func (c *client) NewClientInDomainAndAccount(domain string, account string, project string) (Client, error) { user := &User{} user.Account.Domain.Path = domain user.Account.Name = account + user.Project.Name = project if found, err := c.GetUserWithKeys(user); err != nil { return nil, err } else if !found { @@ -189,27 +221,51 @@ func (c *client) NewClientInDomainAndAccount(domain string, account string) (Cli } c.config.APIKey = user.APIKey c.config.SecretKey = user.SecretKey + c.user = user - return NewClientFromConf(c.config, nil) + return NewClientFromConf(c.config, nil, project) } -// NewClientFromCSAPIClient creates a client from a CloudStack-Go API client. Mostly used for testing. -func NewClientFromCSAPIClient(cs *cloudstack.CloudStackClient) Client { - c := &client{cs: cs, csAsync: cs, customMetrics: metrics.NewCustomMetrics()} +// NewClientFromCSAPIClient creates a client from a CloudStack-Go API client. Used only for testing. +func NewClientFromCSAPIClient(cs *cloudstack.CloudStackClient, user *User) Client { + if user == nil { + user = &User{ + Account: Account{ + Domain: Domain{ + CPUAvailable: "Unlimited", + MemoryAvailable: "Unlimited", + VMAvailable: "Unlimited", + }, + CPUAvailable: "Unlimited", + MemoryAvailable: "Unlimited", + VMAvailable: "Unlimited", + }, + } + } + c := &client{ + cs: cs, + csAsync: cs, + customMetrics: metrics.NewCustomMetrics(), + user: user, + } return c } // generateClientCacheKey generates a cache key from a Config -func generateClientCacheKey(conf Config) string { - return fmt.Sprintf("%+v", conf) +func generateClientCacheKey(conf Config, project string) string { + return fmt.Sprintf("%s-%+v", project, conf) } // newClientCache returns a new instance of client cache -func newClientCache(clientConfig *corev1.ConfigMap) *ttlcache.Cache { - clientCache := ttlcache.NewCache() - clientCache.SetTTL(GetClientCacheTTL(clientConfig)) - clientCache.SkipTtlExtensionOnHit(false) - return clientCache +func newClientCache(clientConfig *corev1.ConfigMap) *ttlcache.Cache[string, *client] { + cache := ttlcache.New[string, *client]( + ttlcache.WithTTL[string, *client](GetClientCacheTTL(clientConfig)), + ttlcache.WithDisableTouchOnHit[string, *client](), + ) + + go cache.Start() // starts automatic expired item deletion + + return cache } // GetClientCacheTTL returns a client cache TTL duration from the passed config map diff --git a/pkg/cloud/client_test.go b/pkg/cloud/client_test.go index edc65735..3c4fbfc7 100644 --- a/pkg/cloud/client_test.go +++ b/pkg/cloud/client_test.go @@ -22,9 +22,12 @@ import ( corev1 "k8s.io/api/core/v1" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta1" "sigs.k8s.io/cluster-api-provider-cloudstack/test/helpers" ) @@ -34,122 +37,158 @@ type Global struct { VerifySSL bool `ini:"verify-ssl"` } -var _ = Describe("Client", func() { - - var () - - BeforeEach(func() { +var _ = ginkgo.Describe("Client", func() { + + var ( + mockCtrl *gomock.Controller + mockClient *cloudstack.CloudStackClient + us *cloudstack.MockUserServiceIface + ds *cloudstack.MockDomainServiceIface + as *cloudstack.MockAccountServiceIface + ) + + ginkgo.BeforeEach(func() { + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) + mockClient = cloudstack.NewMockClient(mockCtrl) + us = mockClient.User.(*cloudstack.MockUserServiceIface) + ds = mockClient.Domain.(*cloudstack.MockDomainServiceIface) + as = mockClient.Account.(*cloudstack.MockAccountServiceIface) }) - AfterEach(func() { + ginkgo.AfterEach(func() { }) - Context("When fetching a YAML config.", func() { - It("Handles the positive case.", func() { + ginkgo.Context("When fetching a YAML config.", func() { + ginkgo.It("Handles the positive case.", func() { // This test fixture is useful for development, but the actual method of parsing is confinded to the client's // new client method. The parsing used here is more of a schema, and we don't need to test another library's // abilities to parse said schema. - Skip("Dev test suite.") + ginkgo.Skip("Dev test suite.") // Create a real cloud client. var connectionErr error _, connectionErr = helpers.NewCSClient() - Ω(connectionErr).ShouldNot(HaveOccurred()) + gomega.Ω(connectionErr).ShouldNot(gomega.HaveOccurred()) _, connectionErr = cloud.NewClientFromYamlPath(os.Getenv("REPO_ROOT")+"/cloud-config.yaml", "myendpoint") - Ω(connectionErr).ShouldNot(HaveOccurred()) + gomega.Ω(connectionErr).ShouldNot(gomega.HaveOccurred()) }) }) - Context("GetClientCacheTTL", func() { - It("Returns the default TTL when a nil is passed", func() { + ginkgo.Context("GetClientCacheTTL", func() { + ginkgo.It("Returns the default TTL when a nil is passed", func() { result := cloud.GetClientCacheTTL(nil) - Ω(result).Should(Equal(cloud.DefaultClientCacheTTL)) + gomega.Ω(result).Should(gomega.Equal(cloud.DefaultClientCacheTTL)) }) - It("Returns the default TTL when an empty config map is passed", func() { + ginkgo.It("Returns the default TTL when an empty config map is passed", func() { clientConfig := &corev1.ConfigMap{} result := cloud.GetClientCacheTTL(clientConfig) - Ω(result).Should(Equal(cloud.DefaultClientCacheTTL)) + gomega.Ω(result).Should(gomega.Equal(cloud.DefaultClientCacheTTL)) }) - It("Returns the default TTL when the TTL key does not exist", func() { + ginkgo.It("Returns the default TTL when the TTL key does not exist", func() { clientConfig := &corev1.ConfigMap{} clientConfig.Data = map[string]string{} clientConfig.Data[cloud.ClientCacheTTLKey+"XXXX"] = "1m5s" result := cloud.GetClientCacheTTL(clientConfig) - Ω(result).Should(Equal(cloud.DefaultClientCacheTTL)) + gomega.Ω(result).Should(gomega.Equal(cloud.DefaultClientCacheTTL)) }) - It("Returns the default TTL when the TTL value is invalid", func() { + ginkgo.It("Returns the default TTL when the TTL value is invalid", func() { clientConfig := &corev1.ConfigMap{} clientConfig.Data = map[string]string{} clientConfig.Data[cloud.ClientCacheTTLKey] = "5mXXX" result := cloud.GetClientCacheTTL(clientConfig) - Ω(result).Should(Equal(cloud.DefaultClientCacheTTL)) + gomega.Ω(result).Should(gomega.Equal(cloud.DefaultClientCacheTTL)) }) - It("Returns the TTL from the input clientConfig map", func() { + ginkgo.It("Returns the TTL from the input clientConfig map", func() { clientConfig := &corev1.ConfigMap{} clientConfig.Data = map[string]string{} clientConfig.Data[cloud.ClientCacheTTLKey] = "5m10s" expected, _ := time.ParseDuration("5m10s") result := cloud.GetClientCacheTTL(clientConfig) - Ω(result).Should(Equal(expected)) + gomega.Ω(result).Should(gomega.Equal(expected)) }) }) - Context("NewClientFromConf", func() { + ginkgo.Context("NewClientFromConf", func() { clientConfig := &corev1.ConfigMap{} - - BeforeEach(func() { + cloud.NewAsyncClient = func(_, _, _ string, _ bool, _ ...cloudstack.ClientOption) *cloudstack.CloudStackClient { + return mockClient + } + cloud.NewClient = func(_, _, _ string, _ bool, _ ...cloudstack.ClientOption) *cloudstack.CloudStackClient { + return mockClient + } + + ginkgo.BeforeEach(func() { clientConfig.Data = map[string]string{} clientConfig.Data[cloud.ClientCacheTTLKey] = "100ms" + fakeListParams := &cloudstack.ListUsersParams{} + fakeUser := &cloudstack.User{ + Id: dummies.UserID, + Account: dummies.AccountName, + Domain: dummies.DomainName, + } + us.EXPECT().NewListUsersParams().Return(fakeListParams).AnyTimes() + us.EXPECT().ListUsers(fakeListParams).Return(&cloudstack.ListUsersResponse{ + Count: 1, Users: []*cloudstack.User{fakeUser}, + }, nil).AnyTimes() + + dsp := &cloudstack.ListDomainsParams{} + ds.EXPECT().NewListDomainsParams().Return(dsp).AnyTimes() + ds.EXPECT().ListDomains(dsp).Return(&cloudstack.ListDomainsResponse{Count: 1, Domains: []*cloudstack.Domain{{ + Id: dummies.DomainID, + Name: dummies.DomainName, + Path: dummies.DomainPath, + }}}, nil).AnyTimes() + + asp := &cloudstack.ListAccountsParams{} + as.EXPECT().NewListAccountsParams().Return(asp).AnyTimes() + as.EXPECT().ListAccounts(asp).Return(&cloudstack.ListAccountsResponse{Count: 1, Accounts: []*cloudstack.Account{{ + Id: dummies.AccountID, + Name: dummies.AccountName, + }}}, nil).AnyTimes() + ukp := &cloudstack.GetUserKeysParams{} + us.EXPECT().NewGetUserKeysParams(gomock.Any()).Return(ukp).AnyTimes() + us.EXPECT().GetUserKeys(ukp).Return(&cloudstack.GetUserKeysResponse{ + Apikey: dummies.Apikey, + Secretkey: dummies.SecretKey, + }, nil).AnyTimes() + }) - It("Returns a new client", func() { + ginkgo.It("Returns a new client", func() { config := cloud.Config{ APIUrl: "http://1.1.1.1/", } - result, err := cloud.NewClientFromConf(config, clientConfig) - Ω(err).ShouldNot(HaveOccurred()) - Ω(result).ShouldNot(BeNil()) + result, err := cloud.NewClientFromConf(config, clientConfig, "") + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(result).ShouldNot(gomega.BeNil()) }) - It("Returns a new client for a different config", func() { + ginkgo.It("Returns a new client for a different config", func() { config1 := cloud.Config{ APIUrl: "http://2.2.2.2/", } config2 := cloud.Config{ APIUrl: "http://3.3.3.3/", } - result1, _ := cloud.NewClientFromConf(config1, clientConfig) - result2, _ := cloud.NewClientFromConf(config2, clientConfig) - Ω(result1).ShouldNot(Equal(result2)) + result1, _ := cloud.NewClientFromConf(config1, clientConfig, "") + result2, _ := cloud.NewClientFromConf(config2, clientConfig, "") + gomega.Ω(result1).ShouldNot(gomega.Equal(result2)) }) - It("Returns a cached client for the same config", func() { + ginkgo.It("Returns a cached client for the same config", func() { config1 := cloud.Config{ APIUrl: "http://4.4.4.4/", } config2 := cloud.Config{ APIUrl: "http://4.4.4.4/", } - result1, _ := cloud.NewClientFromConf(config1, clientConfig) - result2, _ := cloud.NewClientFromConf(config2, clientConfig) - Ω(result1).Should(Equal(result2)) - }) - - It("Returns a new client after cache expiration", func() { - config1 := cloud.Config{ - APIUrl: "http://5.5.5.5/", - } - config2 := cloud.Config{ - APIUrl: "http://5.5.5.5/", - } - result1, _ := cloud.NewClientFromConf(config1, clientConfig) - time.Sleep(150 * time.Millisecond) - result2, _ := cloud.NewClientFromConf(config2, clientConfig) - Ω(result1).ShouldNot(Equal(result2)) + result1, _ := cloud.NewClientFromConf(config1, clientConfig, "") + result2, _ := cloud.NewClientFromConf(config2, clientConfig, "") + gomega.Ω(result1).Should(gomega.Equal(result2)) }) }) }) diff --git a/pkg/cloud/cloud_suite_test.go b/pkg/cloud/cloud_suite_test.go index dcc119a3..ffa0e59c 100644 --- a/pkg/cloud/cloud_suite_test.go +++ b/pkg/cloud/cloud_suite_test.go @@ -22,11 +22,11 @@ import ( "testing" "github.com/apache/cloudstack-go/v2/cloudstack" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/util/uuid" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/test/helpers" ) @@ -39,23 +39,23 @@ var ( ) func TestCloud(t *testing.T) { - RegisterFailHandler(Fail) - BeforeSuite(func() { - suiteConfig, _ := GinkgoConfiguration() + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.BeforeSuite(func() { + suiteConfig, _ := ginkgo.GinkgoConfiguration() if !strings.Contains(suiteConfig.LabelFilter, "!integ") { // Skip if integ tests are filtered out. // Create a real cloud client. var connectionErr error realCSClient, connectionErr = helpers.NewCSClient() - Ω(connectionErr).ShouldNot(HaveOccurred()) + gomega.Ω(connectionErr).ShouldNot(gomega.HaveOccurred()) repoRoot := os.Getenv("REPO_ROOT") realCloudClient, connectionErr = cloud.NewClientFromYamlPath( repoRoot+"/cloud-config.yaml", "myendpoint") - Ω(connectionErr).ShouldNot(HaveOccurred()) + gomega.Ω(connectionErr).ShouldNot(gomega.HaveOccurred()) // Create a real CloudStack client. realCSClient, connectionErr = helpers.NewCSClient() - Ω(connectionErr).ShouldNot(HaveOccurred()) + gomega.Ω(connectionErr).ShouldNot(gomega.HaveOccurred()) // Create a new account and user to run tests that use a real ACS instance. uid := string(uuid.NewUUID()) @@ -63,35 +63,35 @@ func TestCloud(t *testing.T) { Name: "TestAccount-" + uid, Domain: cloud.Domain{Name: "TestDomain-" + uid, Path: "ROOT/TestDomain-" + uid}} newUser := cloud.User{Account: newAccount} - Ω(helpers.GetOrCreateUserWithKey(realCSClient, &newUser)).Should(Succeed()) + gomega.Ω(helpers.GetOrCreateUserWithKey(realCSClient, &newUser)).Should(gomega.Succeed()) testDomainPath = newAccount.Domain.Path - Ω(newUser.APIKey).ShouldNot(BeEmpty()) + gomega.Ω(newUser.APIKey).ShouldNot(gomega.BeEmpty()) // Switch to test account user. realCloudClient, connectionErr = realCloudClient.NewClientInDomainAndAccount( - newAccount.Domain.Name, newAccount.Name) - Ω(connectionErr).ShouldNot(HaveOccurred()) + newAccount.Domain.Name, newAccount.Name, "") + gomega.Ω(connectionErr).ShouldNot(gomega.HaveOccurred()) } }) - AfterSuite(func() { + ginkgo.AfterSuite(func() { if realCSClient != nil { // Check for nil in case the before suite setup failed. // Delete created domain. id, err, found := helpers.GetDomainByPath(realCSClient, testDomainPath) - Ω(err).ShouldNot(HaveOccurred()) - Ω(found).Should(BeTrue()) - Ω(helpers.DeleteDomain(realCSClient, id)).Should(Succeed()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(found).Should(gomega.BeTrue()) + gomega.Ω(helpers.DeleteDomain(realCSClient, id)).Should(gomega.Succeed()) } }) - RunSpecs(t, "Cloud Suite") + ginkgo.RunSpecs(t, "Cloud Suite") } // FetchIntegTestResources runs through basic CloudStack Client setup methods needed to test others. func FetchIntegTestResources() { - Ω(realCloudClient.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(Succeed()) - Ω(dummies.CSFailureDomain1.Spec.Zone.ID).ShouldNot(BeEmpty()) + gomega.Ω(realCloudClient.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(gomega.Succeed()) + gomega.Ω(dummies.CSFailureDomain1.Spec.Zone.ID).ShouldNot(gomega.BeEmpty()) dummies.CSMachine1.Spec.DiskOffering.Name = "" dummies.CSCluster.Spec.ControlPlaneEndpoint.Host = "" - Ω(realCloudClient.GetOrCreateIsolatedNetwork( - dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(realCloudClient.GetOrCreateIsolatedNetwork( + dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) } diff --git a/pkg/cloud/helpers_test.go b/pkg/cloud/helpers_test.go index 78fcdcf2..2d8de10c 100644 --- a/pkg/cloud/helpers_test.go +++ b/pkg/cloud/helpers_test.go @@ -23,9 +23,9 @@ import ( "io" "reflect" - "github.com/golang/mock/gomock" - . "github.com/onsi/gomega" + gomega "github.com/onsi/gomega" "github.com/onsi/gomega/types" + "go.uber.org/mock/gomock" ) // This matcher is used to make gomega matching compatible with gomock parameter matching. @@ -48,7 +48,7 @@ func (p paramMatcher) String() string { } func (p paramMatcher) Matches(x interface{}) (retVal bool) { - return Ω(x).Should(p.matcher) + return gomega.Ω(x).Should(p.matcher) } // This generates translating matchers. @@ -61,16 +61,16 @@ func (p paramMatcher) Matches(x interface{}) (retVal bool) { // // DomainIDEquals = FieldMatcherGenerator("GetDomainid") // p := &CreateNewSomethingParams{Domainid: "FakeDomainID"} -// Ω(p).DomainIDEquals("FakeDomainID") +// gomega.Ω(p).DomainIDEquals("FakeDomainID") func FieldMatcherGenerator(fetchFunc string) func(string) types.GomegaMatcher { return func(expected string) types.GomegaMatcher { - return WithTransform( + return gomega.WithTransform( func(x interface{}) string { meth := reflect.ValueOf(x).MethodByName(fetchFunc) fmt.Println(meth.Call(nil)[0]) return meth.Call(nil)[0].String() - }, Equal(expected)) + }, gomega.Equal(expected)) } } diff --git a/pkg/cloud/instance.go b/pkg/cloud/instance.go index b4abf16e..ad6a7cdc 100644 --- a/pkg/cloud/instance.go +++ b/pkg/cloud/instance.go @@ -19,18 +19,19 @@ package cloud import ( "encoding/base64" "fmt" + "net" + "strconv" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/hashicorp/go-multierror" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ) type VMIface interface { @@ -41,10 +42,18 @@ type VMIface interface { // Set infrastructure spec and status from the CloudStack API's virtual machine metrics type. func setMachineDataFromVMMetrics(vmResponse *cloudstack.VirtualMachinesMetric, csMachine *infrav1.CloudStackMachine) { - csMachine.Spec.ProviderID = pointer.String(fmt.Sprintf("cloudstack:///%s", vmResponse.Id)) + csMachine.Spec.ProviderID = ptr.To(fmt.Sprintf("cloudstack:///%s", vmResponse.Id)) // InstanceID is later used as required parameter to destroy VM. - csMachine.Spec.InstanceID = pointer.String(vmResponse.Id) - csMachine.Status.Addresses = []corev1.NodeAddress{{Type: corev1.NodeInternalIP, Address: vmResponse.Ipaddress}} + csMachine.Spec.InstanceID = ptr.To(vmResponse.Id) + csMachine.Status.Addresses = []corev1.NodeAddress{} + for _, nic := range vmResponse.Nic { + if nic.Ipaddress != "" { + csMachine.Status.Addresses = append(csMachine.Status.Addresses, corev1.NodeAddress{ + Type: corev1.NodeInternalIP, + Address: nic.Ipaddress, + }) + } + } newInstanceState := vmResponse.State if newInstanceState != csMachine.Status.InstanceState || (newInstanceState != "" && csMachine.Status.InstanceStateLastUpdated.IsZero()) { csMachine.Status.InstanceState = newInstanceState @@ -57,7 +66,7 @@ func setMachineDataFromVMMetrics(vmResponse *cloudstack.VirtualMachinesMetric, c func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) error { // Attempt to fetch by ID. if csMachine.Spec.InstanceID != nil { - vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByID(*csMachine.Spec.InstanceID) + vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByID(*csMachine.Spec.InstanceID, cloudstack.WithProject(c.user.Project.ID)) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err @@ -71,7 +80,7 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) // Attempt fetch by name. if csMachine.Name != "" { - vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByName(csMachine.Name) // add opts usage + vmResp, count, err := c.cs.VirtualMachine.GetVirtualMachinesMetricByName(csMachine.Name, cloudstack.WithProject(c.user.Project.ID)) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match") { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err @@ -85,66 +94,63 @@ func (c *client) ResolveVMInstanceDetails(csMachine *infrav1.CloudStackMachine) return errors.New("no match found") } -func (c *client) ResolveServiceOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (offeringID string, retErr error) { +func (c *client) ResolveServiceOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (offering cloudstack.ServiceOffering, retErr error) { if len(csMachine.Spec.Offering.ID) > 0 { - csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByID(csMachine.Spec.Offering.ID) + csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByID(csMachine.Spec.Offering.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return "", multierror.Append(retErr, errors.Wrapf( - err, "could not get Service Offering by ID %s", csMachine.Spec.Offering.ID)) + return cloudstack.ServiceOffering{}, errors.Wrapf( + err, "could not get Service Offering by ID %s", csMachine.Spec.Offering.ID) } else if count != 1 { - return "", multierror.Append(retErr, errors.Errorf( - "expected 1 Service Offering with UUID %s, but got %d", csMachine.Spec.Offering.ID, count)) + return *csOffering, errors.Errorf( + "expected 1 Service Offering with UUID %s, but got %d", csMachine.Spec.Offering.ID, count) } if len(csMachine.Spec.Offering.Name) > 0 && csMachine.Spec.Offering.Name != csOffering.Name { - return "", multierror.Append(retErr, errors.Errorf( - "offering name %s does not match name %s returned using UUID %s", csMachine.Spec.Offering.Name, csOffering.Name, csMachine.Spec.Offering.ID)) + return *csOffering, errors.Errorf( + "offering name %s does not match name %s returned using UUID %s", csMachine.Spec.Offering.Name, csOffering.Name, csMachine.Spec.Offering.ID) } - return csMachine.Spec.Offering.ID, nil + return *csOffering, nil } - offeringID, count, err := c.cs.ServiceOffering.GetServiceOfferingID(csMachine.Spec.Offering.Name, cloudstack.WithZone(zoneID)) + csOffering, count, err := c.cs.ServiceOffering.GetServiceOfferingByName(csMachine.Spec.Offering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return "", multierror.Append(retErr, errors.Wrapf( - err, "could not get Service Offering ID from %s in zone %s", csMachine.Spec.Offering.Name, zoneID)) + return cloudstack.ServiceOffering{}, errors.Wrapf( + err, "could not get Service Offering ID from %s in zone %s", csMachine.Spec.Offering.Name, zoneID) } else if count != 1 { - return "", multierror.Append(retErr, errors.Errorf( - "expected 1 Service Offering with name %s in zone %s, but got %d", csMachine.Spec.Offering.Name, zoneID, count)) + return *csOffering, errors.Errorf( + "expected 1 Service Offering with name %s in zone %s, but got %d", csMachine.Spec.Offering.Name, zoneID, count) } - return offeringID, nil + return *csOffering, nil } func (c *client) ResolveTemplate( - csCluster *infrav1.CloudStackCluster, csMachine *infrav1.CloudStackMachine, zoneID string, ) (templateID string, retErr error) { if len(csMachine.Spec.Template.ID) > 0 { - csTemplate, count, err := c.cs.Template.GetTemplateByID(csMachine.Spec.Template.ID, "executable") + csTemplate, count, err := c.cs.Template.GetTemplateByID(csMachine.Spec.Template.ID, "executable", cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return "", multierror.Append(retErr, errors.Wrapf( - err, "could not get Template by ID %s", csMachine.Spec.Template.ID)) + return "", errors.Wrapf(err, "could not get Template by ID %s", csMachine.Spec.Template.ID) } else if count != 1 { - return "", multierror.Append(retErr, errors.Errorf( - "expected 1 Template with UUID %s, but got %d", csMachine.Spec.Template.ID, count)) + return "", errors.Errorf( + "expected 1 Template with UUID %s, but got %d", csMachine.Spec.Template.ID, count) } if len(csMachine.Spec.Template.Name) > 0 && csMachine.Spec.Template.Name != csTemplate.Name { - return "", multierror.Append(retErr, errors.Errorf( - "template name %s does not match name %s returned using UUID %s", csMachine.Spec.Template.Name, csTemplate.Name, csMachine.Spec.Template.ID)) + return "", errors.Errorf( + "template name %s does not match name %s returned using UUID %s", csMachine.Spec.Template.Name, csTemplate.Name, csMachine.Spec.Template.ID) } return csMachine.Spec.Template.ID, nil } - templateID, count, err := c.cs.Template.GetTemplateID(csMachine.Spec.Template.Name, "executable", zoneID) + templateID, count, err := c.cs.Template.GetTemplateID(csMachine.Spec.Template.Name, "executable", zoneID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return "", multierror.Append(retErr, errors.Wrapf( - err, "could not get Template ID from %s", csMachine.Spec.Template.Name)) + return "", errors.Wrapf(err, "could not get Template ID from %s", csMachine.Spec.Template.Name) } else if count != 1 { - return "", multierror.Append(retErr, errors.Errorf( - "expected 1 Template with name %s, but got %d", csMachine.Spec.Template.Name, count)) + return "", errors.Errorf( + "expected 1 Template with name %s, but got %d", csMachine.Spec.Template.Name, count) } return templateID, nil } @@ -155,22 +161,21 @@ func (c *client) ResolveTemplate( func (c *client) ResolveDiskOffering(csMachine *infrav1.CloudStackMachine, zoneID string) (diskOfferingID string, retErr error) { diskOfferingID = csMachine.Spec.DiskOffering.ID if len(csMachine.Spec.DiskOffering.Name) > 0 { - diskID, count, err := c.cs.DiskOffering.GetDiskOfferingID(csMachine.Spec.DiskOffering.Name, cloudstack.WithZone(zoneID)) + diskID, count, err := c.cs.DiskOffering.GetDiskOfferingID(csMachine.Spec.DiskOffering.Name, cloudstack.WithZone(zoneID), cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return "", multierror.Append(retErr, errors.Wrapf( - err, "could not get DiskOffering ID from %s", csMachine.Spec.DiskOffering.Name)) + return "", errors.Wrapf(err, "could not get DiskOffering ID from %s", csMachine.Spec.DiskOffering.Name) } else if count != 1 { - return "", multierror.Append(retErr, errors.Errorf( - "expected 1 DiskOffering with name %s in zone %s, but got %d", csMachine.Spec.DiskOffering.Name, zoneID, count)) + return "", errors.Errorf( + "expected 1 DiskOffering with name %s in zone %s, but got %d", csMachine.Spec.DiskOffering.Name, zoneID, count) } else if len(csMachine.Spec.DiskOffering.ID) > 0 && diskID != csMachine.Spec.DiskOffering.ID { - return "", multierror.Append(retErr, errors.Errorf( + return "", errors.Errorf( "diskOffering ID %s does not match ID %s returned using name %s in zone %s", - csMachine.Spec.DiskOffering.ID, diskID, csMachine.Spec.DiskOffering.Name, zoneID)) + csMachine.Spec.DiskOffering.ID, diskID, csMachine.Spec.DiskOffering.Name, zoneID) } else if len(diskID) == 0 { - return "", multierror.Append(retErr, errors.Errorf( + return "", errors.Errorf( "empty diskOffering ID %s returned using name %s in zone %s", - diskID, csMachine.Spec.DiskOffering.Name, zoneID)) + diskID, csMachine.Spec.DiskOffering.Name, zoneID) } diskOfferingID = diskID } @@ -178,56 +183,290 @@ func (c *client) ResolveDiskOffering(csMachine *infrav1.CloudStackMachine, zoneI return "", nil } - return verifyDiskoffering(csMachine, c, diskOfferingID, retErr) + return verifyDiskoffering(csMachine, c, diskOfferingID) } -func verifyDiskoffering(csMachine *infrav1.CloudStackMachine, c *client, diskOfferingID string, retErr error) (string, error) { - csDiskOffering, count, err := c.cs.DiskOffering.GetDiskOfferingByID(diskOfferingID) +func verifyDiskoffering(csMachine *infrav1.CloudStackMachine, c *client, diskOfferingID string) (string, error) { + csDiskOffering, count, err := c.cs.DiskOffering.GetDiskOfferingByID(diskOfferingID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return "", multierror.Append(retErr, errors.Wrapf( - err, "could not get DiskOffering by ID %s", diskOfferingID)) + return "", errors.Wrapf(err, "could not get DiskOffering by ID %s", diskOfferingID) } else if count != 1 { - return "", multierror.Append(retErr, errors.Errorf( - "expected 1 DiskOffering with UUID %s, but got %d", diskOfferingID, count)) + return "", errors.Errorf( + "expected 1 DiskOffering with UUID %s, but got %d", diskOfferingID, count) } if csDiskOffering.Iscustomized && csMachine.Spec.DiskOffering.CustomSize == 0 { - return "", multierror.Append(retErr, errors.Errorf( + return "", errors.Errorf( "diskOffering with UUID %s is customized, disk size can not be 0 GB", - diskOfferingID)) + diskOfferingID) } if !csDiskOffering.Iscustomized && csMachine.Spec.DiskOffering.CustomSize > 0 { - return "", multierror.Append(retErr, errors.Errorf( + return "", errors.Errorf( "diskOffering with UUID %s is not customized, disk size can not be specified", - diskOfferingID)) + diskOfferingID) } return diskOfferingID, nil } -// GetOrCreateVMInstance CreateVMInstance will fetch or create a VM instance, and -// sets the infrastructure machine spec and status accordingly. -func (c *client) GetOrCreateVMInstance( - csMachine *infrav1.CloudStackMachine, - capiMachine *clusterv1.Machine, - csCluster *infrav1.CloudStackCluster, - fd *infrav1.CloudStackFailureDomain, - affinity *infrav1.CloudStackAffinityGroup, - userData string, +// CheckAccountLimits Checks the account's limit of VM, CPU & Memory +func (c *client) CheckAccountLimits(offering *cloudstack.ServiceOffering) error { + if c.user.Account.CPUAvailable != "Unlimited" { + cpuAvailable, err := strconv.ParseInt(c.user.Account.CPUAvailable, 10, 0) + if err == nil && int64(offering.Cpunumber) > cpuAvailable { + return fmt.Errorf("CPU available (%d) in account can't fulfil the requirement: %d", cpuAvailable, offering.Cpunumber) + } + } + + if c.user.Account.MemoryAvailable != "Unlimited" { + memoryAvailable, err := strconv.ParseInt(c.user.Account.MemoryAvailable, 10, 0) + if err == nil && int64(offering.Memory) > memoryAvailable { + return fmt.Errorf("memory available (%d) in account can't fulfil the requirement: %d", memoryAvailable, offering.Memory) + } + } + + if c.user.Account.VMAvailable != "Unlimited" { + vmAvailable, err := strconv.ParseInt(c.user.Account.VMAvailable, 10, 0) + if err == nil && vmAvailable < 1 { + return fmt.Errorf("VM Limit in account has reached it's maximum value") + } + } + return nil +} + +// CheckDomainLimits Checks the domain's limit of VM, CPU & Memory +func (c *client) CheckDomainLimits(offering *cloudstack.ServiceOffering) error { + if c.user.Account.Domain.CPUAvailable != "Unlimited" { + cpuAvailable, err := strconv.ParseInt(c.user.Account.Domain.CPUAvailable, 10, 0) + if err == nil && int64(offering.Cpunumber) > cpuAvailable { + return fmt.Errorf("CPU available (%d) in domain can't fulfil the requirement: %d", cpuAvailable, offering.Cpunumber) + } + } + + if c.user.Account.Domain.MemoryAvailable != "Unlimited" { + memoryAvailable, err := strconv.ParseInt(c.user.Account.Domain.MemoryAvailable, 10, 0) + if err == nil && int64(offering.Memory) > memoryAvailable { + return fmt.Errorf("memory available (%d) in domain can't fulfil the requirement: %d", memoryAvailable, offering.Memory) + } + } + + if c.user.Account.Domain.VMAvailable != "Unlimited" { + vmAvailable, err := strconv.ParseInt(c.user.Account.Domain.VMAvailable, 10, 0) + if err == nil && vmAvailable < 1 { + return fmt.Errorf("VM Limit in domain has reached it's maximum value") + } + } + return nil +} + +// CheckProjectLimits Checks the project's limit of VM, CPU & Memory +func (c *client) CheckProjectLimits(offering *cloudstack.ServiceOffering) error { + if c.user.Project.ID == "" { + return nil + } + + if c.user.Project.CPUAvailable != "Unlimited" { + cpuAvailable, err := strconv.ParseInt(c.user.Project.CPUAvailable, 10, 0) + if err == nil && int64(offering.Cpunumber) > cpuAvailable { + return fmt.Errorf("CPU available (%d) in project can't fulfil the requirement: %d", cpuAvailable, offering.Cpunumber) + } + } + + if c.user.Project.MemoryAvailable != "Unlimited" { + memoryAvailable, err := strconv.ParseInt(c.user.Project.MemoryAvailable, 10, 0) + if err == nil && int64(offering.Memory) > memoryAvailable { + return fmt.Errorf("memory available (%d) in project can't fulfil the requirement: %d", memoryAvailable, offering.Memory) + } + } + + if c.user.Project.VMAvailable != "Unlimited" { + vmAvailable, err := strconv.ParseInt(c.user.Project.VMAvailable, 10, 0) + if err == nil && vmAvailable < 1 { + return fmt.Errorf("VM Limit in project has reached it's maximum value") + } + } + return nil +} + +// CheckLimits will check the account & domain limits +func (c *client) CheckLimits( + offering *cloudstack.ServiceOffering, ) error { + err := c.CheckAccountLimits(offering) + if err != nil { + return err + } - // Check if VM instance already exists. - if err := c.ResolveVMInstanceDetails(csMachine); err == nil || - !strings.Contains(strings.ToLower(err.Error()), "no match") { + err = c.CheckDomainLimits(offering) + if err != nil { return err } - offeringID, err := c.ResolveServiceOffering(csMachine, fd.Spec.Zone.ID) + err = c.CheckProjectLimits(offering) if err != nil { return err } - templateID, err := c.ResolveTemplate(csCluster, csMachine, fd.Spec.Zone.ID) + + return nil +} + +func (c *client) isFreeIPAvailable(networkID, ip string) (bool, error) { + params := c.cs.Address.NewListPublicIpAddressesParams() + params.SetNetworkid(networkID) + params.SetAllocatedonly(false) + params.SetForvirtualnetwork(false) + params.SetListall(true) + + if ip != "" { + params.SetIpaddress(ip) + } + + resp, err := c.cs.Address.ListPublicIpAddresses(params) + if err != nil { + return false, errors.Wrapf(err, "failed to list public IP addresses for network %q", networkID) + } + + for _, addr := range resp.PublicIpAddresses { + if addr.State == "Free" { + return true, nil + } + } + + return false, nil +} + +func (c *client) buildIPEntry(resolvedNet *cloudstack.Network, ip string) (map[string]string, error) { + if ip != "" { + if err := validateIPInCIDR(ip, resolvedNet.Cidr); err != nil { + return nil, err + } + } + + if resolvedNet.Type == NetworkTypeShared { + isAvailable, err := c.isFreeIPAvailable(resolvedNet.Id, ip) + if err != nil { + return nil, err + } + if !isAvailable { + if ip != "" { + return nil, errors.Errorf("IP %q is already allocated in network %q or out of range", ip, resolvedNet.Id) + } + return nil, errors.Errorf("no free IPs available in network %q", resolvedNet.Id) + } + } + + entry := map[string]string{ + "networkid": resolvedNet.Id, + } + if ip != "" { + entry["ip"] = ip + } + return entry, nil +} + +func (c *client) resolveNetworkByName(name string) (*cloudstack.Network, error) { + net, count, err := c.cs.Network.GetNetworkByName(name, cloudstack.WithProject(c.user.Project.ID)) + if err != nil { + return nil, errors.Wrapf(err, "failed to look up network %q", name) + } + if count != 1 { + return nil, errors.Errorf("expected 1 network named %q, but got %d", name, count) + } + return net, nil +} + +func (c *client) buildIPToNetworkList(csMachine *infrav1.CloudStackMachine) ([]map[string]string, error) { + var ipToNetworkList []map[string]string + + for _, net := range csMachine.Spec.Networks { + resolvedNet, err := c.resolveNetwork(net) + if err != nil { + return nil, err + } + + entry, err := c.buildIPEntry(resolvedNet, net.IP) + if err != nil { + return nil, err + } + + ipToNetworkList = append(ipToNetworkList, entry) + } + + return ipToNetworkList, nil +} + +func (c *client) resolveNetwork(net infrav1.NetworkSpec) (*cloudstack.Network, error) { + if net.ID == "" { + return c.resolveNetworkByName(net.Name) + } + + resolvedNet, _, err := c.cs.Network.GetNetworkByID(net.ID, cloudstack.WithProject(c.user.Project.ID)) + if err != nil { + return nil, errors.Wrapf(err, "failed to get network %q by ID", net.ID) + } + return resolvedNet, nil +} + +func validateIPInCIDR(ipStr, cidrStr string) error { + ip := net.ParseIP(ipStr) + if ip == nil { + return errors.Errorf("invalid IP address %q", ipStr) + } + + _, cidr, err := net.ParseCIDR(cidrStr) + if err != nil { + return errors.Wrapf(err, "invalid CIDR %q", cidrStr) + } + + if !cidr.Contains(ip) { + return errors.Errorf("IP %q is not within network CIDR %q", ipStr, cidrStr) + } + + return nil +} + +func (c *client) configureNetworkParams( + p *cloudstack.DeployVirtualMachineParams, + csMachine *infrav1.CloudStackMachine, + fd *infrav1.CloudStackFailureDomain, +) error { + if len(csMachine.Spec.Networks) == 0 { + if fd.Spec.Zone.Network.ID != "" { + p.SetNetworkids([]string{fd.Spec.Zone.Network.ID}) + } + } else { + firstNetwork := csMachine.Spec.Networks[0] + zoneNet := fd.Spec.Zone.Network + + if zoneNet.ID != "" && firstNetwork.ID != "" && firstNetwork.ID != zoneNet.ID { + return errors.Errorf("first network ID %q does not match zone network ID %q", firstNetwork.ID, zoneNet.ID) + } + if zoneNet.Name != "" && firstNetwork.Name != "" && firstNetwork.Name != zoneNet.Name { + return errors.Errorf("first network name %q does not match zone network name %q", firstNetwork.Name, zoneNet.Name) + } + + ipToNetworkList, err := c.buildIPToNetworkList(csMachine) + if err != nil { + return err + } + p.SetIptonetworklist(ipToNetworkList) + } + + return nil +} + +// DeployVM will create a VM instance, +// and sets the infrastructure machine spec and status accordingly. +func (c *client) DeployVM( + csMachine *infrav1.CloudStackMachine, + capiMachine *clusterv1.Machine, + fd *infrav1.CloudStackFailureDomain, + affinity *infrav1.CloudStackAffinityGroup, + offering *cloudstack.ServiceOffering, + userData string, +) error { + templateID, err := c.ResolveTemplate(csMachine, fd.Spec.Zone.ID) if err != nil { return err } @@ -236,12 +475,16 @@ func (c *client) GetOrCreateVMInstance( return err } - // Create VM instance. - p := c.cs.VirtualMachine.NewDeployVirtualMachineParams(offeringID, templateID, fd.Spec.Zone.ID) - p.SetNetworkids([]string{fd.Spec.Zone.Network.ID}) + p := c.cs.VirtualMachine.NewDeployVirtualMachineParams(offering.Id, templateID, fd.Spec.Zone.ID) + + if err := c.configureNetworkParams(p, csMachine, fd); err != nil { + return err + } + setIfNotEmpty(csMachine.Name, p.SetName) setIfNotEmpty(capiMachine.Name, p.SetDisplayname) setIfNotEmpty(diskOfferingID, p.SetDiskofferingid) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) setIntIfPositive(csMachine.Spec.DiskOffering.CustomSize, p.SetSize) setIfNotEmpty(csMachine.Spec.SSHKey, p.SetKeypair) @@ -272,7 +515,7 @@ func (c *client) GetOrCreateVMInstance( // CloudStack may have created the VM even though it reported an error. We attempt to // retrieve the VM so we can populate the CloudStackMachine for the user to manually // clean up. - vm, findErr := findVirtualMachine(c.cs.VirtualMachine, templateID, fd, csMachine) + vm, findErr := findVirtualMachine(c.cs.VirtualMachine, templateID, fd, csMachine, c.user.Project.ID) if findErr != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(findErr) return fmt.Errorf("%v; find virtual machine: %v", err, findErr) @@ -283,12 +526,48 @@ func (c *client) GetOrCreateVMInstance( return err } - csMachine.Spec.InstanceID = pointer.String(vm.Id) + csMachine.Spec.InstanceID = ptr.To(vm.Id) csMachine.Status.InstanceState = vm.State - } else { - csMachine.Spec.InstanceID = pointer.String(deployVMResp.Id) - csMachine.Status.Status = pointer.String(metav1.StatusSuccess) + + return fmt.Errorf("incomplete vm deployment (vm_id=%v): %w", vm.Id, err) + } + + csMachine.Spec.InstanceID = ptr.To(deployVMResp.Id) + csMachine.Status.Status = ptr.To(metav1.StatusSuccess) + + return nil +} + +// GetOrCreateVMInstance CreateVMInstance will fetch or create a VM instance, and +// sets the infrastructure machine spec and status accordingly. +func (c *client) GetOrCreateVMInstance( + csMachine *infrav1.CloudStackMachine, + capiMachine *clusterv1.Machine, + _ *infrav1.CloudStackCluster, + fd *infrav1.CloudStackFailureDomain, + affinity *infrav1.CloudStackAffinityGroup, + userData string, +) error { + // Check if VM instance already exists. + if err := c.ResolveVMInstanceDetails(csMachine); err == nil || + !strings.Contains(strings.ToLower(err.Error()), "no match") { + return err } + + offering, err := c.ResolveServiceOffering(csMachine, fd.Spec.Zone.ID) + if err != nil { + return err + } + + err = c.CheckLimits(&offering) + if err != nil { + return err + } + + if err := c.DeployVM(csMachine, capiMachine, fd, affinity, &offering, userData); err != nil { + return err + } + // Resolve uses a VM metrics request response to fill cloudstack machine status. // The deployment response is insufficient. return c.ResolveVMInstanceDetails(csMachine) @@ -300,13 +579,14 @@ func findVirtualMachine( client cloudstack.VirtualMachineServiceIface, templateID string, failureDomain *infrav1.CloudStackFailureDomain, - machine *infrav1.CloudStackMachine, + machine *infrav1.CloudStackMachine, projectID string, ) (*cloudstack.VirtualMachine, error) { params := client.NewListVirtualMachinesParams() params.SetTemplateid(templateID) params.SetZoneid(failureDomain.Spec.Zone.ID) params.SetNetworkid(failureDomain.Spec.Zone.Network.ID) params.SetName(machine.Name) + setIfNotEmpty(projectID, params.SetProjectid) response, err := client.ListVirtualMachines(params) if err != nil { @@ -322,15 +602,22 @@ func findVirtualMachine( // DestroyVMInstance Destroys a VM instance. Assumes machine has been fetched prior and has an instance ID. func (c *client) DestroyVMInstance(csMachine *infrav1.CloudStackMachine) error { + p := c.cs.Configuration.NewListCapabilitiesParams() + capabilities, err := c.cs.Configuration.ListCapabilities(p) + expunge := true + if err == nil { + expunge = capabilities.Capabilities.Allowuserexpungerecovervm + } + // Attempt deletion regardless of machine state. - p := c.csAsync.VirtualMachine.NewDestroyVirtualMachineParams(*csMachine.Spec.InstanceID) + p2 := c.csAsync.VirtualMachine.NewDestroyVirtualMachineParams(*csMachine.Spec.InstanceID) volIDs, err := c.listVMInstanceDatadiskVolumeIDs(*csMachine.Spec.InstanceID) if err != nil { return err } - p.SetExpunge(true) - setArrayIfNotEmpty(volIDs, p.SetVolumeids) - if _, err := c.csAsync.VirtualMachine.DestroyVirtualMachine(p); err != nil && + p2.SetExpunge(expunge) + setArrayIfNotEmpty(volIDs, p2.SetVolumeids) + if _, err := c.csAsync.VirtualMachine.DestroyVirtualMachine(p2); err != nil && strings.Contains(strings.ToLower(err.Error()), "unable to find uuid for id") { // VM doesn't exist. Success... return nil @@ -359,6 +646,7 @@ func (c *client) listVMInstanceDatadiskVolumeIDs(instanceID string) ([]string, e p.SetVirtualmachineid(instanceID) // VM root volumes are destroyed automatically, no need to explicitly include p.SetType("DATADISK") + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) listVOLResp, err := c.csAsync.Volume.ListVolumes(p) if err != nil { @@ -372,5 +660,4 @@ func (c *client) listVMInstanceDatadiskVolumeIDs(instanceID string) ([]string, e } return ret, nil - } diff --git a/pkg/cloud/instance_test.go b/pkg/cloud/instance_test.go index 4677ef69..52d42b85 100644 --- a/pkg/cloud/instance_test.go +++ b/pkg/cloud/instance_test.go @@ -21,19 +21,19 @@ import ( "fmt" "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" + gomock "go.uber.org/mock/gomock" + "k8s.io/utils/ptr" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "github.com/pkg/errors" - "k8s.io/utils/pointer" ) -var _ = Describe("Instance", func() { +var _ = ginkgo.Describe("Instance", func() { const ( unknownErrorMessage = "unknown err" offeringFakeID = "123" @@ -46,217 +46,558 @@ var _ = Describe("Instance", func() { unknownError := errors.New(unknownErrorMessage) var ( - mockCtrl *gomock.Controller - mockClient *cloudstack.CloudStackClient - vms *cloudstack.MockVirtualMachineServiceIface - sos *cloudstack.MockServiceOfferingServiceIface - dos *cloudstack.MockDiskOfferingServiceIface - ts *cloudstack.MockTemplateServiceIface - vs *cloudstack.MockVolumeServiceIface - client cloud.Client + mockCtrl *gomock.Controller + mockClient *cloudstack.CloudStackClient + configuration *cloudstack.MockConfigurationServiceIface + vms *cloudstack.MockVirtualMachineServiceIface + sos *cloudstack.MockServiceOfferingServiceIface + dos *cloudstack.MockDiskOfferingServiceIface + ts *cloudstack.MockTemplateServiceIface + vs *cloudstack.MockVolumeServiceIface + client cloud.Client ) - BeforeEach(func() { - mockCtrl = gomock.NewController(GinkgoT()) + ginkgo.BeforeEach(func() { + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = cloudstack.NewMockClient(mockCtrl) + configuration = mockClient.Configuration.(*cloudstack.MockConfigurationServiceIface) vms = mockClient.VirtualMachine.(*cloudstack.MockVirtualMachineServiceIface) sos = mockClient.ServiceOffering.(*cloudstack.MockServiceOfferingServiceIface) dos = mockClient.DiskOffering.(*cloudstack.MockDiskOfferingServiceIface) ts = mockClient.Template.(*cloudstack.MockTemplateServiceIface) vs = mockClient.Volume.(*cloudstack.MockVolumeServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) dummies.SetDummyVars() }) - AfterEach(func() { + ginkgo.AfterEach(func() { mockCtrl.Finish() }) - Context("when fetching a VM instance", func() { - It("Handles an unknown error when fetching by ID", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, unknownError) - Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).To(MatchError(unknownErrorMessage)) + ginkgo.Context("when fetching a VM instance", func() { + ginkgo.It("Handles an unknown error when fetching by ID", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, unknownError) + gomega.Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).To(gomega.MatchError(unknownErrorMessage)) }) - It("Handles finding more than one VM instance by ID", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, 2, nil) - Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)). - Should(MatchError("found more than one VM Instance with ID " + *dummies.CSMachine1.Spec.InstanceID)) + ginkgo.It("Handles finding more than one VM instance by ID", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, 2, nil) + gomega.Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)). + Should(gomega.MatchError("found more than one VM Instance with ID " + *dummies.CSMachine1.Spec.InstanceID)) }) - It("sets dummies.CSMachine1 spec and status values when VM instance found by ID", func() { + ginkgo.It("sets dummies.CSMachine1 spec and status values when VM instance found by ID", func() { vmsResp := &cloudstack.VirtualMachinesMetric{Id: *dummies.CSMachine1.Spec.InstanceID} - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(vmsResp, 1, nil) - Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(Succeed()) - Ω(dummies.CSMachine1.Spec.ProviderID).Should(Equal(pointer.String("cloudstack:///" + vmsResp.Id))) - Ω(dummies.CSMachine1.Spec.InstanceID).Should(Equal(pointer.String(vmsResp.Id))) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(vmsResp, 1, nil) + gomega.Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(gomega.Succeed()) + gomega.Ω(dummies.CSMachine1.Spec.ProviderID).Should(gomega.Equal(ptr.To("cloudstack:///" + vmsResp.Id))) + gomega.Ω(dummies.CSMachine1.Spec.InstanceID).Should(gomega.Equal(ptr.To(vmsResp.Id))) }) - It("handles an unknown error when fetching by name", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, unknownError) + ginkgo.It("handles an unknown error when fetching by name", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, unknownError) - Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(MatchError(unknownErrorMessage)) + gomega.Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(gomega.MatchError(unknownErrorMessage)) }) - It("handles finding more than one VM instance by Name", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, 2, nil) + ginkgo.It("handles finding more than one VM instance by Name", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, 2, nil) - Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should( - MatchError("found more than one VM Instance with name " + dummies.CSMachine1.Name)) + gomega.Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should( + gomega.MatchError("found more than one VM Instance with name " + dummies.CSMachine1.Name)) }) - It("sets dummies.CSMachine1 spec and status values when VM instance found by Name", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name). + ginkgo.It("sets dummies.CSMachine1 spec and status values when VM instance found by Name", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{Id: *dummies.CSMachine1.Spec.InstanceID}, -1, nil) - Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(Succeed()) - Ω(dummies.CSMachine1.Spec.ProviderID).Should(Equal( - pointer.String(fmt.Sprintf("cloudstack:///%s", *dummies.CSMachine1.Spec.InstanceID)))) - Ω(dummies.CSMachine1.Spec.InstanceID).Should(Equal(pointer.String(*dummies.CSMachine1.Spec.InstanceID))) + gomega.Ω(client.ResolveVMInstanceDetails(dummies.CSMachine1)).Should(gomega.Succeed()) + gomega.Ω(dummies.CSMachine1.Spec.ProviderID).Should(gomega.Equal( + ptr.To(fmt.Sprintf("cloudstack:///%s", *dummies.CSMachine1.Spec.InstanceID)))) + gomega.Ω(dummies.CSMachine1.Spec.InstanceID).Should(gomega.Equal(ptr.To(*dummies.CSMachine1.Spec.InstanceID))) }) }) - Context("when creating a VM instance", func() { + ginkgo.Context("when creating a VM instance", func() { vmMetricResp := &cloudstack.VirtualMachinesMetric{} expectVMNotFound := func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) } - It("doesn't re-create if one already exists.", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(vmMetricResp, -1, nil) - Ω(client.GetOrCreateVMInstance( + ginkgo.It("doesn't re-create if one already exists.", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(vmMetricResp, -1, nil) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - Should(Succeed()) + Should(gomega.Succeed()) }) - It("returns unknown error while fetching VM instance", func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, unknownError) - Ω(client.GetOrCreateVMInstance( + ginkgo.It("returns unknown error while fetching VM instance", func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, unknownError) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - Should(MatchError(unknownErrorMessage)) + Should(gomega.MatchError(unknownErrorMessage)) }) - It("returns errors occurring while fetching service offering information", func() { + ginkgo.It("returns errors occurring while fetching service offering information", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return("", -1, unknownError) - Ω(client.GetOrCreateVMInstance( + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(&cloudstack.ServiceOffering{}, -1, unknownError) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors if more than one service offering found", func() { + ginkgo.It("returns errors if more than one service offering found", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return("", 2, nil) - Ω(client.GetOrCreateVMInstance( + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + }, 2, nil) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors while fetching template", func() { + ginkgo.It("returns errors while fetching template", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(dummies.CSMachine1.Spec.Offering.ID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return("", -1, unknownError) - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors when more than one template found", func() { + ginkgo.It("returns errors when more than one template found", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(dummies.CSMachine1.Spec.Offering.ID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return("", 2, nil) - Ω(client.GetOrCreateVMInstance( + + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return("", 2, nil) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors when more than one diskoffering found", func() { + ginkgo.It("returns errors when more than one diskoffering found", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(dummies.CSMachine1.Spec.Offering.ID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 2, nil) - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors when fetching diskoffering", func() { + ginkgo.It("returns errors when fetching diskoffering", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(dummies.CSMachine1.Spec.Offering.ID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, unknownError) - Ω(client.GetOrCreateVMInstance( + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, unknownError) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors when disk size not zero for non-customized disk offering", func() { + ginkgo.It("returns errors when disk size not zero for non-customized disk offering", func() { expectVMNotFound() dummies.CSMachine1.Spec.DiskOffering.CustomSize = 1 - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(dummies.CSMachine1.Spec.Offering.ID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) - Ω(client.GetOrCreateVMInstance( + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("returns errors when disk size zero for customized disk offering", func() { + ginkgo.It("returns errors when disk size zero for customized disk offering", func() { expectVMNotFound() dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(dummies.CSMachine1.Spec.Offering.ID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()).Return(dummies.CSMachine1.Spec.Template.ID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID).Return(&cloudstack.DiskOffering{Iscustomized: true}, 1, nil) - Ω(client.GetOrCreateVMInstance( + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: true}, 1, nil) + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - ShouldNot(Succeed()) + ShouldNot(gomega.Succeed()) }) - It("handles deployment errors", func() { + ginkgo.Context("when account & domains have limits", func() { + ginkgo.It("returns errors when there are not enough available CPU in account", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "1", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError(gomega.MatchRegexp("CPU available .* in account can't fulfil the requirement:.*"))) + }) + + ginkgo.It("returns errors when there are not enough available CPU in domain", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "1", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError(gomega.MatchRegexp("CPU available .* in domain can't fulfil the requirement:.*"))) + }) + + ginkgo.It("returns errors when there are not enough available CPU in project", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "1", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError(gomega.MatchRegexp("CPU available .* in project can't fulfil the requirement:.*"))) + }) + + ginkgo.It("returns errors when there is not enough available memory in account", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "512", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError(gomega.MatchRegexp("memory available .* in account can't fulfil the requirement:.*"))) + }) + + ginkgo.It("returns errors when there is not enough available memory in domain", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "512", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError(gomega.MatchRegexp("memory available .* in domain can't fulfil the requirement:.*"))) + }) + + ginkgo.It("returns errors when there is not enough available memory in project", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "512", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "512", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError(gomega.MatchRegexp("memory available .* in project can't fulfil the requirement:.*"))) + }) + + ginkgo.It("returns errors when there is not enough available VM limit in account", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "0", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError("VM Limit in account has reached it's maximum value")) + }) + + ginkgo.It("returns errors when there is not enough available VM limit in domain", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "0", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "10", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "20", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError("VM Limit in domain has reached it's maximum value")) + }) + + ginkgo.It("returns errors when there is not enough available VM limit in project", func() { + expectVMNotFound() + dummies.CSMachine1.Spec.DiskOffering.CustomSize = 0 + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 2, + Memory: 1024, + }, 1, nil) + user := &cloud.User{ + Account: cloud.Account{ + Domain: cloud.Domain{ + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "10", + }, + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "10", + }, + Project: cloud.Project{ + ID: "123", + CPUAvailable: "20", + MemoryAvailable: "2048", + VMAvailable: "0", + }, + } + c := cloud.NewClientFromCSAPIClient(mockClient, user) + gomega.Ω(c.GetOrCreateVMInstance( + dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). + Should(gomega.MatchError("VM Limit in project has reached it's maximum value")) + }) + }) + + ginkgo.It("handles deployment errors", func() { expectVMNotFound() - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(offeringFakeID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()). Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID). + dos.EXPECT().GetDiskOfferingByID(diskOfferingFakeID, gomock.Any()). Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) vms.EXPECT().NewDeployVirtualMachineParams(offeringFakeID, templateFakeID, dummies.Zone1.ID). Return(&cloudstack.DeployVirtualMachineParams{}) vms.EXPECT().DeployVirtualMachine(gomock.Any()).Return(nil, unknownError) vms.EXPECT().NewListVirtualMachinesParams().Return(&cloudstack.ListVirtualMachinesParams{}) vms.EXPECT().ListVirtualMachines(gomock.Any()).Return(&cloudstack.ListVirtualMachinesResponse{}, nil) - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - Should(MatchError(unknownErrorMessage)) + Should(gomega.MatchError(unknownErrorMessage)) }) - Context("when using UUIDs and/or names to locate service offerings and templates", func() { - BeforeEach(func() { + ginkgo.Context("when using UUIDs and/or names to locate service offerings and templates", func() { + ginkgo.BeforeEach(func() { gomock.InOrder( - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(nil, -1, notFoundError), - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{}, 1, nil)) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) }) ActionAndAssert := func() { @@ -271,156 +612,181 @@ var _ = Describe("Instance", func() { func(p interface{}) { params := p.(*cloudstack.DeployVirtualMachineParams) displayName, _ := params.GetDisplayname() - Ω(displayName == dummies.CAPIMachine.Name).Should(BeTrue()) + gomega.Ω(displayName == dummies.CAPIMachine.Name).Should(gomega.BeTrue()) b64UserData, _ := params.GetUserdata() userData, err := base64.StdEncoding.DecodeString(b64UserData) - Ω(err).ToNot(HaveOccurred()) + gomega.Ω(err).ToNot(gomega.HaveOccurred()) decompressedUserData, err := decompress(userData) - Ω(err).ToNot(HaveOccurred()) + gomega.Ω(err).ToNot(gomega.HaveOccurred()) - Ω(string(decompressedUserData)).To(Equal(expectUserData)) + gomega.Ω(string(decompressedUserData)).To(gomega.Equal(expectUserData)) }).Return(deploymentResp, nil) - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, expectUserData)). - Should(Succeed()) + Should(gomega.Succeed()) } - It("works with service offering name and template name", func() { + ginkgo.It("works with service offering name and template name", func() { dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID dummies.CSMachine1.Spec.Offering.ID = "" dummies.CSMachine1.Spec.Template.ID = "" dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(offeringFakeID, 1, nil) + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) ActionAndAssert() }) - It("works with service offering name and template name without disk offering", func() { + ginkgo.It("works with service offering name and template name without disk offering", func() { dummies.CSMachine1.Spec.Offering.ID = "" dummies.CSMachine1.Spec.Template.ID = "" dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" dummies.CSMachine1.Spec.DiskOffering = infrav1.CloudStackResourceDiskOffering{} - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(offeringFakeID, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) ActionAndAssert() }) - It("works with service offering ID and template name", func() { + ginkgo.It("works with service offering ID and template name", func() { dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID dummies.CSMachine1.Spec.Offering.ID = offeringFakeID dummies.CSMachine1.Spec.Template.ID = "" dummies.CSMachine1.Spec.Offering.Name = "" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: ""}, 1, nil) - ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) + ts.EXPECT().GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ActionAndAssert() }) - It("works with service offering name and template ID", func() { + ginkgo.It("works with service offering name and template ID", func() { dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID dummies.CSMachine1.Spec.Offering.ID = "" dummies.CSMachine1.Spec.Template.ID = templateFakeID dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "" - sos.EXPECT().GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(offeringFakeID, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: ""}, 1, nil) + sos.EXPECT().GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()).Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) + + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: ""}, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ActionAndAssert() }) - It("works with service offering ID and template ID", func() { + ginkgo.It("works with service offering ID and template ID", func() { dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID dummies.CSMachine1.Spec.Offering.ID = offeringFakeID dummies.CSMachine1.Spec.Template.ID = templateFakeID dummies.CSMachine1.Spec.Offering.Name = "" dummies.CSMachine1.Spec.Template.Name = "" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID). - Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()). Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID). + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()). Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter). + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()). Return(&cloudstack.Template{Name: "template"}, 1, nil) ActionAndAssert() }) - It("works with Id and name both provided", func() { + ginkgo.It("works with Id and name both provided", func() { dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID dummies.CSMachine1.Spec.Offering.ID = offeringFakeID dummies.CSMachine1.Spec.Template.ID = templateFakeID dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: "template"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{ + Id: dummies.CSMachine1.Spec.Offering.ID, + Name: dummies.CSMachine1.Spec.Offering.Name, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: "template"}, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID, 1, nil) - dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) + dos.EXPECT().GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()).Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ActionAndAssert() }) }) - Context("when using both UUIDs and names to locate service offerings and templates", func() { - BeforeEach(func() { - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + ginkgo.Context("when using both UUIDs and names to locate service offerings and templates", func() { + ginkgo.BeforeEach(func() { + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) }) - It("works with Id and name both provided, offering name mismatch", func() { + ginkgo.It("works with Id and name both provided, offering name mismatch", func() { dummies.CSMachine1.Spec.Offering.ID = offeringFakeID dummies.CSMachine1.Spec.Template.ID = templateFakeID dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering-not-match"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{Name: "offering-not-match"}, 1, nil) requiredRegexp := "offering name %s does not match name %s returned using UUID %s" - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - Should(MatchError(MatchRegexp(requiredRegexp, dummies.CSMachine1.Spec.Offering.Name, "offering-not-match", offeringFakeID))) + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegexp, dummies.CSMachine1.Spec.Offering.Name, "offering-not-match", offeringFakeID))) }) - It("works with Id and name both provided, template name mismatch", func() { + ginkgo.It("works with Id and name both provided, template name mismatch", func() { dummies.CSMachine1.Spec.Offering.ID = offeringFakeID dummies.CSMachine1.Spec.Template.ID = templateFakeID dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: "template-not-match"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: "template-not-match"}, 1, nil) requiredRegexp := "template name %s does not match name %s returned using UUID %s" - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - Should(MatchError(MatchRegexp(requiredRegexp, dummies.CSMachine1.Spec.Template.Name, "template-not-match", templateFakeID))) - + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegexp, dummies.CSMachine1.Spec.Template.Name, "template-not-match", templateFakeID))) }) - It("works with Id and name both provided, disk offering id/name mismatch", func() { + ginkgo.It("works with Id and name both provided, disk offering id/name mismatch", func() { dummies.CSMachine1.Spec.Offering.ID = offeringFakeID dummies.CSMachine1.Spec.Template.ID = templateFakeID dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID @@ -428,45 +794,49 @@ var _ = Describe("Instance", func() { dummies.CSMachine1.Spec.Template.Name = "template" dummies.CSMachine1.Spec.DiskOffering.Name = "diskoffering" - sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) - ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter).Return(&cloudstack.Template{Name: "template"}, 1, nil) + sos.EXPECT().GetServiceOfferingByID(dummies.CSMachine1.Spec.Offering.ID, gomock.Any()).Return(&cloudstack.ServiceOffering{Name: "offering"}, 1, nil) + ts.EXPECT().GetTemplateByID(dummies.CSMachine1.Spec.Template.ID, executableFilter, gomock.Any()).Return(&cloudstack.Template{Name: "template"}, 1, nil) dos.EXPECT().GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()).Return(diskOfferingFakeID+"-not-match", 1, nil) requiredRegexp := "diskOffering ID %s does not match ID %s returned using name %s" - Ω(client.GetOrCreateVMInstance( + gomega.Ω(client.GetOrCreateVMInstance( dummies.CSMachine1, dummies.CAPIMachine, dummies.CSCluster, dummies.CSFailureDomain1, dummies.CSAffinityGroup, "")). - Should(MatchError(MatchRegexp(requiredRegexp, dummies.CSMachine1.Spec.DiskOffering.ID, diskOfferingFakeID+"-not-match", dummies.CSMachine1.Spec.DiskOffering.Name))) - + Should(gomega.MatchError(gomega.MatchRegexp(requiredRegexp, dummies.CSMachine1.Spec.DiskOffering.ID, diskOfferingFakeID+"-not-match", dummies.CSMachine1.Spec.DiskOffering.Name))) }) }) - It("doesn't compress user data", func() { + ginkgo.It("doesn't compress user data", func() { dummies.CSMachine1.Spec.DiskOffering.ID = diskOfferingFakeID dummies.CSMachine1.Spec.Offering.ID = "" dummies.CSMachine1.Spec.Template.ID = "" dummies.CSMachine1.Spec.Offering.Name = "offering" dummies.CSMachine1.Spec.Template.Name = "template" - dummies.CSMachine1.Spec.UncompressedUserData = pointer.Bool(true) + dummies.CSMachine1.Spec.UncompressedUserData = ptr.To(true) vms.EXPECT(). - GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(nil, -1, notFoundError) vms.EXPECT(). - GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{}, 1, nil) vms.EXPECT(). - GetVirtualMachinesMetricByName(dummies.CSMachine1.Name). + GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()). Return(nil, -1, notFoundError) + sos.EXPECT(). - GetServiceOfferingID(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). - Return(offeringFakeID, 1, nil) + GetServiceOfferingByName(dummies.CSMachine1.Spec.Offering.Name, gomock.Any()). + Return(&cloudstack.ServiceOffering{ + Id: offeringFakeID, + Cpunumber: 1, + Memory: 1024, + }, 1, nil) dos.EXPECT(). GetDiskOfferingID(dummies.CSMachine1.Spec.DiskOffering.Name, gomock.Any()). Return(diskOfferingFakeID, 1, nil) dos.EXPECT(). - GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID). + GetDiskOfferingByID(dummies.CSMachine1.Spec.DiskOffering.ID, gomock.Any()). Return(&cloudstack.DiskOffering{Iscustomized: false}, 1, nil) ts.EXPECT(). - GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID). + GetTemplateID(dummies.CSMachine1.Spec.Template.Name, executableFilter, dummies.Zone1.ID, gomock.Any()). Return(templateFakeID, 1, nil) vms.EXPECT(). NewDeployVirtualMachineParams(offeringFakeID, templateFakeID, dummies.Zone1.ID). @@ -482,13 +852,13 @@ var _ = Describe("Instance", func() { func(p interface{}) { params := p.(*cloudstack.DeployVirtualMachineParams) displayName, _ := params.GetDisplayname() - Ω(displayName == dummies.CAPIMachine.Name).Should(BeTrue()) + gomega.Ω(displayName == dummies.CAPIMachine.Name).Should(gomega.BeTrue()) // Ensure the user data is only base64 encoded. b64UserData, _ := params.GetUserdata() userData, err := base64.StdEncoding.DecodeString(b64UserData) - Ω(err).ToNot(HaveOccurred()) - Ω(string(userData)).To(Equal(expectUserData)) + gomega.Ω(err).ToNot(gomega.HaveOccurred()) + gomega.Ω(string(userData)).To(gomega.Equal(expectUserData)) }).Return(deploymentResp, nil) err := client.GetOrCreateVMInstance( @@ -499,13 +869,17 @@ var _ = Describe("Instance", func() { dummies.CSAffinityGroup, expectUserData, ) - Ω(err).Should(Succeed()) + gomega.Ω(err).Should(gomega.Succeed()) }) }) - Context("when destroying a VM instance", func() { + ginkgo.Context("when destroying a VM instance", func() { + listCapabilitiesParams := &cloudstack.ListCapabilitiesParams{} expungeDestroyParams := &cloudstack.DestroyVirtualMachineParams{} expungeDestroyParams.SetExpunge(true) + listCapabilitiesResponse := &cloudstack.ListCapabilitiesResponse{ + Capabilities: &cloudstack.Capability{Allowuserexpungerecovervm: true}, + } listVolumesParams := &cloudstack.ListVolumesParams{} listVolumesResponse := &cloudstack.ListVolumesResponse{ Volumes: []*cloudstack.Volume{ @@ -518,7 +892,12 @@ var _ = Describe("Instance", func() { }, } - It("calls destroy and finds VM doesn't exist, then returns nil", func() { + ginkgo.BeforeEach(func() { + configuration.EXPECT().NewListCapabilitiesParams().Return(listCapabilitiesParams) + configuration.EXPECT().ListCapabilities(listCapabilitiesParams).Return(listCapabilitiesResponse, nil) + }) + + ginkgo.It("calls destroy and finds VM doesn't exist, then returns nil", func() { listVolumesParams.SetVirtualmachineid(*dummies.CSMachine1.Spec.InstanceID) listVolumesParams.SetType("DATADISK") vms.EXPECT().NewDestroyVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID). @@ -526,11 +905,11 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, fmt.Errorf("unable to find uuid for id")) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - Ω(client.DestroyVMInstance(dummies.CSMachine1)). - Should(Succeed()) + gomega.Ω(client.DestroyVMInstance(dummies.CSMachine1)). + Should(gomega.Succeed()) }) - It("calls destroy and returns unexpected error", func() { + ginkgo.It("calls destroy and returns unexpected error", func() { listVolumesParams.SetVirtualmachineid(*dummies.CSMachine1.Spec.InstanceID) listVolumesParams.SetType("DATADISK") vms.EXPECT().NewDestroyVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID). @@ -538,10 +917,10 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, fmt.Errorf("new error")) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - Ω(client.DestroyVMInstance(dummies.CSMachine1)).Should(MatchError("new error")) + gomega.Ω(client.DestroyVMInstance(dummies.CSMachine1)).Should(gomega.MatchError("new error")) }) - It("calls destroy without error but cannot resolve VM after", func() { + ginkgo.It("calls destroy without error but cannot resolve VM after", func() { listVolumesParams.SetVirtualmachineid(*dummies.CSMachine1.Spec.InstanceID) listVolumesParams.SetType("DATADISK") vms.EXPECT().NewDestroyVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID). @@ -549,13 +928,13 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID).Return(nil, -1, notFoundError) - vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name).Return(nil, -1, notFoundError) - Ω(client.DestroyVMInstance(dummies.CSMachine1)). - Should(Succeed()) + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()).Return(nil, -1, notFoundError) + vms.EXPECT().GetVirtualMachinesMetricByName(dummies.CSMachine1.Name, gomock.Any()).Return(nil, -1, notFoundError) + gomega.Ω(client.DestroyVMInstance(dummies.CSMachine1)). + Should(gomega.Succeed()) }) - It("calls destroy without error and identifies it as expunging", func() { + ginkgo.It("calls destroy without error and identifies it as expunging", func() { listVolumesParams.SetVirtualmachineid(*dummies.CSMachine1.Spec.InstanceID) listVolumesParams.SetType("DATADISK") vms.EXPECT().NewDestroyVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID). @@ -563,15 +942,15 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{ State: "Expunging", }, 1, nil) - Ω(client.DestroyVMInstance(dummies.CSMachine1)). - Should(Succeed()) + gomega.Ω(client.DestroyVMInstance(dummies.CSMachine1)). + Should(gomega.Succeed()) }) - It("calls destroy without error and identifies it as expunged", func() { + ginkgo.It("calls destroy without error and identifies it as expunged", func() { listVolumesParams.SetVirtualmachineid(*dummies.CSMachine1.Spec.InstanceID) listVolumesParams.SetType("DATADISK") vms.EXPECT().NewDestroyVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID). @@ -579,15 +958,15 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{ State: "Expunged", }, 1, nil) - Ω(client.DestroyVMInstance(dummies.CSMachine1)). - Should(Succeed()) + gomega.Ω(client.DestroyVMInstance(dummies.CSMachine1)). + Should(gomega.Succeed()) }) - It("calls destroy without error and identifies it as stopping", func() { + ginkgo.It("calls destroy without error and identifies it as stopping", func() { listVolumesParams.SetVirtualmachineid(*dummies.CSMachine1.Spec.InstanceID) listVolumesParams.SetType("DATADISK") vms.EXPECT().NewDestroyVirtualMachineParams(*dummies.CSMachine1.Spec.InstanceID). @@ -595,11 +974,11 @@ var _ = Describe("Instance", func() { vms.EXPECT().DestroyVirtualMachine(expungeDestroyParams).Return(nil, nil) vs.EXPECT().NewListVolumesParams().Return(listVolumesParams) vs.EXPECT().ListVolumes(listVolumesParams).Return(listVolumesResponse, nil) - vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID). + vms.EXPECT().GetVirtualMachinesMetricByID(*dummies.CSMachine1.Spec.InstanceID, gomock.Any()). Return(&cloudstack.VirtualMachinesMetric{ State: "Stopping", }, 1, nil) - Ω(client.DestroyVMInstance(dummies.CSMachine1)).Should(MatchError("VM deletion in progress")) + gomega.Ω(client.DestroyVMInstance(dummies.CSMachine1)).Should(gomega.MatchError("VM deletion in progress")) }) }) }) diff --git a/pkg/cloud/isolated_network.go b/pkg/cloud/isolated_network.go index e622782e..cf2111a0 100644 --- a/pkg/cloud/isolated_network.go +++ b/pkg/cloud/isolated_network.go @@ -21,28 +21,27 @@ import ( "strings" "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/hashicorp/go-multierror" "github.com/pkg/errors" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ) type IsoNetworkIface interface { GetOrCreateIsolatedNetwork(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error AssociatePublicIPAddress(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error - GetOrCreateLoadBalancerRule(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error + GetOrCreateLoadBalancerRule(*infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error OpenFirewallRules(*infrav1.CloudStackIsolatedNetwork) error - GetPublicIP(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) (*cloudstack.PublicIpAddress, error) - ResolveLoadBalancerRuleDetails(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error + GetPublicIP(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackCluster) (*cloudstack.PublicIpAddress, error) + ResolveLoadBalancerRuleDetails(*infrav1.CloudStackIsolatedNetwork) error AssignVMToLoadBalancerRule(isoNet *infrav1.CloudStackIsolatedNetwork, instanceID string) error DeleteNetwork(infrav1.Network) error - DisposeIsoNetResources(*infrav1.CloudStackFailureDomain, *infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error + DisposeIsoNetResources(*infrav1.CloudStackIsolatedNetwork, *infrav1.CloudStackCluster) error } // getOfferingID fetches an offering id. -func (c *client) getOfferingID() (string, error) { - offeringID, count, retErr := c.cs.NetworkOffering.GetNetworkOfferingID(NetOffering) +func (c *client) getOfferingID(offeringName string) (string, error) { + offeringID, count, retErr := c.cs.NetworkOffering.GetNetworkOfferingID(offeringName) if retErr != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) return "", retErr @@ -59,7 +58,7 @@ func (c *client) AssociatePublicIPAddress( csCluster *infrav1.CloudStackCluster, ) (retErr error) { // Check specified IP address is available or get an unused one if not specified. - publicAddress, err := c.GetPublicIP(fd, isoNet, csCluster) + publicAddress, err := c.GetPublicIP(fd, csCluster) if err != nil { return errors.Wrapf(err, "fetching a public IP address") } @@ -67,8 +66,8 @@ func (c *client) AssociatePublicIPAddress( csCluster.Spec.ControlPlaneEndpoint.Host = publicAddress.Ipaddress isoNet.Status.PublicIPID = publicAddress.Id - // Check if the address is already associated with the network. - if publicAddress.Associatednetworkid == isoNet.Spec.ID { + // Check if the address is already associated with the network or VPC. + if publicAddress.Associatednetworkid == isoNet.Spec.ID || (isoNet.Spec.VPC != nil && publicAddress.Vpcid == isoNet.Spec.VPC.ID) { return nil } @@ -76,6 +75,10 @@ func (c *client) AssociatePublicIPAddress( p := c.cs.Address.NewAssociateIpAddressParams() p.SetIpaddress(isoNet.Spec.ControlPlaneEndpoint.Host) p.SetNetworkid(isoNet.Spec.ID) + if isoNet.Spec.VPC != nil && isoNet.Spec.VPC.ID != "" { + p.SetVpcid(isoNet.Spec.VPC.ID) + } + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) if _, err := c.cs.Address.AssociateIpAddress(p); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return errors.Wrapf(err, @@ -94,37 +97,211 @@ func (c *client) AssociatePublicIPAddress( // CreateIsolatedNetwork creates an isolated network in the relevant FailureDomain per passed network specification. func (c *client) CreateIsolatedNetwork(fd *infrav1.CloudStackFailureDomain, isoNet *infrav1.CloudStackIsolatedNetwork) (retErr error) { // Get network offering ID. - offeringID, err := c.getOfferingID() + offeringName := NetOffering + // First, check if VPC is specified and handle it + if isoNet.Spec.VPC != nil && (isoNet.Spec.VPC.Name != "" || isoNet.Spec.VPC.ID != "") { + // Try to resolve or create the VPC + err := c.ResolveVPC(isoNet.Spec.VPC) + if err != nil { // No VPC found, create it + err = c.CreateVPC(fd, isoNet.Spec.VPC) + if err != nil { + return errors.Wrap(err, "creating VPC with name "+isoNet.Spec.VPC.Name) + } + } + offeringName = NetVPCOffering + } + + if isoNet.Spec.Offering != "" { + offeringName = isoNet.Spec.Offering + } + + // Get network offering ID. + offeringID, err := c.getOfferingID(offeringName) if err != nil { return err } // Do isolated network creation. - p := c.cs.Network.NewCreateNetworkParams(isoNet.Spec.Name, isoNet.Spec.Name, offeringID, fd.Spec.Zone.ID) + p := c.cs.Network.NewCreateNetworkParams(isoNet.Spec.Name, offeringID, fd.Spec.Zone.ID) + p.SetDisplaytext(isoNet.Spec.Name) + + if isoNet.Spec.Gateway != "" { + p.SetGateway(isoNet.Spec.Gateway) + } + + if isoNet.Spec.Netmask != "" { + p.SetNetmask(isoNet.Spec.Netmask) + } + + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) + + // If VPC is specified, set the VPC ID for the network + if isoNet.Spec.VPC != nil && isoNet.Spec.VPC.ID != "" { + p.SetVpcid(isoNet.Spec.VPC.ID) + } + resp, err := c.cs.Network.CreateNetwork(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return errors.Wrapf(err, "creating network with name %s", isoNet.Spec.Name) } isoNet.Spec.ID = resp.Id + isoNet.Spec.Gateway = resp.Gateway + isoNet.Spec.Netmask = resp.Netmask + isoNet.Status.RoutingMode = resp.Ip4routing return c.AddCreatedByCAPCTag(ResourceTypeNetwork, isoNet.Spec.ID) } -// OpenFirewallRules opens a CloudStack firewall for an isolated network. +// OpenFirewallRules opens a CloudStack egress firewall for an isolated network. func (c *client) OpenFirewallRules(isoNet *infrav1.CloudStackIsolatedNetwork) (retErr error) { - p := c.cs.Firewall.NewCreateEgressFirewallRuleParams(isoNet.Spec.ID, NetworkProtocolTCP) - _, retErr = c.cs.Firewall.CreateEgressFirewallRule(p) - if retErr != nil && strings.Contains(strings.ToLower(retErr.Error()), "there is already") { // Already a firewall rule here. - retErr = nil + // Early return if VPC is present + // Firewall rules are not opened for isolated networks within a VPC because VPCs have their own mechanisms for managing firewall rules. + if isoNet.Spec.VPC != nil && isoNet.Spec.VPC.ID != "" { + return nil } - c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) - return retErr + + // Get network details + network, err := c.getNetwork(isoNet.Spec.ID) + if err != nil { + return err + } + + // Early return if egress default policy is true + if network.Egressdefaultpolicy { + isoNet.Status.FirewallRulesOpened = true + return nil + } + + // Check if all required firewall rules exist + allRulesPresent, err := c.checkFirewallRules(isoNet) + if err != nil { + return err + } + if allRulesPresent { + isoNet.Status.FirewallRulesOpened = true + return nil + } + + // Reset status if rules are missing + isoNet.Status.FirewallRulesOpened = false + + // Create firewall rules for each protocol + protocols := []string{NetworkProtocolTCP, NetworkProtocolUDP, NetworkProtocolICMP} + for _, proto := range protocols { + if err := c.createFirewallRule(isoNet, proto); err != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) + return errors.Wrapf(err, "failed creating firewall rule for network ID %s", isoNet.Spec.ID) + } + } + + // Mark firewall rules as opened if all rules were created successfully + isoNet.Status.FirewallRulesOpened = true + return nil +} + +// Helper function to check if all required firewall rules exist +func (c *client) checkFirewallRules(isoNet *infrav1.CloudStackIsolatedNetwork) (bool, error) { + protocols := []string{NetworkProtocolTCP, NetworkProtocolUDP, NetworkProtocolICMP} + p := c.cs.Firewall.NewListEgressFirewallRulesParams() + p.SetNetworkid(isoNet.Spec.ID) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) + + rules, err := c.cs.Firewall.ListEgressFirewallRules(p) + if err != nil { + return false, errors.Wrapf(err, "failed to list egress firewall rules for network ID %s", isoNet.Spec.ID) + } + + // Create a map to track found protocols + foundProtocols := make(map[string]bool) + for _, proto := range protocols { + foundProtocols[proto] = false + } + + // Check each rule for matching protocol and parameters + for _, rule := range rules.EgressFirewallRules { + if _, exists := foundProtocols[rule.Protocol]; exists { + if rule.Protocol == "icmp" { + // For ICMP, ensure icmptype and icmpcode are -1 + if rule.Icmptype == -1 && rule.Icmpcode == -1 { + foundProtocols[rule.Protocol] = true + } + } else { + // For TCP/UDP, ensure no specific ports are set (all ports) + if rule.Startport == 0 && rule.Endport == 0 { + foundProtocols[rule.Protocol] = true + } + } + } + } + + // Return true only if all required protocols are found + for _, proto := range protocols { + if !foundProtocols[proto] { + return false, nil + } + } + return true, nil +} + +// Helper function to get network details +func (c *client) getNetwork(networkID string) (*cloudstack.Network, error) { + network, count, err := c.cs.Network.GetNetworkByID(networkID, cloudstack.WithProject(c.user.Project.ID)) + if err != nil { + return nil, errors.Wrapf(err, "failed to get network by ID %s", networkID) + } + if count == 0 { + return nil, errors.Errorf("no network found with ID %s", networkID) + } + return network, nil +} + +// Helper function to create a firewall rule for a given protocol +func (c *client) createFirewallRule(isoNet *infrav1.CloudStackIsolatedNetwork, proto string) error { + if isoNet.Status.RoutingMode != "" { + // Handle routing firewall rules + p := c.cs.Firewall.NewCreateRoutingFirewallRuleParams(isoNet.Spec.ID, proto) + if proto == "icmp" { + p.SetIcmptype(-1) + p.SetIcmpcode(-1) + } + _, err := c.cs.Firewall.CreateRoutingFirewallRule(p) + if err != nil && !c.isIgnorableFirewallRuleError(err) { + return errors.Wrapf(err, "failed creating routing firewall rule for network ID %s protocol %s", isoNet.Spec.ID, proto) + } + return nil + } + + // Handle egress firewall rules + p := c.cs.Firewall.NewCreateEgressFirewallRuleParams(isoNet.Spec.ID, proto) + if proto == "icmp" { + p.SetIcmptype(-1) + p.SetIcmpcode(-1) + } + _, err := c.cs.Firewall.CreateEgressFirewallRule(p) + if err != nil && !c.isIgnorableFirewallRuleError(err) { + return errors.Wrapf(err, "failed creating egress firewall rule for network ID %s protocol %s", isoNet.Spec.ID, proto) + } + return nil +} + +// Named constants for ignorable error substrings +const ( + ErrAlreadyExists = "there is already" + ErrRuleConflict = "conflicts with rule" + ErrNewRuleConflict = "new rule conflicts with existing rule" +) + +// Helper function to check if an error is ignorable +func (c *client) isIgnorableFirewallRuleError(err error) bool { + errorMsg := strings.ToLower(err.Error()) + return strings.Contains(errorMsg, ErrAlreadyExists) || + strings.Contains(errorMsg, ErrRuleConflict) || + strings.Contains(errorMsg, ErrNewRuleConflict) } // GetPublicIP gets a public IP with ID for cluster endpoint. func (c *client) GetPublicIP( fd *infrav1.CloudStackFailureDomain, - isoNet *infrav1.CloudStackIsolatedNetwork, csCluster *infrav1.CloudStackCluster, ) (*cloudstack.PublicIpAddress, error) { ip := csCluster.Spec.ControlPlaneEndpoint.Host @@ -133,6 +310,7 @@ func (c *client) GetPublicIP( p.SetAllocatedonly(false) p.SetZoneid(fd.Spec.Zone.ID) setIfNotEmpty(ip, p.SetIpaddress) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) publicAddresses, err := c.cs.Address.ListPublicIpAddresses(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -151,39 +329,13 @@ func (c *client) GetPublicIP( return nil, errors.New("no public addresses found in available networks") } -// GetIsolatedNetwork gets an isolated network in the relevant Zone. -func (c *client) GetIsolatedNetwork(isoNet *infrav1.CloudStackIsolatedNetwork) (retErr error) { - netDetails, count, err := c.cs.Network.GetNetworkByName(isoNet.Spec.Name) - if err != nil { - c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", isoNet.Spec.Name)) - } else if count != 1 { - retErr = multierror.Append(retErr, errors.Errorf( - "expected 1 Network with name %s, but got %d", isoNet.Name, count)) - } else { // Got netID from the network's name. - isoNet.Spec.ID = netDetails.Id - return nil - } - - netDetails, count, err = c.cs.Network.GetNetworkByID(isoNet.Spec.ID) - if err != nil { - c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", isoNet.Spec.ID)) - } else if count != 1 { - return multierror.Append(retErr, errors.Errorf("expected 1 Network with UUID %s, but got %d", isoNet.Spec.ID, count)) - } - isoNet.Name = netDetails.Name - return nil -} - // ResolveLoadBalancerRuleDetails resolves the details of a load balancer rule by PublicIPID and Port. func (c *client) ResolveLoadBalancerRuleDetails( - fd *infrav1.CloudStackFailureDomain, isoNet *infrav1.CloudStackIsolatedNetwork, - csCluster *infrav1.CloudStackCluster, ) error { p := c.cs.LoadBalancer.NewListLoadBalancerRulesParams() p.SetPublicipid(isoNet.Status.PublicIPID) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) loadBalancerRules, err := c.cs.LoadBalancer.ListLoadBalancerRules(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) @@ -201,7 +353,6 @@ func (c *client) ResolveLoadBalancerRuleDetails( // GetOrCreateLoadBalancerRule Create a load balancer rule that can be assigned to instances. func (c *client) GetOrCreateLoadBalancerRule( - fd *infrav1.CloudStackFailureDomain, isoNet *infrav1.CloudStackIsolatedNetwork, csCluster *infrav1.CloudStackCluster, ) (retErr error) { @@ -217,7 +368,7 @@ func (c *client) GetOrCreateLoadBalancerRule( } // Check if rule exists. - if err := c.ResolveLoadBalancerRuleDetails(fd, isoNet, csCluster); err == nil || + if err := c.ResolveLoadBalancerRuleDetails(isoNet); err == nil || !strings.Contains(strings.ToLower(err.Error()), "no load balancer rule found") { return errors.Wrap(err, "resolving load balancer rule details") } @@ -244,14 +395,20 @@ func (c *client) GetOrCreateIsolatedNetwork( isoNet *infrav1.CloudStackIsolatedNetwork, csCluster *infrav1.CloudStackCluster, ) error { - // Get or create the isolated network itself and resolve details into passed custom resources. net := isoNet.Network() - if err := c.ResolveNetwork(net); err != nil { // Doesn't exist, create isolated network. + err := c.ResolveNetwork(net) + if err != nil { if err = c.CreateIsolatedNetwork(fd, isoNet); err != nil { return errors.Wrap(err, "creating a new isolated network") } } else { // Network existed and was resolved. Set ID on isoNet CloudStackIsolatedNetwork in case it only had name set. isoNet.Spec.ID = net.ID + isoNet.Spec.Gateway = net.Gateway + isoNet.Spec.Netmask = net.Netmask + isoNet.Status.RoutingMode = net.RoutingMode + if net.VPC != nil && net.VPC.ID != "" { + isoNet.Spec.VPC = net.VPC + } } // Tag the created network. @@ -260,14 +417,24 @@ func (c *client) GetOrCreateIsolatedNetwork( return errors.Wrapf(err, "tagging network with id %s", networkID) } - // Associate Public IP with CloudStackIsolatedNetwork - if err := c.AssociatePublicIPAddress(fd, isoNet, csCluster); err != nil { - return errors.Wrapf(err, "associating public IP address to csCluster") + // Tag the created VPC. + if net.VPC != nil && net.VPC.ID != "" { + if err := c.AddClusterTag(ResourceTypeVPC, net.VPC.ID, csCluster); err != nil { + return errors.Wrapf(err, "tagging VPC with id %s", net.VPC.ID) + } } - // Setup a load balancing rule to map VMs to Public IP. - if err := c.GetOrCreateLoadBalancerRule(fd, isoNet, csCluster); err != nil { - return errors.Wrap(err, "getting or creating load balancing rule") + // Handle control plane endpoint based on network type + if isoNet.Status.RoutingMode == "" { + // For non-routed networks, use public IP and load balancer + if err := c.AssociatePublicIPAddress(fd, isoNet, csCluster); err != nil { + return errors.Wrapf(err, "associating public IP address to csCluster") + } + + // Setup a load balancing rule to map VMs to Public IP. + if err := c.GetOrCreateLoadBalancerRule(isoNet, csCluster); err != nil { + return errors.Wrap(err, "getting or creating load balancing rule") + } } // Open the Isolated Network on endopint port. @@ -307,7 +474,6 @@ func (c *client) DeleteNetwork(net infrav1.Network) error { // DisposeIsoNetResources cleans up isolated network resources. func (c *client) DisposeIsoNetResources( - zone *infrav1.CloudStackFailureDomain, isoNet *infrav1.CloudStackIsolatedNetwork, csCluster *infrav1.CloudStackCluster, ) (retError error) { @@ -322,15 +488,22 @@ func (c *client) DisposeIsoNetResources( if err := c.RemoveClusterTagFromNetwork(csCluster, *isoNet.Network()); err != nil { return err } - if err := c.DeleteNetworkIfNotInUse(csCluster, *isoNet.Network()); err != nil { + if err := c.DeleteNetworkIfNotInUse(*isoNet.Network()); err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") { return err } - + if isoNet.Spec.VPC != nil && isoNet.Spec.VPC.ID != "" { + if err := c.RemoveClusterTagFromVPC(csCluster, *isoNet.Spec.VPC); err != nil { + return err + } + if err := c.DeleteVPCIfNotInUse(*isoNet.Spec.VPC); err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") { + return err + } + } return nil } // DeleteNetworkIfNotInUse deletes an isolated network if the network is no longer in use (indicated by in use tags). -func (c *client) DeleteNetworkIfNotInUse(csCluster *infrav1.CloudStackCluster, net infrav1.Network) (retError error) { +func (c *client) DeleteNetworkIfNotInUse(net infrav1.Network) (retError error) { tags, err := c.GetTags(ResourceTypeNetwork, net.ID) if err != nil { return err @@ -355,7 +528,7 @@ func (c *client) DeleteNetworkIfNotInUse(csCluster *infrav1.CloudStackCluster, n func (c *client) DisassociatePublicIPAddressIfNotInUse(isoNet *infrav1.CloudStackIsolatedNetwork) (retError error) { if tagsAllowDisposal, err := c.DoClusterTagsAllowDisposal(ResourceTypeIPAddress, isoNet.Status.PublicIPID); err != nil { return err - } else if publicIP, _, err := c.cs.Address.GetPublicIpAddressByID(isoNet.Status.PublicIPID); err != nil { + } else if publicIP, _, err := c.cs.Address.GetPublicIpAddressByID(isoNet.Status.PublicIPID, cloudstack.WithProject(c.user.Project.ID)); err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return err } else if publicIP == nil || publicIP.Issourcenat { // Can't disassociate an address if it's the source NAT address. diff --git a/pkg/cloud/isolated_network_test.go b/pkg/cloud/isolated_network_test.go index f5907082..841b1029 100644 --- a/pkg/cloud/isolated_network_test.go +++ b/pkg/cloud/isolated_network_test.go @@ -20,15 +20,15 @@ import ( "strconv" csapi "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "github.com/pkg/errors" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" ) -var _ = Describe("Network", func() { +var _ = ginkgo.Describe("Network", func() { const ( ipAddress = "192.168.1.14" @@ -48,9 +48,9 @@ var _ = Describe("Network", func() { client cloud.Client ) - BeforeEach(func() { + ginkgo.BeforeEach(func() { // Setup new mock services. - mockCtrl = gomock.NewController(GinkgoT()) + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = csapi.NewMockClient(mockCtrl) ns = mockClient.Network.(*csapi.MockNetworkServiceIface) nos = mockClient.NetworkOffering.(*csapi.MockNetworkOfferingServiceIface) @@ -58,24 +58,26 @@ var _ = Describe("Network", func() { as = mockClient.Address.(*csapi.MockAddressServiceIface) lbs = mockClient.LoadBalancer.(*csapi.MockLoadBalancerServiceIface) rs = mockClient.Resourcetags.(*csapi.MockResourcetagsServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) dummies.SetDummyVars() }) - AfterEach(func() { + ginkgo.AfterEach(func() { mockCtrl.Finish() }) - Context("Get or Create Isolated network in CloudStack", func() { - It("calls to create an isolated network when not found", func() { + ginkgo.Context("Get or Create Isolated network in CloudStack", func() { + ginkgo.It("calls to create an isolated network when not found", func() { dummies.Zone1.Network = dummies.ISONet1 dummies.Zone1.Network.ID = "" + dummies.CSISONet1.Spec.VPC = nil // Explicitly disable VPC for CSISONet1. + dummies.ISONet1.VPC = nil // Explicitly disable VPC for ISONet1. nos.EXPECT().GetNetworkOfferingID(gomock.Any()).Return("someOfferingID", 1, nil) - ns.EXPECT().NewCreateNetworkParams(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + ns.EXPECT().NewCreateNetworkParams(gomock.Any(), gomock.Any(), gomock.Any()). Return(&csapi.CreateNetworkParams{}) - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(nil, 0, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(nil, 0, nil) ns.EXPECT().CreateNetwork(gomock.Any()).Return(&csapi.CreateNetworkResponse{Id: dummies.ISONet1.ID}, nil) as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). @@ -84,20 +86,56 @@ var _ = Describe("Network", func() { PublicIpAddresses: []*csapi.PublicIpAddress{{Id: dummies.PublicIPID, Ipaddress: "fakeIP"}}}, nil) as.EXPECT().NewAssociateIpAddressParams().Return(&csapi.AssociateIpAddressParams{}) as.EXPECT().AssociateIpAddress(gomock.Any()) - fs.EXPECT().NewCreateEgressFirewallRuleParams(dummies.ISONet1.ID, cloud.NetworkProtocolTCP). - Return(&csapi.CreateEgressFirewallRuleParams{}) - fs.EXPECT().CreateEgressFirewallRule(&csapi.CreateEgressFirewallRuleParams{}). - Return(&csapi.CreateEgressFirewallRuleResponse{}, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(&csapi.Network{Egressdefaultpolicy: false}, 1, nil) - // Will add cluster tag once to Network and once to PublicIP. - createdByResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}} - gomock.InOrder( - rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), - rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil), - rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), - rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil)) + // Mock firewall rule check: no existing rules, so create TCP, UDP, ICMP rules + fs.EXPECT().NewListEgressFirewallRulesParams().Return(&csapi.ListEgressFirewallRulesParams{}) + fs.EXPECT().ListEgressFirewallRules(gomock.Any()).Return(&csapi.ListEgressFirewallRulesResponse{ + Count: 0, + EgressFirewallRules: []*csapi.EgressFirewallRule{}, + }, nil) - // Will add creation and cluster tags to network and PublicIP. + // Mock firewall rule creation + fs.EXPECT().NewCreateEgressFirewallRuleParams(dummies.ISONet1.ID, gomock.Any()). + DoAndReturn(func(_ string, protocol string) *csapi.CreateEgressFirewallRuleParams { + p := &csapi.CreateEgressFirewallRuleParams{} + if protocol == "icmp" { + p.SetIcmptype(-1) + p.SetIcmpcode(-1) + } + return p + }).Times(3) + + ruleParamsICMP := &csapi.CreateEgressFirewallRuleParams{} + ruleParamsICMP.SetIcmptype(-1) + ruleParamsICMP.SetIcmpcode(-1) + gomock.InOrder( + fs.EXPECT().CreateEgressFirewallRule(&csapi.CreateEgressFirewallRuleParams{}). + Return(&csapi.CreateEgressFirewallRuleResponse{}, nil).Times(2), + fs.EXPECT().CreateEgressFirewallRule(ruleParamsICMP). + Return(&csapi.CreateEgressFirewallRuleResponse{}, nil), + ) + + // Expect 6 ListTags calls: 3 for network (AddCreatedByCAPCTag, AddClusterTag x2), + // 3 for public IP (GetPublicIP, AddClusterTag, AddCreatedByCAPCTag) + emptyResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{}} + clusterTagResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}} + gomock.InOrder( + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // For AddCreatedByCAPCTag (Network) + rs.EXPECT().ListTags(gomock.Any()).Return(emptyResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // For AddClusterTag (Network, IsCapcManaged) + rs.EXPECT().ListTags(gomock.Any()).Return(clusterTagResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // For AddClusterTag (Network, AddTags) + rs.EXPECT().ListTags(gomock.Any()).Return(clusterTagResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // For GetPublicIP (PublicIpAddress) + rs.EXPECT().ListTags(gomock.Any()).Return(clusterTagResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // For AddClusterTag (PublicIpAddress) + rs.EXPECT().ListTags(gomock.Any()).Return(clusterTagResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // For AddCreatedByCAPCTag (PublicIpAddress) + rs.EXPECT().ListTags(gomock.Any()).Return(emptyResponse, nil), + ) + + // Expect 4 CreateTags calls: 2 for network (cluster + created-by), 2 for public IP (cluster + created-by) rs.EXPECT().NewCreateTagsParams(gomock.Any(), gomock.Any(), gomock.Any()). Return(&csapi.CreateTagsParams{}).Times(4) rs.EXPECT().CreateTags(gomock.Any()).Return(&csapi.CreateTagsResponse{}, nil).Times(4) @@ -107,74 +145,134 @@ var _ = Describe("Network", func() { &csapi.ListLoadBalancerRulesResponse{LoadBalancerRules: []*csapi.LoadBalancerRule{ {Publicport: strconv.Itoa(int(dummies.EndPointPort)), Id: dummies.LBRuleID}}}, nil) - Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) }) - It("fails to get network offering from CloudStack", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(nil, 0, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(nil, 0, nil) + ginkgo.It("fails to get network offering from CloudStack", func() { + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(nil, 0, nil) nos.EXPECT().GetNetworkOfferingID(gomock.Any()).Return("", -1, fakeError) err := client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("creating a new isolated network")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("creating a new isolated network")) }) }) - Context("for a closed firewall", func() { - It("OpenFirewallRule asks CloudStack to open the firewall", func() { - dummies.Zone1.Network = dummies.ISONet1 - fs.EXPECT().NewCreateEgressFirewallRuleParams(dummies.ISONet1.ID, cloud.NetworkProtocolTCP). - Return(&csapi.CreateEgressFirewallRuleParams{}) - fs.EXPECT().CreateEgressFirewallRule(&csapi.CreateEgressFirewallRuleParams{}). - Return(&csapi.CreateEgressFirewallRuleResponse{}, nil) + ginkgo.Context("OpenFirewallRules", func() { + ginkgo.It("creates firewall rules when none exist", func() { + isoNet := dummies.CSISONet1 + isoNet.Status.RoutingMode = "" // Egress rules + isoNet.Spec.ID = "net-123" + + // Mock GetNetworkByID to return a network with Egressdefaultpolicy set to false + ns.EXPECT().GetNetworkByID(isoNet.Spec.ID, gomock.Any()).Return(&csapi.Network{Egressdefaultpolicy: false}, 1, nil) - Ω(client.OpenFirewallRules(dummies.CSISONet1)).Should(Succeed()) + // Mock firewall rule check: no existing rules + fs.EXPECT().NewListEgressFirewallRulesParams().Return(&csapi.ListEgressFirewallRulesParams{}) + fs.EXPECT().ListEgressFirewallRules(gomock.Any()).Return(&csapi.ListEgressFirewallRulesResponse{ + Count: 0, + EgressFirewallRules: []*csapi.EgressFirewallRule{}, + }, nil) + + // Mock firewall rule creation for TCP, UDP, ICMP + fs.EXPECT().NewCreateEgressFirewallRuleParams(isoNet.Spec.ID, gomock.Any()). + DoAndReturn(func(_ string, protocol string) *csapi.CreateEgressFirewallRuleParams { + p := &csapi.CreateEgressFirewallRuleParams{} + if protocol == "icmp" { + p.SetIcmptype(-1) + p.SetIcmpcode(-1) + } + return p + }).Times(3) + fs.EXPECT().CreateEgressFirewallRule(gomock.Any()).Return(&csapi.CreateEgressFirewallRuleResponse{}, nil).Times(3) + + err := client.OpenFirewallRules(isoNet) + gomega.Expect(err).To(gomega.BeNil()) + gomega.Expect(isoNet.Status.FirewallRulesOpened).To(gomega.BeTrue()) }) - }) - Context("for an open firewall", func() { - It("OpenFirewallRule asks CloudStack to open the firewall anyway, but doesn't fail", func() { - dummies.Zone1.Network = dummies.ISONet1 + ginkgo.It("skips creating firewall rules if they already exist", func() { + isoNet := dummies.CSISONet1 + isoNet.Status.RoutingMode = "" // Egress rules + isoNet.Spec.ID = "net-123" + + // Mock GetNetworkByID to return a network with Egressdefaultpolicy set to false + ns.EXPECT().GetNetworkByID(isoNet.Spec.ID, gomock.Any()).Return(&csapi.Network{Egressdefaultpolicy: false}, 1, nil) + + // Mock firewall rule check: all required rules exist + fs.EXPECT().NewListEgressFirewallRulesParams().Return(&csapi.ListEgressFirewallRulesParams{}) + fs.EXPECT().ListEgressFirewallRules(gomock.Any()).Return(&csapi.ListEgressFirewallRulesResponse{ + Count: 3, + EgressFirewallRules: []*csapi.EgressFirewallRule{ + {Protocol: "tcp"}, + {Protocol: "udp"}, + {Protocol: "icmp", Icmptype: -1, Icmpcode: -1}, + }, + }, nil) + + err := client.OpenFirewallRules(isoNet) + gomega.Expect(err).To(gomega.BeNil()) + gomega.Expect(isoNet.Status.FirewallRulesOpened).To(gomega.BeTrue()) + }) + + ginkgo.It("handles error during firewall rule creation", func() { + isoNet := dummies.CSISONet1 + isoNet.Status.RoutingMode = "" // Egress rules + isoNet.Spec.ID = "net-123" - fs.EXPECT().NewCreateEgressFirewallRuleParams(dummies.ISONet1.ID, "tcp"). - Return(&csapi.CreateEgressFirewallRuleParams{}) - fs.EXPECT().CreateEgressFirewallRule(&csapi.CreateEgressFirewallRuleParams{}). - Return(&csapi.CreateEgressFirewallRuleResponse{}, errors.New("there is already a rule like this")) + // Mock GetNetworkByID to return a network with Egressdefaultpolicy set to false + ns.EXPECT().GetNetworkByID(isoNet.Spec.ID, gomock.Any()).Return(&csapi.Network{Egressdefaultpolicy: false}, 1, nil) - Ω(client.OpenFirewallRules(dummies.CSISONet1)).Should(Succeed()) + // Mock firewall rule check: no existing rules + fs.EXPECT().NewListEgressFirewallRulesParams().Return(&csapi.ListEgressFirewallRulesParams{}) + fs.EXPECT().ListEgressFirewallRules(gomock.Any()).Return(&csapi.ListEgressFirewallRulesResponse{ + Count: 0, + EgressFirewallRules: []*csapi.EgressFirewallRule{}, + }, nil) + + // Mock firewall rule creation: fail on UDP + fs.EXPECT().NewCreateEgressFirewallRuleParams(isoNet.Spec.ID, "tcp").Return(&csapi.CreateEgressFirewallRuleParams{}) + fs.EXPECT().CreateEgressFirewallRule(gomock.Any()).Return(&csapi.CreateEgressFirewallRuleResponse{}, nil) + fs.EXPECT().NewCreateEgressFirewallRuleParams(isoNet.Spec.ID, "udp").Return(&csapi.CreateEgressFirewallRuleParams{}) + fs.EXPECT().CreateEgressFirewallRule(gomock.Any()).Return(nil, errors.New("failed to create UDP rule")) + + err := client.OpenFirewallRules(isoNet) + gomega.Expect(err).To(gomega.HaveOccurred()) + gomega.Expect(err.Error()).To(gomega.ContainSubstring("failed creating egress firewall rule for network ID net-123 protocol udp")) + gomega.Expect(isoNet.Status.FirewallRulesOpened).To(gomega.BeFalse()) }) }) - Context("in an isolated network with public IPs available", func() { - It("will resolve public IP details given an endpoint spec", func() { + ginkgo.Context("in an isolated network with public IPs available", func() { + ginkgo.It("will resolve public IP details given an endpoint spec", func() { as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). Return(&csapi.ListPublicIpAddressesResponse{ Count: 1, PublicIpAddresses: []*csapi.PublicIpAddress{{Id: "PublicIPID", Ipaddress: ipAddress}}, }, nil) - publicIPAddress, err := client.GetPublicIP(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) - Ω(err).Should(Succeed()) - Ω(publicIPAddress).ShouldNot(BeNil()) - Ω(publicIPAddress.Ipaddress).Should(Equal(ipAddress)) + publicIPAddress, err := client.GetPublicIP(dummies.CSFailureDomain1, dummies.CSCluster) + gomega.Ω(err).Should(gomega.Succeed()) + gomega.Ω(publicIPAddress).ShouldNot(gomega.BeNil()) + gomega.Ω(publicIPAddress.Ipaddress).Should(gomega.Equal(ipAddress)) }) }) - Context("In an isolated network with all public IPs allocated", func() { - It("No public IP addresses available", func() { + ginkgo.Context("In an isolated network with all public IPs allocated", func() { + ginkgo.It("No public IP addresses available", func() { as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). Return(&csapi.ListPublicIpAddressesResponse{ Count: 0, PublicIpAddresses: []*csapi.PublicIpAddress{}, }, nil) - publicIPAddress, err := client.GetPublicIP(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) - Ω(publicIPAddress).Should(BeNil()) - Ω(err.Error()).Should(ContainSubstring("no public addresses found in available networks")) + publicIPAddress, err := client.GetPublicIP(dummies.CSFailureDomain1, dummies.CSCluster) + gomega.Ω(publicIPAddress).Should(gomega.BeNil()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("no public addresses found in available networks")) }) - It("All Public IPs allocated", func() { + ginkgo.It("All Public IPs allocated", func() { as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). Return(&csapi.ListPublicIpAddressesResponse{ @@ -190,14 +288,14 @@ var _ = Describe("Network", func() { Associatednetworkid: "1", }}, }, nil) - publicIPAddress, err := client.GetPublicIP(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) - Ω(publicIPAddress).Should(BeNil()) - Ω(err.Error()).Should(ContainSubstring("all Public IP Address(es) found were already allocated")) + publicIPAddress, err := client.GetPublicIP(dummies.CSFailureDomain1, dummies.CSCluster) + gomega.Ω(publicIPAddress).Should(gomega.BeNil()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("all Public IP Address(es) found were already allocated")) }) }) - Context("Associate Public IP address to Network", func() { - It("Successfully Associated Public IP to provided isolated network", func() { + ginkgo.Context("Associate Public IP address to Network", func() { + ginkgo.It("Successfully Associated Public IP to provided isolated network", func() { as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). Return(&csapi.ListPublicIpAddressesResponse{ @@ -207,21 +305,23 @@ var _ = Describe("Network", func() { aip := &csapi.AssociateIpAddressParams{} as.EXPECT().NewAssociateIpAddressParams().Return(aip) as.EXPECT().AssociateIpAddress(aip).Return(&csapi.AssociateIpAddressResponse{}, nil) - // Will add cluster tag once to Network and once to PublicIP. createdByResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}} + emptyResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{}} gomock.InOrder( - rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), - rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil)) - - // Will add creation and cluster tags to network and PublicIP. + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // AddClusterTag: IsCapcManaged + rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // AddClusterTag: AddTags + rs.EXPECT().ListTags(gomock.Any()).Return(emptyResponse, nil), + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}), // AddCreatedByCAPCTag: AddTags + rs.EXPECT().ListTags(gomock.Any()).Return(emptyResponse, nil), + ) rs.EXPECT().NewCreateTagsParams(gomock.Any(), gomock.Any(), gomock.Any()). Return(&csapi.CreateTagsParams{}).Times(2) rs.EXPECT().CreateTags(gomock.Any()).Return(&csapi.CreateTagsResponse{}, nil).Times(2) - - Ω(client.AssociatePublicIPAddress(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(client.AssociatePublicIPAddress(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) }) - It("Failure Associating Public IP to Isolated network", func() { + ginkgo.It("Failure Associating Public IP to Isolated network", func() { as.EXPECT().NewListPublicIpAddressesParams().Return(&csapi.ListPublicIpAddressesParams{}) as.EXPECT().ListPublicIpAddresses(gomock.Any()). Return(&csapi.ListPublicIpAddressesResponse{ @@ -231,57 +331,57 @@ var _ = Describe("Network", func() { aip := &csapi.AssociateIpAddressParams{} as.EXPECT().NewAssociateIpAddressParams().Return(aip) as.EXPECT().AssociateIpAddress(aip).Return(nil, errors.New("Failed to allocate IP address")) - Ω(client.AssociatePublicIPAddress(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster).Error()).Should(ContainSubstring("associating public IP address with ID")) + gomega.Ω(client.AssociatePublicIPAddress(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster).Error()).Should(gomega.ContainSubstring("associating public IP address with ID")) }) }) - Context("The specific load balancer rule does exist", func() { - It("resolves the rule's ID", func() { + ginkgo.Context("The specific load balancer rule does exist", func() { + ginkgo.It("resolves the rule's ID", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()).Return( &csapi.ListLoadBalancerRulesResponse{LoadBalancerRules: []*csapi.LoadBalancerRule{ {Publicport: strconv.Itoa(int(dummies.EndPointPort)), Id: dummies.LBRuleID}}}, nil) dummies.CSISONet1.Status.LBRuleID = "" - Ω(client.ResolveLoadBalancerRuleDetails(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) - Ω(dummies.CSISONet1.Status.LBRuleID).Should(Equal(dummies.LBRuleID)) + gomega.Ω(client.ResolveLoadBalancerRuleDetails(dummies.CSISONet1)).Should(gomega.Succeed()) + gomega.Ω(dummies.CSISONet1.Status.LBRuleID).Should(gomega.Equal(dummies.LBRuleID)) }) - It("Failed to resolve LB rule details", func() { + ginkgo.It("Failed to resolve LB rule details", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()).Return( &csapi.ListLoadBalancerRulesResponse{LoadBalancerRules: []*csapi.LoadBalancerRule{ {Publicport: "differentPublicPort", Id: dummies.LBRuleID}}}, nil) dummies.CSISONet1.Status.LBRuleID = "" - Ω(client.ResolveLoadBalancerRuleDetails(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster).Error()). - Should(Equal("no load balancer rule found")) + gomega.Ω(client.ResolveLoadBalancerRuleDetails(dummies.CSISONet1).Error()). + Should(gomega.Equal("no load balancer rule found")) }) - It("Failed to list LB rules", func() { + ginkgo.It("Failed to list LB rules", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()).Return( nil, fakeError) dummies.CSISONet1.Status.LBRuleID = "" - Ω(client.ResolveLoadBalancerRuleDetails(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster).Error()). - Should(ContainSubstring("listing load balancer rules")) + gomega.Ω(client.ResolveLoadBalancerRuleDetails(dummies.CSISONet1).Error()). + Should(gomega.ContainSubstring("listing load balancer rules")) }) - It("doesn't create a new load balancer rule on create", func() { + ginkgo.It("doesn't create a new load balancer rule on create", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()). Return(&csapi.ListLoadBalancerRulesResponse{ LoadBalancerRules: []*csapi.LoadBalancerRule{ {Publicport: strconv.Itoa(int(dummies.EndPointPort)), Id: dummies.LBRuleID}}}, nil) - Ω(client.GetOrCreateLoadBalancerRule(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) - Ω(dummies.CSISONet1.Status.LBRuleID).Should(Equal(dummies.LBRuleID)) + gomega.Ω(client.GetOrCreateLoadBalancerRule(dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(dummies.CSISONet1.Status.LBRuleID).Should(gomega.Equal(dummies.LBRuleID)) }) }) - Context("Assign VM to Load Balancer rule", func() { - It("Associates VM to LB rule", func() { + ginkgo.Context("Assign VM to Load Balancer rule", func() { + ginkgo.It("Associates VM to LB rule", func() { dummies.CSISONet1.Status.LBRuleID = "lbruleid" lbip := &csapi.ListLoadBalancerRuleInstancesParams{} albp := &csapi.AssignToLoadBalancerRuleParams{} @@ -291,10 +391,10 @@ var _ = Describe("Network", func() { lbs.EXPECT().NewAssignToLoadBalancerRuleParams(dummies.CSISONet1.Status.LBRuleID).Return(albp) lbs.EXPECT().AssignToLoadBalancerRule(albp).Return(&csapi.AssignToLoadBalancerRuleResponse{}, nil) - Ω(client.AssignVMToLoadBalancerRule(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)).Should(Succeed()) + gomega.Ω(client.AssignVMToLoadBalancerRule(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)).Should(gomega.Succeed()) }) - It("Associating VM to LB rule fails", func() { + ginkgo.It("Associating VM to LB rule fails", func() { dummies.CSISONet1.Status.LBRuleID = "lbruleid" lbip := &csapi.ListLoadBalancerRuleInstancesParams{} albp := &csapi.AssignToLoadBalancerRuleParams{} @@ -304,10 +404,10 @@ var _ = Describe("Network", func() { lbs.EXPECT().NewAssignToLoadBalancerRuleParams(dummies.CSISONet1.Status.LBRuleID).Return(albp) lbs.EXPECT().AssignToLoadBalancerRule(albp).Return(nil, fakeError) - Ω(client.AssignVMToLoadBalancerRule(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)).ShouldNot(Succeed()) + gomega.Ω(client.AssignVMToLoadBalancerRule(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)).ShouldNot(gomega.Succeed()) }) - It("LB Rule already assigned to VM", func() { + ginkgo.It("LB Rule already assigned to VM", func() { dummies.CSISONet1.Status.LBRuleID = "lbruleid" lbip := &csapi.ListLoadBalancerRuleInstancesParams{} lbs.EXPECT().NewListLoadBalancerRuleInstancesParams(dummies.CSISONet1.Status.LBRuleID). @@ -319,12 +419,12 @@ var _ = Describe("Network", func() { }}, }, nil) - Ω(client.AssignVMToLoadBalancerRule(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)).Should(Succeed()) + gomega.Ω(client.AssignVMToLoadBalancerRule(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)).Should(gomega.Succeed()) }) }) - Context("load balancer rule does not exist", func() { - It("calls cloudstack to create a new load balancer rule.", func() { + ginkgo.Context("load balancer rule does not exist", func() { + ginkgo.It("calls cloudstack to create a new load balancer rule.", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()). Return(&csapi.ListLoadBalancerRulesResponse{ @@ -334,20 +434,20 @@ var _ = Describe("Network", func() { lbs.EXPECT().CreateLoadBalancerRule(gomock.Any()). Return(&csapi.CreateLoadBalancerRuleResponse{Id: "2ndLBRuleID"}, nil) - Ω(client.GetOrCreateLoadBalancerRule(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) - Ω(dummies.CSISONet1.Status.LBRuleID).Should(Equal("2ndLBRuleID")) + gomega.Ω(client.GetOrCreateLoadBalancerRule(dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(dummies.CSISONet1.Status.LBRuleID).Should(gomega.Equal("2ndLBRuleID")) }) - It("Fails to resolve load balancer rule details", func() { + ginkgo.It("Fails to resolve load balancer rule details", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()). Return(nil, fakeError) - err := client.GetOrCreateLoadBalancerRule(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring(errorMessage)) + err := client.GetOrCreateLoadBalancerRule(dummies.CSISONet1, dummies.CSCluster) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(errorMessage)) }) - It("Fails to create a new load balancer rule.", func() { + ginkgo.It("Fails to create a new load balancer rule.", func() { lbs.EXPECT().NewListLoadBalancerRulesParams().Return(&csapi.ListLoadBalancerRulesParams{}) lbs.EXPECT().ListLoadBalancerRules(gomock.Any()). Return(&csapi.ListLoadBalancerRulesResponse{ @@ -356,44 +456,44 @@ var _ = Describe("Network", func() { Return(&csapi.CreateLoadBalancerRuleParams{}) lbs.EXPECT().CreateLoadBalancerRule(gomock.Any()). Return(nil, fakeError) - err := client.GetOrCreateLoadBalancerRule(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(Equal(errorMessage)) + err := client.GetOrCreateLoadBalancerRule(dummies.CSISONet1, dummies.CSCluster) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal(errorMessage)) }) }) - Context("Delete Network", func() { - It("Calls CloudStack to delete network", func() { + ginkgo.Context("Delete Network", func() { + ginkgo.It("Calls CloudStack to delete network", func() { dnp := &csapi.DeleteNetworkParams{} ns.EXPECT().NewDeleteNetworkParams(dummies.ISONet1.ID).Return(dnp) ns.EXPECT().DeleteNetwork(dnp).Return(&csapi.DeleteNetworkResponse{}, nil) - Ω(client.DeleteNetwork(dummies.ISONet1)).Should(Succeed()) + gomega.Ω(client.DeleteNetwork(dummies.ISONet1)).Should(gomega.Succeed()) }) - It("Network deletion failure", func() { + ginkgo.It("Network deletion failure", func() { dnp := &csapi.DeleteNetworkParams{} ns.EXPECT().NewDeleteNetworkParams(dummies.ISONet1.ID).Return(dnp) ns.EXPECT().DeleteNetwork(dnp).Return(nil, fakeError) err := client.DeleteNetwork(dummies.ISONet1) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("deleting network with id " + dummies.ISONet1.ID)) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("deleting network with id " + dummies.ISONet1.ID)) }) }) - Context("Dispose or cleanup isolate network resources", func() { - It("delete all isolated network resources when not managed by CAPC", func() { + ginkgo.Context("Dispose or cleanup isolate network resources", func() { + ginkgo.It("delete all isolated network resources when not managed by CAPC", func() { dummies.CSISONet1.Status.PublicIPID = "publicIpId" rtlp := &csapi.ListTagsParams{} rs.EXPECT().NewListTagsParams().Return(rtlp).Times(4) rs.EXPECT().ListTags(rtlp).Return(&csapi.ListTagsResponse{}, nil).Times(4) - as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID).Return(&csapi.PublicIpAddress{}, 1, nil) + as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID, gomock.Any()).Return(&csapi.PublicIpAddress{}, 1, nil) - Ω(client.DisposeIsoNetResources(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(client.DisposeIsoNetResources(dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) }) - It("delete all isolated network resources when managed by CAPC", func() { + ginkgo.It("delete all isolated network resources when managed by CAPC", func() { dummies.CSISONet1.Status.PublicIPID = "publicIpId" rtdp := &csapi.DeleteTagsParams{} rtlp := &csapi.ListTagsParams{} @@ -404,14 +504,14 @@ var _ = Describe("Network", func() { rs.EXPECT().NewListTagsParams().Return(rtlp).Times(4) rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil).Times(3) rs.EXPECT().ListTags(rtlp).Return(&csapi.ListTagsResponse{}, nil).Times(1) - as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID).Return(&csapi.PublicIpAddress{}, 1, nil) + as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID, gomock.Any()).Return(&csapi.PublicIpAddress{}, 1, nil) as.EXPECT().NewDisassociateIpAddressParams(dummies.CSISONet1.Status.PublicIPID).Return(dap) as.EXPECT().DisassociateIpAddress(dap).Return(&csapi.DisassociateIpAddressResponse{}, nil) - Ω(client.DisposeIsoNetResources(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(client.DisposeIsoNetResources(dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) }) - It("disassociate IP address fails due to failure in deleting a resource i.e., disassociate Public IP", func() { + ginkgo.It("disassociate IP address fails due to failure in deleting a resource i.e., disassociate Public IP", func() { dummies.CSISONet1.Status.PublicIPID = "publicIpId" rtdp := &csapi.DeleteTagsParams{} rtlp := &csapi.ListTagsParams{} @@ -421,27 +521,27 @@ var _ = Describe("Network", func() { rs.EXPECT().DeleteTags(rtdp).Return(&csapi.DeleteTagsResponse{}, nil).Times(2) rs.EXPECT().NewListTagsParams().Return(rtlp).Times(2) rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil).Times(2) - as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID).Return(&csapi.PublicIpAddress{}, 1, nil) + as.EXPECT().GetPublicIpAddressByID(dummies.CSISONet1.Status.PublicIPID, gomock.Any()).Return(&csapi.PublicIpAddress{}, 1, nil) as.EXPECT().NewDisassociateIpAddressParams(dummies.CSISONet1.Status.PublicIPID).Return(dap) as.EXPECT().DisassociateIpAddress(dap).Return(nil, fakeError) - Ω(client.DisposeIsoNetResources(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).ShouldNot(Succeed()) + gomega.Ω(client.DisposeIsoNetResources(dummies.CSISONet1, dummies.CSCluster)).ShouldNot(gomega.Succeed()) }) }) - Context("Networking Integ Tests", Label("integ"), func() { - BeforeEach(func() { + ginkgo.Context("Networking Integ Tests", ginkgo.Label("integ"), func() { + ginkgo.BeforeEach(func() { client = realCloudClient // Delete any existing tags existingTags, err := client.GetTags(cloud.ResourceTypeNetwork, dummies.Net1.ID) if err != nil { - Fail("Failed to get existing tags. Error: " + err.Error()) + ginkgo.Fail("Failed to get existing tags. Error: " + err.Error()) } if len(existingTags) != 0 { err = client.DeleteTags(cloud.ResourceTypeNetwork, dummies.Net1.ID, existingTags) if err != nil { - Fail("Failed to delete existing tags. Error: " + err.Error()) + ginkgo.Fail("Failed to delete existing tags. Error: " + err.Error()) } } dummies.SetDummyVars() @@ -454,32 +554,32 @@ var _ = Describe("Network", func() { FetchIntegTestResources() }) - It("fetches an isolated network", func() { + ginkgo.It("fetches an isolated network", func() { dummies.SetDummyIsoNetToNameOnly() dummies.SetClusterSpecToNet(&dummies.ISONet1) - Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) - Ω(dummies.ISONet1.ID).ShouldNot(BeEmpty()) - Ω(dummies.ISONet1.Type).Should(Equal(cloud.NetworkTypeIsolated)) + gomega.Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(gomega.Succeed()) + gomega.Ω(dummies.ISONet1.ID).ShouldNot(gomega.BeEmpty()) + gomega.Ω(dummies.ISONet1.Type).Should(gomega.Equal(cloud.NetworkTypeIsolated)) }) - It("fetches a public IP", func() { + ginkgo.It("fetches a public IP", func() { dummies.Zone1.ID = "" dummies.SetDummyIsoNetToNameOnly() dummies.SetClusterSpecToNet(&dummies.ISONet1) dummies.CSCluster.Spec.ControlPlaneEndpoint.Host = "" - Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) + gomega.Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(gomega.Succeed()) }) - It("adds an isolated network and doesn't fail when asked to GetOrCreateIsolatedNetwork multiple times", func() { - Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) - Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + ginkgo.It("adds an isolated network and doesn't fail when asked to GetOrCreateIsolatedNetwork multiple times", func() { + gomega.Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) // Network should now exist if it didn't at the start. - Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) + gomega.Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(gomega.Succeed()) // Do once more. - Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(client.GetOrCreateIsolatedNetwork(dummies.CSFailureDomain1, dummies.CSISONet1, dummies.CSCluster)).Should(gomega.Succeed()) }) }) }) diff --git a/pkg/cloud/network.go b/pkg/cloud/network.go index cc7e11fd..7adce7da 100644 --- a/pkg/cloud/network.go +++ b/pkg/cloud/network.go @@ -17,9 +17,10 @@ limitations under the License. package cloud import ( + "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ) type NetworkIface interface { @@ -29,10 +30,13 @@ type NetworkIface interface { const ( NetOffering = "DefaultIsolatedNetworkOfferingWithSourceNatService" + NetVPCOffering = "DefaultIsolatedNetworkOfferingForVpcNetworks" K8sDefaultAPIPort = 6443 NetworkTypeIsolated = "Isolated" NetworkTypeShared = "Shared" NetworkProtocolTCP = "tcp" + NetworkProtocolUDP = "udp" + NetworkProtocolICMP = "icmp" ) // NetworkExists checks that the network already exists based on the presence of all fields. @@ -49,7 +53,7 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { // TODO rebuild this to consider cases with networks in many zones. // Use ListNetworks instead. netName := net.Name - netDetails, count, err := c.cs.Network.GetNetworkByName(netName) + netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", netName)) @@ -59,11 +63,22 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { } else { // Got netID from the network's name. net.ID = netDetails.Id net.Type = netDetails.Type + net.Gateway = netDetails.Gateway + net.Netmask = netDetails.Netmask + net.Offering = netDetails.Networkofferingname + net.RoutingMode = netDetails.Ip4routing + if netDetails.Vpcid != "" { + if net.VPC == nil { + net.VPC = &infrav1.VPC{} + } + net.VPC.ID = netDetails.Vpcid + net.VPC.Name = netDetails.Vpcname + } return nil } // Now get network details. - netDetails, count, err = c.cs.Network.GetNetworkByID(net.ID) + netDetails, count, err = c.cs.Network.GetNetworkByID(net.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", net.ID)) } else if count != 1 { @@ -73,6 +88,17 @@ func (c *client) ResolveNetwork(net *infrav1.Network) (retErr error) { net.Name = netDetails.Name net.ID = netDetails.Id net.Type = netDetails.Type + net.Gateway = netDetails.Gateway + net.Netmask = netDetails.Netmask + net.Offering = netDetails.Networkofferingname + net.RoutingMode = netDetails.Ip4routing + if netDetails.Vpcid != "" { + if net.VPC == nil { + net.VPC = &infrav1.VPC{} + } + net.VPC.ID = netDetails.Vpcid + net.VPC.Name = netDetails.Vpcname + } return nil } diff --git a/pkg/cloud/network_test.go b/pkg/cloud/network_test.go index 56c85bdf..d149abac 100644 --- a/pkg/cloud/network_test.go +++ b/pkg/cloud/network_test.go @@ -21,14 +21,14 @@ import ( "fmt" csapi "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" ) -var _ = Describe("Network", func() { +var _ = ginkgo.Describe("Network", func() { var ( // Declare shared vars. mockCtrl *gomock.Controller mockClient *csapi.CloudStackClient @@ -37,45 +37,45 @@ var _ = Describe("Network", func() { client cloud.Client ) - BeforeEach(func() { + ginkgo.BeforeEach(func() { // Setup new mock services. - mockCtrl = gomock.NewController(GinkgoT()) + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = csapi.NewMockClient(mockCtrl) ns = mockClient.Network.(*csapi.MockNetworkServiceIface) rs = mockClient.Resourcetags.(*csapi.MockResourcetagsServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) dummies.SetDummyVars() }) - AfterEach(func() { + ginkgo.AfterEach(func() { mockCtrl.Finish() }) - Context("for an existing network", func() { - It("resolves network by ID", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(nil, 0, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) + ginkgo.Context("for an existing network", func() { + ginkgo.It("resolves network by ID", func() { + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(nil, 0, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) - Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) + gomega.Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(gomega.Succeed()) }) - It("resolves network by Name", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) + ginkgo.It("resolves network by Name", func() { + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 1, nil) - Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(Succeed()) + gomega.Ω(client.ResolveNetwork(&dummies.ISONet1)).Should(gomega.Succeed()) }) - It("When there exists more than one network with the same name", func() { - ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 2, nil) - ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID).Return(nil, 2, errors.New("There is more then one result for Network UUID")) + ginkgo.It("When there exists more than one network with the same name", func() { + ns.EXPECT().GetNetworkByName(dummies.ISONet1.Name, gomock.Any()).Return(dummies.CAPCNetToCSAPINet(&dummies.ISONet1), 2, nil) + ns.EXPECT().GetNetworkByID(dummies.ISONet1.ID, gomock.Any()).Return(nil, 2, errors.New("There is more then one result for Network UUID")) err := client.ResolveNetwork(&dummies.ISONet1) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring(fmt.Sprintf("expected 1 Network with name %s, but got %d", dummies.ISONet1.Name, 2))) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("expected 1 Network with name %s, but got %d", dummies.ISONet1.Name, 2))) }) }) - Context("Remove cluster tag from network", func() { - It("Remove tag from network", func() { + ginkgo.Context("Remove cluster tag from network", func() { + ginkgo.It("Remove tag from network", func() { rtdp := &csapi.DeleteTagsParams{} createdByCAPCResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: dummies.CSClusterTagKey, Value: "1"}}} rtlp := &csapi.ListTagsParams{} @@ -83,7 +83,7 @@ var _ = Describe("Network", func() { rs.EXPECT().DeleteTags(rtdp).Return(&csapi.DeleteTagsResponse{}, nil) rs.EXPECT().NewListTagsParams().Return(rtlp) rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil) - Ω(client.RemoveClusterTagFromNetwork(dummies.CSCluster, dummies.ISONet1)).Should(Succeed()) + gomega.Ω(client.RemoveClusterTagFromNetwork(dummies.CSCluster, dummies.ISONet1)).Should(gomega.Succeed()) }) }) }) diff --git a/pkg/cloud/tags.go b/pkg/cloud/tags.go index 79bd813a..57b73306 100644 --- a/pkg/cloud/tags.go +++ b/pkg/cloud/tags.go @@ -22,7 +22,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/pkg/errors" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ) type TagIface interface { @@ -45,15 +45,6 @@ const ( ResourceTypeIPAddress ResourceType = "PublicIpAddress" ) -// ignoreAlreadyPresentErrors returns nil if the error is an already present tag error. -func ignoreAlreadyPresentErrors(err error, rType ResourceType, rID string) error { - matchSubString := strings.ToLower("already on " + string(rType) + " with id " + rID) - if err != nil && !strings.Contains(strings.ToLower(err.Error()), matchSubString) { - return err - } - return nil -} - func (c *client) IsCapcManaged(resourceType ResourceType, resourceID string) (bool, error) { tags, err := c.GetTags(resourceType, resourceID) if err != nil { @@ -117,10 +108,30 @@ func (c *client) DoClusterTagsAllowDisposal(resourceType ResourceType, resourceI // AddTags adds arbitrary tags to a resource. func (c *client) AddTags(resourceType ResourceType, resourceID string, tags map[string]string) error { - p := c.cs.Resourcetags.NewCreateTagsParams([]string{resourceID}, string(resourceType), tags) - _, err := c.cs.Resourcetags.CreateTags(p) - c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) - return ignoreAlreadyPresentErrors(err, resourceType, resourceID) + // Retrieve existing tags + existingTags, err := c.GetTags(resourceType, resourceID) + if err != nil { + return errors.Wrapf(err, "fetching tags for resource %s with ID %s", resourceType, resourceID) + } + + // Identify tags that need to be added or updated + tagsToAdd := make(map[string]string) + for key, value := range tags { + if existingValue, exists := existingTags[key]; !exists || existingValue != value { + tagsToAdd[key] = value + } + } + + // If there are tags to add, call the API + if len(tagsToAdd) > 0 { + p := c.cs.Resourcetags.NewCreateTagsParams([]string{resourceID}, string(resourceType), tagsToAdd) + if _, err := c.cs.Resourcetags.CreateTags(p); err != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) + return errors.Wrapf(err, "creating tags for resource %s with ID %s", resourceType, resourceID) + } + } + + return nil } // GetTags gets all of a resource's tags. @@ -129,6 +140,7 @@ func (c *client) GetTags(resourceType ResourceType, resourceID string) (map[stri p.SetResourceid(resourceID) p.SetResourcetype(string(resourceType)) p.SetListall(true) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) listTagResponse, err := c.cs.Resourcetags.ListTags(p) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) diff --git a/pkg/cloud/tags_test.go b/pkg/cloud/tags_test.go index 89c165af..459bb77c 100644 --- a/pkg/cloud/tags_test.go +++ b/pkg/cloud/tags_test.go @@ -18,15 +18,15 @@ package cloud_test import ( csapi "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "github.com/pkg/errors" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" ) -var _ = Describe("Tag Unit Tests", func() { +var _ = ginkgo.Describe("Tag Unit Tests", func() { const ( errorMessage = "Error" ) @@ -39,133 +39,135 @@ var _ = Describe("Tag Unit Tests", func() { client cloud.Client ) - BeforeEach(func() { + ginkgo.BeforeEach(func() { dummies.SetDummyVars() - mockCtrl = gomock.NewController(GinkgoT()) + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = csapi.NewMockClient(mockCtrl) rs = mockClient.Resourcetags.(*csapi.MockResourcetagsServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) }) - Context("Tag Integ Tests", Label("integ"), func() { - BeforeEach(func() { + ginkgo.Context("Tag Integ Tests", ginkgo.Label("integ"), func() { + ginkgo.BeforeEach(func() { client = realCloudClient FetchIntegTestResources() existingTags, err := client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID) if err != nil { - Fail("Failed to get existing tags. Error: " + err.Error()) + ginkgo.Fail("Failed to get existing tags. Error: " + err.Error()) } if len(existingTags) > 0 { err = client.DeleteTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, existingTags) if err != nil { - Fail("Failed to delete existing tags. Error: " + err.Error()) + ginkgo.Fail("Failed to delete existing tags. Error: " + err.Error()) } } }) - It("adds and gets a resource tag", func() { - Ω(client.AddTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(Succeed()) - Ω(client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Equal(dummies.Tags)) + ginkgo.It("adds and gets a resource tag", func() { + gomega.Ω(client.AddTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(gomega.Succeed()) + gomega.Ω(client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Equal(dummies.Tags)) }) - It("deletes a resource tag", func() { - Ω(client.AddTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(Succeed()) - Ω(client.DeleteTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(Succeed()) - Ω(client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Equal(map[string]string{})) + ginkgo.It("deletes a resource tag", func() { + gomega.Ω(client.AddTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(gomega.Succeed()) + gomega.Ω(client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Equal(map[string]string{})) }) - It("returns an error when you delete a tag that doesn't exist", func() { - Ω(client.DeleteTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(Succeed()) + ginkgo.It("returns an error when you delete a tag that doesn't exist", func() { + gomega.Ω(client.DeleteTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.Tags)).Should(gomega.Succeed()) }) - It("adds the tags for a cluster (resource created by CAPC)", func() { - Ω(client.AddCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)). - Should(Succeed()) - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)). - Should(Succeed()) + ginkgo.It("adds the tags for a cluster (resource created by CAPC)", func() { + gomega.Ω(client.AddCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)). + Should(gomega.Succeed()) + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)). + Should(gomega.Succeed()) // Verify tags tags, err := client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) - Ω(tags[dummies.CSClusterTagKey]).Should(Equal(dummies.CSClusterTagVal)) + gomega.Ω(tags[dummies.CSClusterTagKey]).Should(gomega.Equal(dummies.CSClusterTagVal)) }) - It("does not fail when the cluster tags are added twice", func() { - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) + ginkgo.It("does not fail when the cluster tags are added twice", func() { + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) }) - It("doesn't adds the tags for a cluster (resource NOT created by CAPC)", func() { - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) + ginkgo.It("doesn't adds the tags for a cluster (resource NOT created by CAPC)", func() { + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) // Verify tags tags, err := client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID) - Ω(err).Should(BeNil()) - Ω(tags[dummies.CreatedByCapcKey]).Should(Equal("")) - Ω(tags[dummies.CSClusterTagKey]).Should(Equal("")) + gomega.Ω(err).Should(gomega.BeNil()) + gomega.Ω(tags[dummies.CreatedByCapcKey]).Should(gomega.Equal("")) + gomega.Ω(tags[dummies.CSClusterTagKey]).Should(gomega.Equal("")) }) - It("deletes a cluster tag", func() { - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) - Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) + ginkgo.It("deletes a cluster tag", func() { + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) - Ω(client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).ShouldNot(HaveKey(dummies.CSClusterTagKey)) + gomega.Ω(client.GetTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).ShouldNot(gomega.HaveKey(dummies.CSClusterTagKey)) }) - It("adds and deletes a created by capc tag", func() { - Ω(client.AddCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Succeed()) - Ω(client.DeleteCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Succeed()) + ginkgo.It("adds and deletes a created by capc tag", func() { + gomega.Ω(client.AddCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Succeed()) }) - It("does not fail when cluster and CAPC created tags are deleted twice", func() { - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) - Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) - Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) - Ω(client.DeleteCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Succeed()) - Ω(client.DeleteCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Succeed()) + ginkgo.It("does not fail when cluster and CAPC created tags are deleted twice", func() { + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Succeed()) }) - It("does not allow a resource to be deleted when there are no tags", func() { + ginkgo.It("does not allow a resource to be deleted when there are no tags", func() { tagsAllowDisposal, err := client.DoClusterTagsAllowDisposal(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID) - Ω(err).Should(BeNil()) - Ω(tagsAllowDisposal).Should(BeFalse()) + gomega.Ω(err).Should(gomega.BeNil()) + gomega.Ω(tagsAllowDisposal).Should(gomega.BeFalse()) }) - It("does not allow a resource to be deleted when there is a cluster tag", func() { - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) + ginkgo.It("does not allow a resource to be deleted when there is a cluster tag", func() { + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) tagsAllowDisposal, err := client.DoClusterTagsAllowDisposal(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID) - Ω(err).Should(BeNil()) - Ω(tagsAllowDisposal).Should(BeFalse()) + gomega.Ω(err).Should(gomega.BeNil()) + gomega.Ω(tagsAllowDisposal).Should(gomega.BeFalse()) }) - It("does allow a resource to be deleted when there are no cluster tags and there is a CAPC created tag", func() { - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) - Ω(client.AddCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(Succeed()) - Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) + ginkgo.It("does allow a resource to be deleted when there are no cluster tags and there is a CAPC created tag", func() { + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) + gomega.Ω(client.AddCreatedByCAPCTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID)).Should(gomega.Succeed()) + gomega.Ω(client.DeleteClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) tagsAllowDisposal, err := client.DoClusterTagsAllowDisposal(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID) - Ω(err).Should(BeNil()) - Ω(tagsAllowDisposal).Should(BeTrue()) + gomega.Ω(err).Should(gomega.BeNil()) + gomega.Ω(tagsAllowDisposal).Should(gomega.BeTrue()) }) }) - Context("Add cluster tag", func() { - It("Add cluster tag if managed by CAPC", func() { + ginkgo.Context("Add cluster tag", func() { + ginkgo.It("Add cluster tag if managed by CAPC", func() { createdByCAPCResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}} rtlp := &csapi.ListTagsParams{} ctp := &csapi.CreateTagsParams{} - rs.EXPECT().NewListTagsParams().Return(rtlp) - rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil) + // Expecting NewListTagsParams to be called twice: + // Once for verifying existing tags and once for creating new tags. + rs.EXPECT().NewListTagsParams().Return(rtlp).Times(2) + rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil).Times(2) rs.EXPECT().NewCreateTagsParams(gomock.Any(), gomock.Any(), gomock.Any()).Return(ctp) rs.EXPECT().CreateTags(ctp).Return(&csapi.CreateTagsResponse{}, nil) - Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(Succeed()) + gomega.Ω(client.AddClusterTag(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, dummies.CSCluster)).Should(gomega.Succeed()) }) }) - Context("Delete tags", func() { - It("delete resource tags fails", func() { + ginkgo.Context("Delete tags", func() { + ginkgo.It("delete resource tags fails", func() { tags := map[string]string{ "key1": "value1", } @@ -182,18 +184,18 @@ var _ = Describe("Tag Unit Tests", func() { }, nil) err := client.DeleteTags(cloud.ResourceTypeNetwork, dummies.CSISONet1.Spec.ID, tags) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("could not remove tag")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("could not remove tag")) }) }) - Context("Get tags for a resource", func() { - It("listing tags for a resource fails", func() { + ginkgo.Context("Get tags for a resource", func() { + ginkgo.It("listing tags for a resource fails", func() { rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}) rs.EXPECT().ListTags(gomock.Any()).Return(nil, fakeError) _, err := client.GetTags(cloud.ResourceTypeNetwork, dummies.ISONet1.ID) - Ω(err).ShouldNot(Succeed()) + gomega.Ω(err).ShouldNot(gomega.Succeed()) }) }) }) diff --git a/pkg/cloud/user_credentials.go b/pkg/cloud/user_credentials.go index 5e413c84..e2201373 100644 --- a/pkg/cloud/user_credentials.go +++ b/pkg/cloud/user_credentials.go @@ -37,16 +37,31 @@ type UserCredIFace interface { // Domain contains specifications that identify a domain. type Domain struct { - Name string - Path string - ID string + Name string + Path string + ID string + CPUAvailable string + MemoryAvailable string + VMAvailable string } // Account contains specifications that identify an account. type Account struct { - Name string - Domain Domain - ID string + Name string + Domain Domain + ID string + CPUAvailable string + MemoryAvailable string + VMAvailable string +} + +// Project contains specifications that identify a project. +type Project struct { + Name string + ID string + CPUAvailable string + MemoryAvailable string + VMAvailable string } // User contains information uniquely identifying and scoping a user. @@ -56,6 +71,7 @@ type User struct { APIKey string SecretKey string Account + Project } // ResolveDomain resolves a domain's information. @@ -105,6 +121,9 @@ func (c *client) ResolveDomain(domain *Domain) error { } domain.Path = resp.Domains[0].Path domain.Name = resp.Domains[0].Name + domain.CPUAvailable = resp.Domains[0].Cpuavailable + domain.MemoryAvailable = resp.Domains[0].Memoryavailable + domain.VMAvailable = resp.Domains[0].Vmavailable return nil } @@ -120,6 +139,9 @@ func (c *client) ResolveDomain(domain *Domain) error { for _, possibleDomain := range resp.Domains { if possibleDomain.Path == domain.Path { domain.ID = possibleDomain.Id + domain.CPUAvailable = possibleDomain.Cpuavailable + domain.MemoryAvailable = possibleDomain.Memoryavailable + domain.VMAvailable = possibleDomain.Vmavailable return nil } } @@ -130,7 +152,8 @@ func (c *client) ResolveDomain(domain *Domain) error { // ResolveAccount resolves an account's information. func (c *client) ResolveAccount(account *Account) error { // Resolve domain prior to any account resolution activity. - if err := c.ResolveDomain(&account.Domain); err != nil { + if err := c.ResolveDomain(&account.Domain); err != nil && + !strings.Contains(err.Error(), "The API [listDomains] does not exist or is not available for the account Account") { return errors.Wrapf(err, "resolving domain %s details", account.Domain.Name) } @@ -150,6 +173,39 @@ func (c *client) ResolveAccount(account *Account) error { } account.ID = resp.Accounts[0].Id account.Name = resp.Accounts[0].Name + account.CPUAvailable = resp.Accounts[0].Cpuavailable + account.MemoryAvailable = resp.Accounts[0].Memoryavailable + account.VMAvailable = resp.Accounts[0].Vmavailable + return nil +} + +// ResolveProject resolves a project's information. +func (c *client) ResolveProject(user *User) error { + if user.Project.Name == "" { + return nil + } + + p := c.cs.Project.NewListProjectsParams() + p.SetListall(true) + p.SetDomainid(user.Domain.ID) + p.SetAccount(user.Account.Name) + p.SetName(user.Project.Name) + setIfNotEmpty(user.Project.ID, p.SetId) + resp, retErr := c.cs.Project.ListProjects(p) + if retErr != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) + return retErr + } else if resp.Count == 0 { + return errors.Errorf("could not find project %s", user.Project.Name) + } else if resp.Count != 1 { + return errors.Errorf("expected 1 Project with name %s in domain ID %s, but got %d", + user.Project.Name, user.Domain.ID, resp.Count) + } + user.Project.ID = resp.Projects[0].Id + user.Project.Name = resp.Projects[0].Name + user.Project.CPUAvailable = resp.Projects[0].Cpuavailable + user.Project.MemoryAvailable = resp.Projects[0].Memoryavailable + user.Project.VMAvailable = resp.Projects[0].Vmavailable return nil } @@ -205,6 +261,10 @@ func (c *client) GetUserWithKeys(user *User) (bool, error) { return false, errors.Wrapf(err, "resolving account %s details", user.Account.Name) } + if err := c.ResolveProject(user); err != nil { + return false, errors.Wrapf(err, "resolving project %s details", user.Project.Name) + } + // List users and take first user that has already has api keys. p := c.cs.User.NewListUsersParams() p.SetAccount(user.Account.Name) diff --git a/pkg/cloud/user_credentials_test.go b/pkg/cloud/user_credentials_test.go index efd94b04..eff18590 100644 --- a/pkg/cloud/user_credentials_test.go +++ b/pkg/cloud/user_credentials_test.go @@ -21,16 +21,16 @@ import ( "fmt" csapi "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" "sigs.k8s.io/cluster-api-provider-cloudstack/test/helpers" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" ) -var _ = Describe("User Credentials", func() { +var _ = ginkgo.Describe("User Credentials", func() { const ( errorMessage = "Error" ) @@ -44,25 +44,25 @@ var _ = Describe("User Credentials", func() { us *csapi.MockUserServiceIface ) - BeforeEach(func() { - mockCtrl = gomock.NewController(GinkgoT()) + ginkgo.BeforeEach(func() { + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = csapi.NewMockClient(mockCtrl) ds = mockClient.Domain.(*csapi.MockDomainServiceIface) as = mockClient.Account.(*csapi.MockAccountServiceIface) us = mockClient.User.(*csapi.MockUserServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) dummies.SetDummyVars() // dummies.SetDummyClusterStatus() // dummies.SetDummyCSMachineStatuses() dummies.SetDummyCAPCClusterVars() }) - AfterEach(func() { + ginkgo.AfterEach(func() { mockCtrl.Finish() }) - Context("Get domain in CloudStack", func() { - It("search for CloudStack domain", func() { + ginkgo.Context("Get domain in CloudStack", func() { + ginkgo.It("search for CloudStack domain", func() { dummies.Domain.Path = "domainPath1" dsp := &csapi.ListDomainsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -71,10 +71,10 @@ var _ = Describe("User Credentials", func() { Path: "ROOT/domainPath1", }}}, nil) - Ω(client.ResolveDomain(&dummies.Domain)).Should(Succeed()) + gomega.Ω(client.ResolveDomain(&dummies.Domain)).Should(gomega.Succeed()) }) - It("search for CloudStack domain with incorrect domain path", func() { + ginkgo.It("search for CloudStack domain with incorrect domain path", func() { dummies.Domain.Path = "/domainPath1" dsp := &csapi.ListDomainsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -84,11 +84,11 @@ var _ = Describe("User Credentials", func() { }}}, nil) err := client.ResolveDomain(&dummies.Domain) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(Equal(fmt.Sprintf("domain Path %s did not match domain ID %s", dummies.Domain.Path, dummies.Domain.ID))) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal(fmt.Sprintf("domain Path %s did not match domain ID %s", dummies.Domain.Path, dummies.Domain.ID))) }) - It("search for CloudStack domain returns more than one domain", func() { + ginkgo.It("search for CloudStack domain returns more than one domain", func() { dummies.Domain.Path = "domainPath1" dsp := &csapi.ListDomainsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -98,11 +98,11 @@ var _ = Describe("User Credentials", func() { }}}, nil) err := client.ResolveDomain(&dummies.Domain) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(Equal(fmt.Sprintf("domain ID %s provided, expected exactly one domain, got %d", dummies.Domain.ID, 2))) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal(fmt.Sprintf("domain ID %s provided, expected exactly one domain, got %d", dummies.Domain.ID, 2))) }) - It("search for CloudStack domain when only domain Name is provided", func() { + ginkgo.It("search for CloudStack domain when only domain Name is provided", func() { dummies.Domain.ID = "" dsp := &csapi.ListDomainsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -110,10 +110,10 @@ var _ = Describe("User Credentials", func() { Name: "domainName", }}}, nil) - Ω(client.ResolveDomain(&dummies.Domain)).Should(Succeed()) + gomega.Ω(client.ResolveDomain(&dummies.Domain)).Should(gomega.Succeed()) }) - It("search for CloudStack domain when only domain Name is provided, but returns > 1 domain", func() { + ginkgo.It("search for CloudStack domain when only domain Name is provided, but returns > 1 domain", func() { dummies.Domain.ID = "" dsp := &csapi.ListDomainsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -122,13 +122,13 @@ var _ = Describe("User Credentials", func() { }}}, nil) err := client.ResolveDomain(&dummies.Domain) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(Equal(fmt.Sprintf("only domain name: %s provided, expected exactly one domain, got %d", dummies.Domain.Name, 2))) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal(fmt.Sprintf("only domain name: %s provided, expected exactly one domain, got %d", dummies.Domain.Name, 2))) }) }) - Context("Get Account in CloudStack", func() { - It("search for account in CloudStack", func() { + ginkgo.Context("Get Account in CloudStack", func() { + ginkgo.It("search for account in CloudStack", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -142,11 +142,11 @@ var _ = Describe("User Credentials", func() { Name: dummies.AccountName, }}}, nil) - Ω(client.ResolveAccount(&dummies.Account)).Should(Succeed()) + gomega.Ω(client.ResolveAccount(&dummies.Account)).Should(gomega.Succeed()) }) - It("no account found in CloudStack for the provided Account name", func() { + ginkgo.It("no account found in CloudStack for the provided Account name", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -158,11 +158,11 @@ var _ = Describe("User Credentials", func() { as.EXPECT().ListAccounts(asp).Return(&csapi.ListAccountsResponse{Count: 0, Accounts: []*csapi.Account{}}, nil) err := client.ResolveAccount(&dummies.Account) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("could not find account")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("could not find account")) }) - It("More than one account found in the provided domain and account name", func() { + ginkgo.It("More than one account found in the provided domain and account name", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -174,11 +174,11 @@ var _ = Describe("User Credentials", func() { as.EXPECT().ListAccounts(asp).Return(&csapi.ListAccountsResponse{Count: 2, Accounts: []*csapi.Account{}}, nil) err := client.ResolveAccount(&dummies.Account) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("expected 1 Account with account name")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("expected 1 Account with account name")) }) - It("fails to list accounts", func() { + ginkgo.It("fails to list accounts", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -189,16 +189,16 @@ var _ = Describe("User Credentials", func() { as.EXPECT().NewListAccountsParams().Return(asp) as.EXPECT().ListAccounts(asp).Return(nil, fakeError) - Ω(client.ResolveAccount(&dummies.Account)).ShouldNot(Succeed()) + gomega.Ω(client.ResolveAccount(&dummies.Account)).ShouldNot(gomega.Succeed()) }) }) - Context("Get User from CloudStack", func() { - BeforeEach(func() { + ginkgo.Context("Get User from CloudStack", func() { + ginkgo.BeforeEach(func() { dummies.SetDummyUserVars() }) - It("search for user in CloudStack", func() { + ginkgo.It("search for user in CloudStack", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} usp := &csapi.ListUsersParams{} @@ -221,10 +221,10 @@ var _ = Describe("User Credentials", func() { }}, }, nil) - Ω(client.ResolveUser(&dummies.User)).Should(Succeed()) + gomega.Ω(client.ResolveUser(&dummies.User)).Should(gomega.Succeed()) }) - It("search for user fails while resolving account in CloudStack", func() { + ginkgo.It("search for user fails while resolving account in CloudStack", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} ds.EXPECT().NewListDomainsParams().Return(dsp) @@ -236,11 +236,11 @@ var _ = Describe("User Credentials", func() { as.EXPECT().ListAccounts(asp).Return(nil, fakeError) err := client.ResolveUser(&dummies.User) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("resolving account")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("resolving account")) }) - It("search for user in CloudStack fails", func() { + ginkgo.It("search for user in CloudStack fails", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} usp := &csapi.ListUsersParams{} @@ -257,10 +257,10 @@ var _ = Describe("User Credentials", func() { us.EXPECT().NewListUsersParams().Return(usp) us.EXPECT().ListUsers(usp).Return(nil, fakeError) - Ω(client.ResolveUser(&dummies.User)).ShouldNot(Succeed()) + gomega.Ω(client.ResolveUser(&dummies.User)).ShouldNot(gomega.Succeed()) }) - It("search for user in CloudStack results in more than one user", func() { + ginkgo.It("search for user in CloudStack results in more than one user", func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} usp := &csapi.ListUsersParams{} @@ -281,12 +281,12 @@ var _ = Describe("User Credentials", func() { }, nil) err := client.ResolveUser(&dummies.User) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("expected 1 User with username")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("expected 1 User with username")) }) }) - Context("Get user keys in CloudStack", func() { + ginkgo.Context("Get user keys in CloudStack", func() { initialCalls := func() { dsp := &csapi.ListDomainsParams{} asp := &csapi.ListAccountsParams{} @@ -303,7 +303,7 @@ var _ = Describe("User Credentials", func() { }}}, nil) } - It("get user keys", func() { + ginkgo.It("get user keys", func() { initialCalls() usp := &csapi.ListUsersParams{} ukp := &csapi.GetUserKeysParams{} @@ -321,10 +321,10 @@ var _ = Describe("User Credentials", func() { Secretkey: dummies.SecretKey, }, nil) - Ω(client.ResolveUserKeys(&dummies.User)).Should(Succeed()) + gomega.Ω(client.ResolveUserKeys(&dummies.User)).Should(gomega.Succeed()) }) - It("get user keys fils when resolving user", func() { + ginkgo.It("get user keys fils when resolving user", func() { initialCalls() usp := &csapi.ListUsersParams{} @@ -332,12 +332,12 @@ var _ = Describe("User Credentials", func() { us.EXPECT().ListUsers(usp).Return(nil, fakeError) err := client.ResolveUserKeys(&dummies.User) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("error encountered when resolving user details")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("error encountered when resolving user details")) }) - It("get user keys fils when resolving user keys", func() { + ginkgo.It("get user keys fils when resolving user keys", func() { initialCalls() usp := &csapi.ListUsersParams{} ukp := &csapi.GetUserKeysParams{} @@ -354,13 +354,13 @@ var _ = Describe("User Credentials", func() { us.EXPECT().GetUserKeys(ukp).Return(nil, fakeError) err := client.ResolveUserKeys(&dummies.User) - Ω(err).ShouldNot(Succeed()) - Ω(err.Error()).Should(ContainSubstring("error encountered when resolving user api keys")) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("error encountered when resolving user api keys")) }) }) - Context("Get user with keys", func() { - BeforeEach(func() { + ginkgo.Context("Get user with keys", func() { + ginkgo.BeforeEach(func() { dummies.SetDummyUserVars() }) @@ -372,7 +372,7 @@ var _ = Describe("User Credentials", func() { Path: "ROOT", }}}, nil) } - It("get first user for given account and domain", func() { + ginkgo.It("get first user for given account and domain", func() { initialCalls() initialCalls() asp := &csapi.ListAccountsParams{} @@ -399,22 +399,22 @@ var _ = Describe("User Credentials", func() { }, nil) result, err := client.GetUserWithKeys(&dummies.User) - Ω(err).Should(Succeed()) - Ω(result).Should(BeTrue()) + gomega.Ω(err).Should(gomega.Succeed()) + gomega.Ω(result).Should(gomega.BeTrue()) }) - It("fails to resolve accout", func() { + ginkgo.It("fails to resolve accout", func() { initialCalls() asp := &csapi.ListAccountsParams{} as.EXPECT().NewListAccountsParams().Return(asp) as.EXPECT().ListAccounts(asp).Return(nil, fakeError) result, err := client.GetUserWithKeys(&dummies.User) - Ω(err.Error()).Should(ContainSubstring(fmt.Sprintf("resolving account %s details", dummies.User.Account.Name))) - Ω(result).Should(BeFalse()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("resolving account %s details", dummies.User.Account.Name))) + gomega.Ω(result).Should(gomega.BeFalse()) }) - It("fails to resolve accout", func() { + ginkgo.It("fails to resolve accout", func() { initialCalls() asp := &csapi.ListAccountsParams{} usp := &csapi.ListUsersParams{} @@ -428,17 +428,17 @@ var _ = Describe("User Credentials", func() { us.EXPECT().ListUsers(usp).Return(nil, fakeError) result, err := client.GetUserWithKeys(&dummies.User) - Ω(err).ShouldNot(Succeed()) - Ω(result).Should(BeFalse()) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(result).Should(gomega.BeFalse()) }) }) - Context("UserCred Integ Tests", Label("integ"), func() { + ginkgo.Context("UserCred Integ Tests", ginkgo.Label("integ"), func() { var domain cloud.Domain var account cloud.Account var user cloud.User - BeforeEach(func() { + ginkgo.BeforeEach(func() { client = realCloudClient domain = cloud.Domain{Path: testDomainPath} @@ -446,43 +446,43 @@ var _ = Describe("User Credentials", func() { user = cloud.User{Name: helpers.TempUserName, Account: account} }) - It("can resolve a domain from the path", func() { - Ω(client.ResolveDomain(&domain)).Should(Succeed()) - Ω(domain.ID).ShouldNot(BeEmpty()) + ginkgo.It("can resolve a domain from the path", func() { + gomega.Ω(client.ResolveDomain(&domain)).Should(gomega.Succeed()) + gomega.Ω(domain.ID).ShouldNot(gomega.BeEmpty()) }) - It("can resolve an account from the domain path and account name", func() { - Ω(client.ResolveAccount(&account)).Should(Succeed()) - Ω(account.ID).ShouldNot(BeEmpty()) + ginkgo.It("can resolve an account from the domain path and account name", func() { + gomega.Ω(client.ResolveAccount(&account)).Should(gomega.Succeed()) + gomega.Ω(account.ID).ShouldNot(gomega.BeEmpty()) }) - It("can resolve a user from the domain path, account name, and user name", func() { - Ω(client.ResolveUser(&user)).Should(Succeed()) - Ω(user.ID).ShouldNot(BeEmpty()) + ginkgo.It("can resolve a user from the domain path, account name, and user name", func() { + gomega.Ω(client.ResolveUser(&user)).Should(gomega.Succeed()) + gomega.Ω(user.ID).ShouldNot(gomega.BeEmpty()) }) - It("can get sub-domain user's credentials", func() { - Ω(client.ResolveUserKeys(&user)).Should(Succeed()) + ginkgo.It("can get sub-domain user's credentials", func() { + gomega.Ω(client.ResolveUserKeys(&user)).Should(gomega.Succeed()) - Ω(user.APIKey).ShouldNot(BeEmpty()) - Ω(user.SecretKey).ShouldNot(BeEmpty()) + gomega.Ω(user.APIKey).ShouldNot(gomega.BeEmpty()) + gomega.Ω(user.SecretKey).ShouldNot(gomega.BeEmpty()) }) - It("can get an arbitrary user with keys from domain and account specifications alone", func() { + ginkgo.It("can get an arbitrary user with keys from domain and account specifications alone", func() { found, err := client.GetUserWithKeys(&user) - Ω(err).ShouldNot(HaveOccurred()) - Ω(found).Should(BeTrue()) - Ω(user.APIKey).ShouldNot(BeEmpty()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(found).Should(gomega.BeTrue()) + gomega.Ω(user.APIKey).ShouldNot(gomega.BeEmpty()) }) - It("can get create a new client as another user", func() { + ginkgo.It("can get create a new client as another user", func() { found, err := client.GetUserWithKeys(&user) - Ω(err).ShouldNot(HaveOccurred()) - Ω(found).Should(BeTrue()) - Ω(user.APIKey).ShouldNot(BeEmpty()) - newClient, err := client.NewClientInDomainAndAccount(user.Account.Domain.Name, user.Account.Name) - Ω(err).ShouldNot(HaveOccurred()) - Ω(newClient).ShouldNot(BeNil()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(found).Should(gomega.BeTrue()) + gomega.Ω(user.APIKey).ShouldNot(gomega.BeEmpty()) + newClient, err := client.NewClientInDomainAndAccount(user.Account.Domain.Name, user.Account.Name, "") + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(newClient).ShouldNot(gomega.BeNil()) }) }) }) diff --git a/pkg/cloud/vpc.go b/pkg/cloud/vpc.go new file mode 100644 index 00000000..b9946c7a --- /dev/null +++ b/pkg/cloud/vpc.go @@ -0,0 +1,167 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloud + +import ( + "strings" + + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/pkg/errors" +) + +// ResourceTypeVPC is the type identifier for VPC resources. +const ( + ResourceTypeVPC = "Vpc" + VPCOffering = "Default VPC offering" +) + +// VPCIface defines the interface for VPC operations. +type VPCIface interface { + ResolveVPC(*infrav1.VPC) error + CreateVPC(*infrav1.CloudStackFailureDomain, *infrav1.VPC) error + RemoveClusterTagFromVPC(*infrav1.CloudStackCluster, infrav1.VPC) error + DeleteVPCIfNotInUse(infrav1.VPC) (retError error) +} + +// getVPCOfferingID fetches a vpc offering id. +func (c *client) getVPCOfferingID(offeringName string) (string, error) { + offeringID, count, retErr := c.cs.VPC.GetVPCOfferingID(offeringName) + if retErr != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(retErr) + return "", retErr + } else if count != 1 { + return "", errors.New("found more than one vpc offering") + } + return offeringID, nil +} + +// ResolveVPC checks if the specified VPC exists by ID or name. +// If it exists, it updates the VPC struct with the resolved ID or name. +func (c *client) ResolveVPC(vpc *infrav1.VPC) error { + if vpc == nil || (vpc.ID == "" && vpc.Name == "") { + return nil + } + + // If VPC ID is provided, check if it exists + if vpc.ID != "" { + resp, count, err := c.cs.VPC.GetVPCByID(vpc.ID, cloudstack.WithProject(c.user.Project.ID)) + if err != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) + return errors.Wrapf(err, "failed to get VPC with ID %s", vpc.ID) + } + if count == 0 { + return errors.Errorf("no VPC found with ID %s", vpc.ID) + } + vpc.Name = resp.Name + vpc.CIDR = resp.Cidr + vpc.Offering = resp.Vpcofferingname + return nil + } + + // If VPC name is provided, check if it exists + resp, count, err := c.cs.VPC.GetVPCByName(vpc.Name, cloudstack.WithProject(c.user.Project.ID)) + if err != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) + return errors.Wrapf(err, "failed to get VPC with name %s", vpc.Name) + } + if count == 0 { + return errors.Errorf("no VPC found with name %s", vpc.Name) + } + vpc.ID = resp.Id + vpc.CIDR = resp.Cidr + vpc.Offering = resp.Vpcofferingname + return nil +} + +// CreateVPC creates a new VPC in CloudStack. +func (c *client) CreateVPC(fd *infrav1.CloudStackFailureDomain, vpc *infrav1.VPC) error { + if vpc == nil || vpc.Name == "" { + return errors.New("VPC name must be specified") + } + + offeringName := VPCOffering + if vpc.Offering != "" { + offeringName = vpc.Offering + } + + offeringID, err := c.getVPCOfferingID(offeringName) + if err != nil { + return err + } + + p := c.cs.VPC.NewCreateVPCParams(vpc.CIDR, vpc.Name, vpc.Name, offeringID, fd.Spec.Zone.ID) + setIfNotEmpty(c.user.Project.ID, p.SetProjectid) + p.SetStart(true) + resp, err := c.cs.VPC.CreateVPC(p) + if err != nil { + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) + return errors.Wrapf(err, "creating VPC with name %s", vpc.Name) + } + vpc.ID = resp.Id + return c.AddCreatedByCAPCTag(ResourceTypeVPC, vpc.ID) +} + +// DeleteVPC deletes a VPC. +func (c *client) DeleteVPC(vpc infrav1.VPC) error { + _, err := c.cs.VPC.DeleteVPC(c.cs.VPC.NewDeleteVPCParams(vpc.ID)) + c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) + return errors.Wrapf(err, "deleting vpc with id %s", vpc.ID) +} + +// DeleteVPCIfNotInUse deletes a VPC if the VPC is no longer in use (indicated by in use tags). +func (c *client) DeleteVPCIfNotInUse(vpc infrav1.VPC) (retError error) { + tags, err := c.GetTags(ResourceTypeVPC, vpc.ID) + if err != nil { + return err + } + + var clusterTagCount int + for tagName := range tags { + if strings.HasPrefix(tagName, ClusterTagNamePrefix) { + clusterTagCount++ + } + } + + if clusterTagCount == 0 && tags[CreatedByCAPCTagName] != "" { + return c.DeleteVPC(vpc) + } + + return nil +} + +func generateVPCTagName(csCluster *infrav1.CloudStackCluster) string { + return ClusterTagNamePrefix + string(csCluster.UID) +} + +// RemoveClusterTagFromVPC removes the cluster in use tag from a VPC. +func (c *client) RemoveClusterTagFromVPC(csCluster *infrav1.CloudStackCluster, vpc infrav1.VPC) (retError error) { + tags, err := c.GetTags(ResourceTypeVPC, vpc.ID) + if err != nil { + return err + } + + ClusterTagName := generateVPCTagName(csCluster) + if tagValue := tags[ClusterTagName]; tagValue != "" { + if err = c.DeleteTags(ResourceTypeVPC, vpc.ID, map[string]string{ClusterTagName: tagValue}); err != nil { + return err + } + } + + return nil +} diff --git a/pkg/cloud/vpc_test.go b/pkg/cloud/vpc_test.go new file mode 100644 index 00000000..e378b90d --- /dev/null +++ b/pkg/cloud/vpc_test.go @@ -0,0 +1,226 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cloud_test + +import ( + "errors" + "fmt" + + csapi "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" + gomock "go.uber.org/mock/gomock" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" +) + +var _ = ginkgo.Describe("VPC", func() { + var ( + mockCtrl *gomock.Controller + mockClient *csapi.CloudStackClient + vs *csapi.MockVPCServiceIface + rs *csapi.MockResourcetagsServiceIface + client cloud.Client + ) + + ginkgo.BeforeEach(func() { + // Setup new mock services. + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) + mockClient = csapi.NewMockClient(mockCtrl) + vs = mockClient.VPC.(*csapi.MockVPCServiceIface) + rs = mockClient.Resourcetags.(*csapi.MockResourcetagsServiceIface) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) + dummies.SetDummyVars() + }) + + ginkgo.AfterEach(func() { + mockCtrl.Finish() + }) + + ginkgo.Context("for an existing VPC", func() { + var dummyVPC infrav1.VPC + + ginkgo.BeforeEach(func() { + dummyVPC = infrav1.VPC{ + ID: "vpc-123", + Name: "test-vpc", + CIDR: "10.0.0.0/16", + } + }) + + ginkgo.It("resolves VPC by ID", func() { + dummyCSVPC := &csapi.VPC{ + Id: dummyVPC.ID, + Name: dummyVPC.Name, + Cidr: dummyVPC.CIDR, + } + + vs.EXPECT().GetVPCByID(dummyVPC.ID, gomock.Any()).Return(dummyCSVPC, 1, nil) + + gomega.Ω(client.ResolveVPC(&dummyVPC)).Should(gomega.Succeed()) + gomega.Ω(dummyVPC.Name).Should(gomega.Equal("test-vpc")) + }) + + ginkgo.It("resolves VPC by Name", func() { + dummyVPC.ID = "" // Clear ID to test by name + + dummyCSVPC := &csapi.VPC{ + Id: "vpc-123", + Name: dummyVPC.Name, + Cidr: dummyVPC.CIDR, + } + + vs.EXPECT().GetVPCByName(dummyVPC.Name, gomock.Any()).Return(dummyCSVPC, 1, nil) + + gomega.Ω(client.ResolveVPC(&dummyVPC)).Should(gomega.Succeed()) + gomega.Ω(dummyVPC.ID).Should(gomega.Equal("vpc-123")) + }) + + ginkgo.It("returns error when VPC not found by ID", func() { + vs.EXPECT().GetVPCByID(dummyVPC.ID, gomock.Any()).Return(nil, 0, nil) + + err := client.ResolveVPC(&dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("no VPC found with ID %s", dummyVPC.ID))) + }) + + ginkgo.It("returns error when VPC not found by Name", func() { + dummyVPC.ID = "" // Clear ID to test by name + + vs.EXPECT().GetVPCByName(dummyVPC.Name, gomock.Any()).Return(nil, 0, nil) + + err := client.ResolveVPC(&dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("no VPC found with name %s", dummyVPC.Name))) + }) + + ginkgo.It("returns error when GetVPCByID fails", func() { + expectedErr := errors.New("API error") + vs.EXPECT().GetVPCByID(dummyVPC.ID, gomock.Any()).Return(nil, 0, expectedErr) + + err := client.ResolveVPC(&dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("failed to get VPC with ID %s", dummyVPC.ID))) + }) + + ginkgo.It("returns error when GetVPCByName fails", func() { + dummyVPC.ID = "" // Clear ID to test by name + expectedErr := errors.New("API error") + vs.EXPECT().GetVPCByName(dummyVPC.Name, gomock.Any()).Return(nil, 0, expectedErr) + + err := client.ResolveVPC(&dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("failed to get VPC with name %s", dummyVPC.Name))) + }) + + ginkgo.It("handles nil VPC", func() { + gomega.Ω(client.ResolveVPC(nil)).Should(gomega.Succeed()) + }) + + ginkgo.It("handles empty VPC", func() { + emptyVPC := &infrav1.VPC{} + gomega.Ω(client.ResolveVPC(emptyVPC)).Should(gomega.Succeed()) + }) + }) + + ginkgo.Context("for creating a VPC", func() { + var ( + dummyFD infrav1.CloudStackFailureDomain + dummyVPC infrav1.VPC + ) + + ginkgo.BeforeEach(func() { + dummyFD = infrav1.CloudStackFailureDomain{ + Spec: infrav1.CloudStackFailureDomainSpec{ + Zone: infrav1.CloudStackZoneSpec{ + ID: "zone-123", + }, + }, + } + dummyVPC = infrav1.VPC{ + Name: "test-vpc", + CIDR: "10.0.0.0/16", + } + }) + + ginkgo.It("creates a new VPC successfully", func() { + offeringID := "offering-123" + createVPCParams := &csapi.CreateVPCParams{} + createVPCResponse := &csapi.CreateVPCResponse{ + Id: "vpc-123", + } + + vs.EXPECT().GetVPCOfferingID(cloud.VPCOffering).Return(offeringID, 1, nil) + vs.EXPECT().NewCreateVPCParams(dummyVPC.CIDR, dummyVPC.Name, dummyVPC.Name, offeringID, dummyFD.Spec.Zone.ID).Return(createVPCParams) + vs.EXPECT().CreateVPC(createVPCParams).Return(createVPCResponse, nil) + rs.EXPECT().NewCreateTagsParams(gomock.Any(), gomock.Any(), gomock.Any()).Return(&csapi.CreateTagsParams{}) + rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}) + rs.EXPECT().ListTags(gomock.Any()).Return(&csapi.ListTagsResponse{}, nil) + rs.EXPECT().CreateTags(gomock.Any()).Return(&csapi.CreateTagsResponse{}, nil) + + gomega.Ω(client.CreateVPC(&dummyFD, &dummyVPC)).Should(gomega.Succeed()) + gomega.Ω(dummyVPC.ID).Should(gomega.Equal("vpc-123")) + }) + + ginkgo.It("returns error when VPC offering cannot be fetched", func() { + expectedErr := errors.New("failed to get VPC offering") + vs.EXPECT().GetVPCOfferingID(cloud.VPCOffering).Return("", 0, expectedErr) + + err := client.CreateVPC(&dummyFD, &dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal(expectedErr.Error())) + }) + + ginkgo.It("returns error when multiple VPC offerings found", func() { + vs.EXPECT().GetVPCOfferingID(cloud.VPCOffering).Return("", 2, nil) + + err := client.CreateVPC(&dummyFD, &dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal("found more than one vpc offering")) + }) + + ginkgo.It("returns error when CreateVPC fails", func() { + offeringID := "offering-123" + createVPCParams := &csapi.CreateVPCParams{} + expectedErr := errors.New("API error") + + vs.EXPECT().GetVPCOfferingID(cloud.VPCOffering).Return(offeringID, 1, nil) + vs.EXPECT().NewCreateVPCParams(dummyVPC.CIDR, dummyVPC.Name, dummyVPC.Name, offeringID, dummyFD.Spec.Zone.ID).Return(createVPCParams) + vs.EXPECT().CreateVPC(createVPCParams).Return(nil, expectedErr) + + err := client.CreateVPC(&dummyFD, &dummyVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring(fmt.Sprintf("creating VPC with name %s", dummyVPC.Name))) + }) + + ginkgo.It("returns error when VPC is nil", func() { + err := client.CreateVPC(&dummyFD, nil) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal("VPC name must be specified")) + }) + + ginkgo.It("returns error when VPC name is empty", func() { + emptyNameVPC := &infrav1.VPC{ + CIDR: "10.0.0.0/16", + } + err := client.CreateVPC(&dummyFD, emptyNameVPC) + gomega.Ω(err).ShouldNot(gomega.Succeed()) + gomega.Ω(err.Error()).Should(gomega.Equal("VPC name must be specified")) + }) + }) +}) diff --git a/pkg/cloud/zone.go b/pkg/cloud/zone.go index d3bde3e2..7a9c9a88 100644 --- a/pkg/cloud/zone.go +++ b/pkg/cloud/zone.go @@ -17,9 +17,10 @@ limitations under the License. package cloud import ( + "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" - infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" ) type ZoneIFace interface { @@ -29,7 +30,6 @@ type ZoneIFace interface { func (c *client) ResolveZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { if zoneID, count, err := c.cs.Zone.GetZoneID(zSpec.Name); err != nil { - retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Zone ID from %v", zSpec.Name)) c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Zone ID from %v", zSpec.Name)) } else if count != 1 { @@ -39,15 +39,15 @@ func (c *client) ResolveZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { zSpec.ID = zoneID } - if resp, count, err := c.cs.Zone.GetZoneByID(zSpec.ID); err != nil { + resp, count, err := c.cs.Zone.GetZoneByID(zSpec.ID) + if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) return multierror.Append(retErr, errors.Wrapf(err, "could not get Zone by ID %v", zSpec.ID)) } else if count != 1 { return multierror.Append(retErr, errors.Errorf( "expected 1 Zone with UUID %s, but got %d", zSpec.ID, count)) - } else { - zSpec.Name = resp.Name } + zSpec.Name = resp.Name return nil } @@ -55,7 +55,7 @@ func (c *client) ResolveZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { // ResolveNetworkForZone fetches details on Zone's specified network. func (c *client) ResolveNetworkForZone(zSpec *infrav1.CloudStackZoneSpec) (retErr error) { netName := zSpec.Network.Name - netDetails, count, err := c.cs.Network.GetNetworkByName(netName) + netDetails, count, err := c.cs.Network.GetNetworkByName(netName, cloudstack.WithProject(c.user.Project.ID)) if err != nil { c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err) retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %v", netName)) @@ -69,7 +69,7 @@ func (c *client) ResolveNetworkForZone(zSpec *infrav1.CloudStackZoneSpec) (retEr } // Now get network details. - netDetails, count, err = c.cs.Network.GetNetworkByID(zSpec.Network.ID) + netDetails, count, err = c.cs.Network.GetNetworkByID(zSpec.Network.ID, cloudstack.WithProject(c.user.Project.ID)) if err != nil { return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", zSpec.Network.ID)) } else if count != 1 { diff --git a/pkg/cloud/zone_test.go b/pkg/cloud/zone_test.go index 0c141d73..51bdfcfc 100644 --- a/pkg/cloud/zone_test.go +++ b/pkg/cloud/zone_test.go @@ -20,15 +20,15 @@ import ( "fmt" csapi "github.com/apache/cloudstack-go/v2/cloudstack" - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "github.com/pkg/errors" + gomock "go.uber.org/mock/gomock" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" - dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta2" + dummies "sigs.k8s.io/cluster-api-provider-cloudstack/test/dummies/v1beta3" ) -var _ = Describe("Zone", func() { +var _ = ginkgo.Describe("Zone", func() { const ( errorMessage = "Error" ) @@ -42,74 +42,74 @@ var _ = Describe("Zone", func() { ns *csapi.MockNetworkServiceIface ) - BeforeEach(func() { - mockCtrl = gomock.NewController(GinkgoT()) + ginkgo.BeforeEach(func() { + mockCtrl = gomock.NewController(ginkgo.GinkgoT()) mockClient = csapi.NewMockClient(mockCtrl) zs = mockClient.Zone.(*csapi.MockZoneServiceIface) ns = mockClient.Network.(*csapi.MockNetworkServiceIface) - client = cloud.NewClientFromCSAPIClient(mockClient) + client = cloud.NewClientFromCSAPIClient(mockClient, nil) dummies.SetDummyVars() }) - AfterEach(func() { + ginkgo.AfterEach(func() { mockCtrl.Finish() }) - Context("an existing abstract dummies.CSCluster", func() { - It("handles zone not found.", func() { + ginkgo.Context("an existing abstract dummies.CSCluster", func() { + ginkgo.It("handles zone not found.", func() { expectedErr := fmt.Errorf("Not found") zs.EXPECT().GetZoneID(dummies.Zone1.Name).Return("", -1, expectedErr) zs.EXPECT().GetZoneByID(dummies.Zone1.ID).Return(nil, -1, expectedErr) err := client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone) - Expect(errors.Cause(err)).To(MatchError(expectedErr)) + gomega.Expect(errors.Cause(err)).To(gomega.MatchError(expectedErr)) }) - It("handles multiple zone IDs returned", func() { + ginkgo.It("handles multiple zone IDs returned", func() { zs.EXPECT().GetZoneID(dummies.Zone1.Name).Return(dummies.Zone1.ID, 2, nil) zs.EXPECT().GetZoneByID(dummies.Zone1.ID).Return(nil, -1, fmt.Errorf("Not found")) - Ω(client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(MatchError(And( - ContainSubstring("expected 1 Zone with name "+dummies.Zone1.Name+", but got 2"), - ContainSubstring("could not get Zone by ID "+dummies.Zone1.ID+": Not found")))) + gomega.Ω(client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(gomega.MatchError(gomega.And( + gomega.ContainSubstring("expected 1 Zone with name "+dummies.Zone1.Name+", but got 2"), + gomega.ContainSubstring("could not get Zone by ID "+dummies.Zone1.ID+": Not found")))) }) - It("returns multiple zones for a given id", func() { + ginkgo.It("returns multiple zones for a given id", func() { zs.EXPECT().GetZoneID(dummies.Zone1.Name).Return(dummies.Zone1.ID, 2, nil) zs.EXPECT().GetZoneByID(dummies.Zone1.ID).Return(&csapi.Zone{}, 2, nil) - Ω(client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone).Error()). - Should(ContainSubstring("expected 1 Zone with name " + dummies.Zone1.Name + ", but got 2")) + gomega.Ω(client.ResolveZone(&dummies.CSFailureDomain1.Spec.Zone).Error()). + Should(gomega.ContainSubstring("expected 1 Zone with name " + dummies.Zone1.Name + ", but got 2")) }) }) - Context("Resolve network for zone", func() { - It("get network by name specfied in zone spec", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone1.Network.Name).Return(&csapi.Network{}, 1, nil) + ginkgo.Context("Resolve network for zone", func() { + ginkgo.It("get network by name specfied in zone spec", func() { + ns.EXPECT().GetNetworkByName(dummies.Zone1.Network.Name, gomock.Any()).Return(&csapi.Network{}, 1, nil) - Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(Succeed()) + gomega.Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain1.Spec.Zone)).Should(gomega.Succeed()) }) - It("get network by name specfied in zone spec returns > 1 network", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name).Return(&csapi.Network{}, 2, nil) - ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID).Return(&csapi.Network{}, 2, nil) + ginkgo.It("get network by name specfied in zone spec returns > 1 network", func() { + ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name, gomock.Any()).Return(&csapi.Network{}, 2, nil) + ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID, gomock.Any()).Return(&csapi.Network{}, 2, nil) - Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone)).Should(MatchError(And( - ContainSubstring(fmt.Sprintf("expected 1 Network with name %s, but got %d", dummies.Zone2.Network.Name, 2)), - ContainSubstring(fmt.Sprintf("expected 1 Network with UUID %v, but got %d", dummies.Zone2.Network.ID, 2))))) + gomega.Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone)).Should(gomega.MatchError(gomega.And( + gomega.ContainSubstring(fmt.Sprintf("expected 1 Network with name %s, but got %d", dummies.Zone2.Network.Name, 2)), + gomega.ContainSubstring(fmt.Sprintf("expected 1 Network with UUID %v, but got %d", dummies.Zone2.Network.ID, 2))))) }) - It("get network by id specfied in zone spec", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name).Return(nil, -1, fakeError) - ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID).Return(&csapi.Network{}, 1, nil) - Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone)).Should(Succeed()) + ginkgo.It("get network by id specfied in zone spec", func() { + ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name, gomock.Any()).Return(nil, -1, fakeError) + ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID, gomock.Any()).Return(&csapi.Network{}, 1, nil) + gomega.Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone)).Should(gomega.Succeed()) }) - It("get network by id fails", func() { - ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name).Return(nil, -1, fakeError) - ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID).Return(nil, -1, fakeError) + ginkgo.It("get network by id fails", func() { + ns.EXPECT().GetNetworkByName(dummies.Zone2.Network.Name, gomock.Any()).Return(nil, -1, fakeError) + ns.EXPECT().GetNetworkByID(dummies.Zone2.Network.ID, gomock.Any()).Return(nil, -1, fakeError) - Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone).Error()).Should(ContainSubstring(fmt.Sprintf("could not get Network by ID %s", dummies.Zone2.Network.ID))) + gomega.Ω(client.ResolveNetworkForZone(&dummies.CSFailureDomain2.Spec.Zone).Error()).Should(gomega.ContainSubstring(fmt.Sprintf("could not get Network by ID %s", dummies.Zone2.Network.ID))) }) }) }) diff --git a/pkg/webhookutil/webhook_validators.go b/pkg/webhookutil/webhook_validators.go index bf442ad9..79b72df6 100644 --- a/pkg/webhookutil/webhook_validators.go +++ b/pkg/webhookutil/webhook_validators.go @@ -38,28 +38,28 @@ func EnsureAtLeastOneFieldExists(value1 string, value2 string, name string, allE return allErrs } -func EnsureEqualStrings(new string, old string, name string, allErrs field.ErrorList) field.ErrorList { - if new != old { +func EnsureEqualStrings(newString string, oldString string, name string, allErrs field.ErrorList) field.ErrorList { + if newString != oldString { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", name), name)) } return allErrs } -func EnsureIntFieldsAreNotNegative(new int64, name string, allErrs field.ErrorList) field.ErrorList { - if new < 0 { +func EnsureIntFieldsAreNotNegative(newInt int64, name string, allErrs field.ErrorList) field.ErrorList { + if newInt < 0 { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", name), name)) } return allErrs } -func EnsureEqualMapStringString(new *map[string]string, old *map[string]string, name string, allErrs field.ErrorList) field.ErrorList { - if old == nil && new == nil { +func EnsureEqualMapStringString(newMap *map[string]string, oldMap *map[string]string, name string, allErrs field.ErrorList) field.ErrorList { + if oldMap == nil && newMap == nil { return allErrs } - if new == nil || old == nil { + if newMap == nil || oldMap == nil { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", name), name)) } - if !reflect.DeepEqual(old, new) { + if !reflect.DeepEqual(oldMap, newMap) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", name), name)) } return allErrs diff --git a/templates/cluster-template-managed-ssh.yaml b/templates/cluster-template-managed-ssh.yaml index 992bc67e..a036adb8 100644 --- a/templates/cluster-template-managed-ssh.yaml +++ b/templates/cluster-template-managed-ssh.yaml @@ -10,7 +10,7 @@ spec: - 192.168.0.0/16 serviceDomain: "cluster.local" infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster name: ${CLUSTER_NAME} controlPlaneRef: @@ -18,11 +18,12 @@ spec: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 name: ${CLUSTER_NAME}-control-plane --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster metadata: name: ${CLUSTER_NAME} spec: + syncWithACS: ${CLOUDSTACK_SYNC_WITH_ACS=false} controlPlaneEndpoint: host: ${CLUSTER_ENDPOINT_IP} port: ${CLUSTER_ENDPOINT_PORT=6443} @@ -56,13 +57,13 @@ spec: - swapoff -a machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate name: "${CLUSTER_NAME}-control-plane" replicas: ${CONTROL_PLANE_MACHINE_COUNT} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -70,7 +71,7 @@ spec: template: spec: sshKey: ${CLOUDSTACK_SSH_KEY_NAME} - offering: + offering: name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} template: name: ${CLOUDSTACK_TEMPLATE_NAME} @@ -95,10 +96,10 @@ spec: kind: KubeadmConfigTemplate infrastructureRef: name: "${CLUSTER_NAME}-md-0" - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 @@ -106,7 +107,7 @@ spec: template: spec: sshKey: ${CLOUDSTACK_SSH_KEY_NAME} - offering: + offering: name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} template: name: ${CLOUDSTACK_TEMPLATE_NAME} diff --git a/templates/cluster-template-ssh-material.yaml b/templates/cluster-template-ssh-material.yaml index 09dd5b42..3c85db2b 100644 --- a/templates/cluster-template-ssh-material.yaml +++ b/templates/cluster-template-ssh-material.yaml @@ -10,7 +10,7 @@ spec: - 192.168.0.0/16 serviceDomain: "cluster.local" infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster name: ${CLUSTER_NAME} controlPlaneRef: @@ -18,11 +18,12 @@ spec: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 name: ${CLUSTER_NAME}-control-plane --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster metadata: name: ${CLUSTER_NAME} spec: + syncWithACS: ${CLOUDSTACK_SYNC_WITH_ACS=false} controlPlaneEndpoint: host: ${CLUSTER_ENDPOINT_IP} port: ${CLUSTER_ENDPOINT_PORT=6443} @@ -61,13 +62,13 @@ spec: - swapoff -a machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate name: "${CLUSTER_NAME}-control-plane" replicas: ${CONTROL_PLANE_MACHINE_COUNT} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -99,10 +100,10 @@ spec: kind: KubeadmConfigTemplate infrastructureRef: name: "${CLUSTER_NAME}-md-0" - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 diff --git a/templates/cluster-template-with-disk-offering.yaml b/templates/cluster-template-with-disk-offering.yaml index 31961fcb..486c96ef 100644 --- a/templates/cluster-template-with-disk-offering.yaml +++ b/templates/cluster-template-with-disk-offering.yaml @@ -10,7 +10,7 @@ spec: - 192.168.0.0/16 serviceDomain: "cluster.local" infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster name: ${CLUSTER_NAME} controlPlaneRef: @@ -18,11 +18,12 @@ spec: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 name: ${CLUSTER_NAME}-control-plane --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster metadata: name: ${CLUSTER_NAME} spec: + syncWithACS: ${CLOUDSTACK_SYNC_WITH_ACS=false} controlPlaneEndpoint: host: ${CLUSTER_ENDPOINT_IP} port: ${CLUSTER_ENDPOINT_PORT=6443} @@ -75,13 +76,13 @@ spec: - ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate name: "${CLUSTER_NAME}-control-plane" replicas: ${CONTROL_PLANE_MACHINE_COUNT} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -119,10 +120,10 @@ spec: kind: KubeadmConfigTemplate infrastructureRef: name: "${CLUSTER_NAME}-md-0" - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 diff --git a/templates/cluster-template-with-existing-vpc-network.yaml b/templates/cluster-template-with-existing-vpc-network.yaml new file mode 100644 index 00000000..71ae717f --- /dev/null +++ b/templates/cluster-template-with-existing-vpc-network.yaml @@ -0,0 +1,128 @@ +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - 192.168.0.0/16 + serviceDomain: "cluster.local" + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackCluster + name: ${CLUSTER_NAME} + controlPlaneRef: + kind: KubeadmControlPlane + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + name: ${CLUSTER_NAME}-control-plane +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + syncWithACS: ${CLOUDSTACK_SYNC_WITH_ACS=false} + controlPlaneEndpoint: + host: ${CLUSTER_ENDPOINT_IP} + port: ${CLUSTER_ENDPOINT_PORT=6443} + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME=failure-domain-1} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME=cloudstack-credentials} + namespace: ${CLOUDSTACK_FD1_SECRET_NAMESPACE=default} + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + vpc: + name: ${CLOUDSTACK_VPC_NAME} +--- +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: "${CLUSTER_NAME}-control-plane" +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: "${CLUSTER_NAME}-control-plane" + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: "${CLUSTER_NAME}-md-0" +spec: + clusterName: "${CLUSTER_NAME}" + replicas: ${WORKER_MACHINE_COUNT} + selector: + matchLabels: null + template: + spec: + clusterName: "${CLUSTER_NAME}" + version: "${KUBERNETES_VERSION}" + bootstrap: + configRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + infrastructureRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a diff --git a/templates/cluster-template-with-kube-vip.yaml b/templates/cluster-template-with-kube-vip.yaml index 3828c31d..c85e0d23 100644 --- a/templates/cluster-template-with-kube-vip.yaml +++ b/templates/cluster-template-with-kube-vip.yaml @@ -10,7 +10,7 @@ spec: - 192.168.0.0/16 serviceDomain: "cluster.local" infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster name: ${CLUSTER_NAME} controlPlaneRef: @@ -18,11 +18,12 @@ spec: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 name: ${CLUSTER_NAME}-control-plane --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster metadata: name: ${CLUSTER_NAME} spec: + syncWithACS: ${CLOUDSTACK_SYNC_WITH_ACS=false} controlPlaneEndpoint: host: ${CLUSTER_ENDPOINT_IP} port: ${CLUSTER_ENDPOINT_PORT=6443} @@ -67,23 +68,29 @@ spec: spec: containers: - args: - - start + - manager env: - name: vip_arp value: "true" - name: vip_leaderelection value: "true" - - name: vip_address - value: ${CLUSTER_ENDPOINT_IP} - name: vip_interface value: ens3 + - name: address + value: ${CLUSTER_ENDPOINT_IP} + - name: vip_cidr + value: "32" + - name: cp_enable + value: "true" + - name: cp_namespace + value: kube-system - name: vip_leaseduration value: "15" - name: vip_renewdeadline value: "10" - name: vip_retryperiod value: "2" - image: public.ecr.aws/i3w0y7q3/plunder-app/kube-vip:v0.3.7-eks-a-v0.0.0-dev-build.0 + image: ghcr.io/kube-vip/kube-vip:v0.4.0 imagePullPolicy: IfNotPresent name: kube-vip resources: {} @@ -95,10 +102,14 @@ spec: volumeMounts: - mountPath: /etc/kubernetes/admin.conf name: kubeconfig + hostAliases: + - hostnames: + - kubernetes + ip: 127.0.0.1 hostNetwork: true volumes: - hostPath: - path: /etc/kubernetes/admin.conf + path: /etc/kubernetes/super-admin.conf type: FileOrCreate name: kubeconfig status: {} @@ -106,13 +117,13 @@ spec: path: /etc/kubernetes/manifests/kube-vip.yaml machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate name: "${CLUSTER_NAME}-control-plane" replicas: ${CONTROL_PLANE_MACHINE_COUNT} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -144,10 +155,10 @@ spec: kind: KubeadmConfigTemplate infrastructureRef: name: "${CLUSTER_NAME}-md-0" - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 diff --git a/templates/cluster-template.yaml b/templates/cluster-template.yaml index 331ea6b6..c9555ee5 100644 --- a/templates/cluster-template.yaml +++ b/templates/cluster-template.yaml @@ -10,7 +10,7 @@ spec: - 192.168.0.0/16 serviceDomain: "cluster.local" infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster name: ${CLUSTER_NAME} controlPlaneRef: @@ -18,11 +18,12 @@ spec: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 name: ${CLUSTER_NAME}-control-plane --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackCluster metadata: name: ${CLUSTER_NAME} spec: + syncWithACS: ${CLOUDSTACK_SYNC_WITH_ACS=false} controlPlaneEndpoint: host: ${CLUSTER_ENDPOINT_IP} port: ${CLUSTER_ENDPOINT_PORT=6443} @@ -56,13 +57,13 @@ spec: - swapoff -a machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate name: "${CLUSTER_NAME}-control-plane" replicas: ${CONTROL_PLANE_MACHINE_COUNT} version: ${KUBERNETES_VERSION} --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-control-plane @@ -94,10 +95,10 @@ spec: kind: KubeadmConfigTemplate infrastructureRef: name: "${CLUSTER_NAME}-md-0" - apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate --- -apiVersion: infrastructure.cluster.x-k8s.io/v1beta2 +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 kind: CloudStackMachineTemplate metadata: name: ${CLUSTER_NAME}-md-0 diff --git a/test/dummies/v1beta1/vars.go b/test/dummies/v1beta1/vars.go index 06d51160..832a41ba 100644 --- a/test/dummies/v1beta1/vars.go +++ b/test/dummies/v1beta1/vars.go @@ -8,7 +8,7 @@ import ( "github.com/smallfish/simpleyaml" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" capcv1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta1" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" capiv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -199,7 +199,7 @@ func SetDummyCSMachineVars() { Kind: "Secret", Name: "IdentitySecret", }, - InstanceID: pointer.String("Instance1"), + InstanceID: ptr.To("Instance1"), Template: capcv1.CloudStackResourceIdentifier{ Name: GetYamlVal("CLOUDSTACK_TEMPLATE_NAME"), }, @@ -344,7 +344,7 @@ func SetClusterSpecToNet(net *capcv1.Network) { func SetDummyCAPIMachineVars() { CAPIMachine = &capiv1.Machine{ - Spec: capiv1.MachineSpec{FailureDomain: pointer.String(Zone1.ID)}, + Spec: capiv1.MachineSpec{FailureDomain: ptr.To(Zone1.ID)}, } } diff --git a/test/dummies/v1beta2/vars.go b/test/dummies/v1beta2/vars.go index 6671be08..de5aa95e 100644 --- a/test/dummies/v1beta2/vars.go +++ b/test/dummies/v1beta2/vars.go @@ -8,7 +8,7 @@ import ( "github.com/smallfish/simpleyaml" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta2" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" "sigs.k8s.io/cluster-api-provider-cloudstack/test/fakes" @@ -228,7 +228,7 @@ func SetDummyCSMachineVars() { }, Spec: infrav1.CloudStackMachineSpec{ Name: "test-machine-1", - InstanceID: pointer.String("Instance1"), + InstanceID: ptr.To("Instance1"), FailureDomainName: GetYamlVal("CLOUDSTACK_FD1_NAME"), Template: infrav1.CloudStackResourceIdentifier{ Name: GetYamlVal("CLOUDSTACK_TEMPLATE_NAME"), @@ -280,7 +280,7 @@ func SetDummyCAPCClusterVars() { EndPointPort = int32(5309) PublicIPID = "FakePublicIPID" ClusterNameSpace = "default" - ClusterLabel = map[string]string{clusterv1.ClusterLabelName: ClusterName} + ClusterLabel = map[string]string{clusterv1.ClusterNameLabel: ClusterName} AffinityGroup = &cloud.AffinityGroup{ Name: "fakeaffinitygroup", Type: cloud.AffinityGroupType, @@ -423,7 +423,7 @@ func SetDummyCAPIMachineVars() { }, Spec: clusterv1.MachineSpec{ ClusterName: ClusterName, - FailureDomain: pointer.String("fd1")}, + FailureDomain: ptr.To("fd1")}, } } diff --git a/test/dummies/v1beta3/vars.go b/test/dummies/v1beta3/vars.go new file mode 100644 index 00000000..aee8ddea --- /dev/null +++ b/test/dummies/v1beta3/vars.go @@ -0,0 +1,435 @@ +package dummies + +import ( + "os" + + csapi "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/onsi/gomega" + "github.com/smallfish/simpleyaml" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3" + "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" + "sigs.k8s.io/cluster-api-provider-cloudstack/test/fakes" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +// GetYamlVal fetches the values in test/e2e/config/cloudstack.yaml by yaml node. A common config file. +func GetYamlVal(variable string) string { + val, err := CSConf.Get("variables").Get(variable).String() + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + return val +} + +var ( // Declare exported dummy vars. + AffinityGroup *cloud.AffinityGroup + CSAffinityGroup *infrav1.CloudStackAffinityGroup + CSCluster *infrav1.CloudStackCluster + CAPIMachine *clusterv1.Machine + CSMachine1 *infrav1.CloudStackMachine + CAPICluster *clusterv1.Cluster + ClusterLabel map[string]string + ClusterName string + ClusterNameSpace string + CSMachineTemplate1 *infrav1.CloudStackMachineTemplate + ACSEndpointSecret1 *corev1.Secret + ACSEndpointSecret2 *corev1.Secret + Zone1 infrav1.CloudStackZoneSpec + Zone2 infrav1.CloudStackZoneSpec + CSFailureDomain1 *infrav1.CloudStackFailureDomain + CSFailureDomain2 *infrav1.CloudStackFailureDomain + Net1 infrav1.Network + Net2 infrav1.Network + ISONet1 infrav1.Network + CSISONet1 *infrav1.CloudStackIsolatedNetwork + Domain cloud.Domain + DomainPath string + DomainName string + DomainID string + Level2Domain cloud.Domain + Level2DomainPath string + Level2DomainName string + Level2DomainID string + Account cloud.Account + AccountName string + AccountID string + Level2Account cloud.Account + Level2AccountName string + Level2AccountID string + User cloud.User + UserID string + Username string + Apikey string + SecretKey string + Tags map[string]string + Tag1 map[string]string + Tag2 map[string]string + Tag1Key string + Tag1Val string + Tag2Key string + Tag2Val string + CSApiVersion string + CSClusterKind string + TestTags map[string]string + CSClusterTagKey string + CSClusterTagVal string + CSClusterTag map[string]string + CreatedByCapcKey string + CreatedByCapcVal string + LBRuleID string + PublicIPID string + EndPointHost string + SyncWithACS *bool + EndPointPort int32 + CSConf *simpleyaml.Yaml + DiskOffering infrav1.CloudStackResourceDiskOffering + BootstrapSecret *corev1.Secret + BootstrapSecretName string + CSMachineOwner *fakes.CloudStackMachineOwner + CSMachineOwnerReference metav1.OwnerReference +) + +// SetDummyVars sets/resets all dummy vars. +func SetDummyVars() { + projDir := os.Getenv("REPO_ROOT") + source, err := os.ReadFile(projDir + "/test/e2e/config/cloudstack.yaml") + if err != nil { + panic(err) + } + CSConf, err = simpleyaml.NewYaml(source) + if err != nil { + panic(err) + } + + // These need to be in order as they build upon eachother. + SetDummyZoneVars() + SetDiskOfferingVars() + SetACSEndpointSecretVars() + SetDummyCAPCClusterVars() + SetDummyCAPIClusterVars() + SetDummyCAPIMachineVars() + SetDummyCSMachineTemplateVars() + SetDummyCSMachineVars() + SetDummyTagVars() + SetDummyBootstrapSecretVar() + SetCSMachineOwner() + SetDummyOwnerReferences() + LBRuleID = "FakeLBRuleID" +} + +func SetDiskOfferingVars() { + DiskOffering = infrav1.CloudStackResourceDiskOffering{CloudStackResourceIdentifier: infrav1.CloudStackResourceIdentifier{Name: "Small"}, + MountPath: "/data", + Device: "/dev/vdb", + Filesystem: "ext4", + Label: "data_disk", + } +} + +func CAPCNetToCSAPINet(net *infrav1.Network) *csapi.Network { + return &csapi.Network{ + Name: net.Name, + Id: net.ID, + Type: net.Type, + } +} + +// SetDummyVars sets/resets tag related dummy vars. +func SetDummyTagVars() { + CSClusterTagKey = "CAPC_cluster_" + string(CSCluster.ObjectMeta.UID) + CSClusterTagVal = "1" + CSClusterTag = map[string]string{CSClusterTagVal: CSClusterTagVal} + CreatedByCapcKey = "create_by_CAPC" + CreatedByCapcVal = "" + Tag1Key = "test_tag1" + Tag1Val = "arbitrary_value1" + Tag2Key = "test_tag2" + Tag2Val = "arbitrary_value2" + Tag1 = map[string]string{Tag2Key: Tag2Val} + Tag2 = map[string]string{Tag2Key: Tag2Val} + Tags = map[string]string{Tag1Key: Tag1Val, Tag2Key: Tag2Val} +} + +func SetCSMachineOwner() { + CSMachineOwner = &fakes.CloudStackMachineOwner{ + TypeMeta: metav1.TypeMeta{ + APIVersion: fakes.GroupVersion.String(), + Kind: "CloudStackMachineOwner", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ClusterName, + Namespace: ClusterNameSpace, + }, + Spec: fakes.CloudStackMachineOwnerSpec{ + Replicas: nil, + }, + Status: fakes.CloudStackMachineOwnerStatus{ + Replicas: nil, + Ready: nil, + ReadyReplicas: nil, + }, + } +} + +func SetDummyOwnerReferences() { + CSMachineOwnerReference = metav1.OwnerReference{ + Kind: "CloudStackMachineOwner", + APIVersion: fakes.GroupVersion.String(), + Name: ClusterName, + UID: "uniqueness", + } +} + +// SetDummyCSMachineTemplateVars resets the values in each of the exported CloudStackMachinesTemplate dummy variables. +func SetDummyCSMachineTemplateVars() { + CSMachineTemplate1 = &infrav1.CloudStackMachineTemplate{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "infrastructure.cluster.x-k8s.io/v1beta3", + Kind: "CloudStackMachineTemplate", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-machinetemplate-1", + Namespace: "default", + }, + Spec: infrav1.CloudStackMachineTemplateSpec{ + Template: infrav1.CloudStackMachineTemplateResource{ + Spec: infrav1.CloudStackMachineSpec{ + Template: infrav1.CloudStackResourceIdentifier{ + Name: GetYamlVal("CLOUDSTACK_TEMPLATE_NAME"), + }, + Offering: infrav1.CloudStackResourceIdentifier{ + Name: GetYamlVal("CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING"), + }, + DiskOffering: DiskOffering, + Details: map[string]string{ + "memoryOvercommitRatio": "1.2", + }, + }, + }, + }, + } +} + +// SetDummyCSMachineVars resets the values in each of the exported CloudStackMachine dummy variables. +func SetDummyCSMachineVars() { + CSMachine1 = &infrav1.CloudStackMachine{ + TypeMeta: metav1.TypeMeta{ + APIVersion: CSApiVersion, + Kind: "CloudStackMachine", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-machine-1", + Namespace: "default", + Labels: ClusterLabel, + }, + Spec: infrav1.CloudStackMachineSpec{ + Name: "test-machine-1", + InstanceID: ptr.To("Instance1"), + FailureDomainName: GetYamlVal("CLOUDSTACK_FD1_NAME"), + Template: infrav1.CloudStackResourceIdentifier{ + Name: GetYamlVal("CLOUDSTACK_TEMPLATE_NAME"), + }, + Offering: infrav1.CloudStackResourceIdentifier{ + Name: GetYamlVal("CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING"), + }, + DiskOffering: infrav1.CloudStackResourceDiskOffering{ + CloudStackResourceIdentifier: infrav1.CloudStackResourceIdentifier{ + Name: "DiskOffering", + }, + MountPath: "/data", + Device: "/dev/vdb", + Filesystem: "ext4", + Label: "data_disk", + }, + Details: map[string]string{ + "memoryOvercommitRatio": "1.2", + }, + }, + } +} + +func SetDummyZoneVars() { + Zone1 = infrav1.CloudStackZoneSpec{Network: Net1} + Zone1.Name = GetYamlVal("CLOUDSTACK_ZONE_NAME") + Zone2 = infrav1.CloudStackZoneSpec{Network: Net2} + Zone2.Name = "Zone2" + Zone2.ID = "FakeZone2ID" +} + +// SetDummyCAPCClusterVars resets the values in each of the exported CloudStackCluster related dummy variables. +// It is intended to be called in BeforeEach() functions. +func SetDummyCAPCClusterVars() { + DomainName = "FakeDomainName" + DomainID = "FakeDomainID" + Domain = cloud.Domain{Name: DomainName, ID: DomainID} + Level2DomainName = "foo/FakeDomainName" + Level2DomainID = "FakeLevel2DomainID" + Level2Domain = cloud.Domain{Name: Level2DomainName, ID: Level2DomainID} + AccountName = "FakeAccountName" + Account = cloud.Account{Name: AccountName, Domain: Domain} + AccountName = "FakeLevel2AccountName" + Level2Account = cloud.Account{Name: Level2AccountName, Domain: Level2Domain} + CSApiVersion = "infrastructure.cluster.x-k8s.io/v1beta3" + CSClusterKind = "CloudStackCluster" + ClusterName = "test-cluster" + EndPointHost = "EndpointHost" + SyncWithACS = ptr.To(true) + EndPointPort = int32(5309) + PublicIPID = "FakePublicIPID" + ClusterNameSpace = "default" + ClusterLabel = map[string]string{clusterv1.ClusterNameLabel: ClusterName} + AffinityGroup = &cloud.AffinityGroup{ + Name: "fakeaffinitygroup", + Type: cloud.AffinityGroupType, + ID: "FakeAffinityGroupID"} + Net1 = infrav1.Network{Name: GetYamlVal("CLOUDSTACK_NETWORK_NAME"), Type: cloud.NetworkTypeShared} + Net2 = infrav1.Network{Name: "SharedGuestNet2", Type: cloud.NetworkTypeShared, ID: "FakeSharedNetID2"} + ISONet1 = infrav1.Network{Name: "isoguestnet1", Type: cloud.NetworkTypeIsolated, ID: "FakeIsolatedNetID1"} + CSFailureDomain1 = &infrav1.CloudStackFailureDomain{ + TypeMeta: metav1.TypeMeta{ + APIVersion: CSApiVersion, + Kind: "CloudStackFailureDomain"}, + ObjectMeta: metav1.ObjectMeta{ + Name: infrav1.FailureDomainHashedMetaName("fd1", ClusterName), + Namespace: "default", + UID: "0", + Labels: ClusterLabel}, + Spec: infrav1.CloudStackFailureDomainSpec{Name: "fd1", Zone: Zone1, + ACSEndpoint: corev1.SecretReference{ + Namespace: ClusterNameSpace, + Name: ACSEndpointSecret1.Name}}} + CSFailureDomain2 = &infrav1.CloudStackFailureDomain{ + TypeMeta: metav1.TypeMeta{ + APIVersion: CSApiVersion, + Kind: "CloudStackFailureDomain"}, + ObjectMeta: metav1.ObjectMeta{ + Name: infrav1.FailureDomainHashedMetaName("fd2", ClusterName), + Namespace: "default", + UID: "0", + Labels: ClusterLabel}, + Spec: infrav1.CloudStackFailureDomainSpec{Name: "fd2", Zone: Zone2, + ACSEndpoint: corev1.SecretReference{ + Namespace: ClusterNameSpace, + Name: ACSEndpointSecret2.Name}}} + + CSAffinityGroup = &infrav1.CloudStackAffinityGroup{ + ObjectMeta: metav1.ObjectMeta{Name: AffinityGroup.Name, Namespace: "default", UID: "0", Labels: ClusterLabel}, + Spec: infrav1.CloudStackAffinityGroupSpec{ + FailureDomainName: CSFailureDomain1.Spec.Name, + Name: AffinityGroup.Name, + Type: AffinityGroup.Type, + ID: AffinityGroup.ID}} + + CSCluster = &infrav1.CloudStackCluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: CSApiVersion, + Kind: CSClusterKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ClusterName, + Namespace: "default", + UID: "0", + Labels: ClusterLabel, + }, + Spec: infrav1.CloudStackClusterSpec{ + ControlPlaneEndpoint: clusterv1.APIEndpoint{Host: EndPointHost, Port: EndPointPort}, + FailureDomains: []infrav1.CloudStackFailureDomainSpec{CSFailureDomain1.Spec, CSFailureDomain2.Spec}, + SyncWithACS: SyncWithACS, + }, + Status: infrav1.CloudStackClusterStatus{}, + } + CSISONet1 = &infrav1.CloudStackIsolatedNetwork{ + ObjectMeta: metav1.ObjectMeta{ + Name: ISONet1.Name, + Namespace: "default", + UID: "0", + Labels: ClusterLabel, + }, + Spec: infrav1.CloudStackIsolatedNetworkSpec{ + ControlPlaneEndpoint: CSCluster.Spec.ControlPlaneEndpoint}} + CSISONet1.Spec.Name = ISONet1.Name + CSISONet1.Spec.ID = ISONet1.ID +} + +func SetACSEndpointSecretVars() { + ACSEndpointSecret1 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ClusterNameSpace, + Name: "acsendpointsecret1"}, + StringData: map[string]string{ + "api-key": "someKey1", + "secret-key": "someSecretKey1", + "api-url": "http://someuri1:8080/client/api"}, + } + ACSEndpointSecret2 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ClusterNameSpace, + Name: "acsendpointsecret2"}, + StringData: map[string]string{ + "api-key": "someKey2", + "secret-key": "someSecretKey2", + "api-url": "http://someuri2:8080/client/api"}, + } +} + +// SetDummyCapiCluster resets the values in each of the exported CAPICluster related dummy variables. +func SetDummyCAPIClusterVars() { + CAPICluster = &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: ClusterName, + Namespace: ClusterNameSpace, + }, + Spec: clusterv1.ClusterSpec{ + InfrastructureRef: &corev1.ObjectReference{ + APIVersion: infrav1.GroupVersion.String(), + Kind: "CloudStackCluster", + Name: "somename", + }, + }, + } +} + +func SetDummyIsoNetToNameOnly() { + ISONet1.ID = "" + ISONet1.Type = "" + Zone1.Network = ISONet1 +} + +func SetDummyBootstrapSecretVar() { + BootstrapSecretName := "such-secret-much-wow" + BootstrapSecretValue := "{{ ds.meta_data.hostname }}{{ds.meta_data.failuredomain}}" + BootstrapSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ClusterNameSpace, + Name: BootstrapSecretName}, + Data: map[string][]byte{"value": []byte(BootstrapSecretValue)}} +} + +// Sets cluster spec to specified network. +func SetClusterSpecToNet(net *infrav1.Network) { + Zone1.Network = *net + CSFailureDomain1 = &infrav1.CloudStackFailureDomain{Spec: infrav1.CloudStackFailureDomainSpec{Zone: Zone1}} + CSCluster.Spec.FailureDomains = []infrav1.CloudStackFailureDomainSpec{CSFailureDomain1.Spec} +} + +func SetDummyCAPIMachineVars() { + CAPIMachine = &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "capi-test-machine-", + Namespace: "default", + Labels: ClusterLabel, + }, + Spec: clusterv1.MachineSpec{ + ClusterName: ClusterName, + FailureDomain: ptr.To("fd1")}, + } +} + +func SetDummyUserVars() { + User.Account = Account + UserID = "FakeUserId" + Username = "FakeUserName" + Apikey = "ApiKey" + SecretKey = "SecretKey" +} diff --git a/test/e2e/README.md b/test/e2e/README.md index 8a3e74e8..d0ba67aa 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -83,3 +83,90 @@ When this environment variable is set to *true* a 15s pause is inserted at the b - Launch the e2e test: *PAUSE_FOR_DEBUGGER_ATTACH=true JOB=MyTest make run-e2e* - Wait for console message: *Pausing 15s so you have a chance to attach a debugger to this process...* - Quickly attach your debugger to the e2e process (i.e., e2e.test) + +## CI/CD for e2e testing + +The community has set up a CI/CD pipeline using Jenkins for e2e testing. + +### How it works + +The CI/CD pipeline works as below + +- User triggers e2e testing by a Github PR comment, only repository OWNERS and a list of engineers are allowed; +- A program monitors the PR comments of the Github repository, parses the comments and kick Jenkins jobs; +- Jenkins creates a Apache CloudStack with specific version and hypervisor type, if needed; +- Jenkins runs CAPC e2e testing with specific Kubernetes versions and images; +- Jenkins posts the results of e2e testing as a Github PR comment, with the link of test logs. + +### How to use it + +Similar as other prow commands(see [here](https://prow.k8s.io/command-help?repo=kubernetes-sigs%2Fcluster-api-provider-cloudstack)), the e2e testing can be triggered by PR comment `/run-e2e`: + +``` +Usage: /run-e2e [-k Kubernetes_Version] [-c CloudStack_Version] [-h Hypervisor] [-i Template/Image] + [-f Kubernetes_Version_Upgrade_From] [-t Kubernetes_Version_Upgrade_To] +``` + +- Supported Kubernetes versions are: ['1.27.2', '1.26.5', '1.25.10', '1.24.14', '1.23.3', '1.22.6']. The default value is '1.27.2'. +- Supported CloudStack versions are: ['4.18', '4.17', '4.16']. If it is not set, an existing environment will be used. +- Supported hypervisors are: ['kvm', 'vmware', 'xen']. The default value is 'kvm'. +- Supported templates are: ['ubuntu-2004-kube', 'rockylinux-8-kube']. The default value is 'ubuntu-2004-kube'. +- By default it tests Kubernetes upgrade from version '1.26.5' to '1.27.2'. + +### Examples + +1. Examples of `/run-e2e` commands + +``` +/run-e2e +/run-e2e -k 1.27.2 -h kvm -i ubuntu-2004-kube +/run-e2e -k 1.27.2 -c 4.18 -h kvm -i ubuntu-2004-kube -f 1.26.5 -t 1.27.2 +``` + +2. Example of test results +``` +Test Results : (tid-126) +Environment: kvm Rocky8(x3), Advanced Networking with Management Server Rocky8 +Kubernetes Version: v1.27.2 +Kubernetes Version upgrade from: v1.26.5 +Kubernetes Version upgrade to: v1.27.2 +CloudStack Version: 4.18 +Template: ubuntu-2004-kube +E2E Test Run Logs: https://github.com/blueorangutan/capc-prs/releases/download/capc-pr-ci-cd/capc-e2e-artifacts-pr277-sl-126.zip + +[PASS] When testing Kubernetes version upgrades Should successfully upgrade kubernetes versions when there is a change in relevant fields +[PASS] When testing subdomain Should create a cluster in a subdomain +[PASS] When testing K8S conformance [Conformance] Should create a workload cluster and run kubetest +[PASS] When testing app deployment to the workload cluster with slow network [ToxiProxy] Should be able to download an HTML from the app deployed to the workload cluster +[PASS] When testing multiple CPs in a shared network with kubevip Should successfully create a cluster with multiple CPs in a shared network +[PASS] When testing resource cleanup Should create a new network when the specified network does not exist +[PASS] When testing app deployment to the workload cluster with network interruption [ToxiProxy] Should be able to create a cluster despite a network interruption during that process +[PASS] When testing node drain timeout A node should be forcefully removed if it cannot be drained in time +[PASS] When testing machine remediation Should replace a machine when it is destroyed +[PASS] When testing with custom disk offering Should successfully create a cluster with a custom disk offering +[PASS] When testing horizontal scale out/in [TC17][TC18][TC20][TC21] Should successfully scale machine replicas up and down horizontally +[PASS] When testing MachineDeployment rolling upgrades Should successfully upgrade Machines upon changes in relevant MachineDeployment fields +[PASS] with two clusters should successfully add and remove a second cluster without breaking the first cluster +[PASS] When testing with disk offering Should successfully create a cluster with disk offering +[PASS] When testing affinity group Should have host affinity group when affinity is pro +[PASS] When testing affinity group Should have host affinity group when affinity is anti +[PASS] When the specified resource does not exist Should fail due to the specified account is not found [TC4a] +[PASS] When the specified resource does not exist Should fail due to the specified domain is not found [TC4b] +[PASS] When the specified resource does not exist Should fail due to the specified control plane offering is not found [TC7] +[PASS] When the specified resource does not exist Should fail due to the specified template is not found [TC6] +[PASS] When the specified resource does not exist Should fail due to the specified zone is not found [TC3] +[PASS] When the specified resource does not exist Should fail due to the specified disk offering is not found +[PASS] When the specified resource does not exist Should fail due to the compute resources are not sufficient for the specified offering [TC8] +[PASS] When the specified resource does not exist Should fail due to the specified disk offer is not customized but the disk size is specified +[PASS] When the specified resource does not exist Should fail due to the specified disk offer is customized but the disk size is not specified +[PASS] When the specified resource does not exist Should fail due to the public IP can not be found +[PASS] When the specified resource does not exist When starting with a healthy cluster Should fail to upgrade worker machine due to insufficient compute resources +[PASS] When the specified resource does not exist When starting with a healthy cluster Should fail to upgrade control plane machine due to insufficient compute resources +[PASS] When testing app deployment to the workload cluster [TC1][PR-Blocking] Should be able to download an HTML from the app deployed to the workload cluster + + +Ran 28 of 29 Specs in 10458.173 seconds +SUCCESS! -- 28 Passed | 0 Failed | 0 Pending | 1 Skipped +PASS + +``` diff --git a/test/e2e/affinity_group.go b/test/e2e/affinity_group.go index bc902c23..ca032ae5 100644 --- a/test/e2e/affinity_group.go +++ b/test/e2e/affinity_group.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/affinity_group_test.go b/test/e2e/affinity_group_test.go index 567dba16..9b229550 100644 --- a/test/e2e/affinity_group_test.go +++ b/test/e2e/affinity_group_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing affinity group", func() { diff --git a/test/e2e/common.go b/test/e2e/common.go index 0496a2fd..f87369c5 100644 --- a/test/e2e/common.go +++ b/test/e2e/common.go @@ -21,16 +21,18 @@ import ( "encoding/json" "errors" "fmt" + "maps" "path/filepath" + "strconv" "strings" "time" - "strconv" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog/v2" "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/blang/semver" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" corev1 "k8s.io/api/core/v1" . "github.com/onsi/gomega" @@ -61,12 +63,25 @@ const ( InvalidDomainName = "CLOUDSTACK_INVALID_DOMAIN_NAME" InvalidTemplateName = "CLOUDSTACK_INVALID_TEMPLATE_NAME" InvalidCPOfferingName = "CLOUDSTACK_INVALID_CONTROL_PLANE_MACHINE_OFFERING" + + CustomNetworkOfferingWithEgressPolicyName = "CLOUDSTACK_NETWORK_CUSTOM_OFFERING_EGRESS_POLICY_TRUE_NAME" + NetworkNameWithCustomOffering = "CLOUDSTACK_NETWORK_NAME_WITH_CUSTOM_OFFERING" + + VPCName = "CLOUDSTACK_VPC_NAME" + VPCNetworkName = "CLOUDSTACK_VPC_NETWORK_NAME" + CustomVPCOfferingName = "CLOUDSTACK_CUSTOM_VPC_OFFERING_NAME" + CustomVPCNetworkOfferingName = "CLOUDSTACK_CUSTOM_VPC_NETWORK_OFFERING_NAME" + VPCWithCustomOfferingName = "CLOUDSTACK_VPC_NAME_WITH_CUSTOM_OFFERING" + VPCNetworkWithCustomOfferingName = "CLOUDSTACK_VPC_NETWORK_NAME_WITH_CUSTOM_OFFERING" ) const ( - ControlPlaneIndicator = "control-plane" - MachineDeploymentIndicator = "md" + ControlPlaneIndicator = "-control-plane-" + MachineDeploymentIndicator = "-md-" DataVolumePrefix = "DATA-" + DefaultVPCOffering = "Default VPC offering" + DefaultVPCNetworkOffering = "DefaultIsolatedNetworkOfferingForVpcNetworks" + DefaultNetworkOffering = "DefaultIsolatedNetworkOfferingWithSourceNatService" ) type CommonSpecInput struct { @@ -114,7 +129,7 @@ func dumpSpecResourcesAndCleanup(ctx context.Context, specName string, clusterPr }) if !skipCleanup { - Byf("Deleting cluster %s/%s", cluster.Namespace, cluster.Name) + Byf("Deleting cluster %s", klog.KObj(cluster)) // While https://github.com/kubernetes-sigs/cluster-api/issues/2955 is addressed in future iterations, there is a chance // that cluster variable is not set even if the cluster exists, so we are calling DeleteAllClustersAndWait // instead of DeleteClusterAndWait @@ -172,13 +187,13 @@ func GetK8sObject(ctx context.Context, resourceType, name, namespace, kubeconfig getArgs := []string{"--ignore-not-found", "--namespace", namespace, resourceType, name, "-o", "json"} stdOut, err := KubectlExec(ctx, "get", kubeconfigPath, getArgs...) if err != nil { - return fmt.Errorf("getting %s/%s/%s with kubectl: %v", resourceType, namespace, name, err) + return fmt.Errorf("getting %s/%s with kubectl: %v", resourceType, klog.KRef(namespace, name), err) } if len(stdOut) == 0 { - return fmt.Errorf("not found %s/%s/%s", resourceType, namespace, name) + return fmt.Errorf("not found %s/%s", resourceType, klog.KRef(namespace, name)) } if err = json.Unmarshal([]byte(stdOut), obj); err != nil { - return fmt.Errorf("parsing %s/%s/%s response: %v", resourceType, namespace, name, err) + return fmt.Errorf("parsing %s/%s response: %v", resourceType, klog.KRef(namespace, name), err) } return nil @@ -250,8 +265,19 @@ func DownloadMetricsFromCAPCManager(ctx context.Context, bootstrapKubeconfigPath return result, nil } +func GetACSVersion(client *cloudstack.CloudStackClient) (string, error) { + msServersResp, err := client.Management.ListManagementServersMetrics(client.Management.NewListManagementServersMetricsParams()) + if err != nil { + return "", err + } + if msServersResp.Count == 0 { + return "", errors.New("no management servers found") + } + return msServersResp.ManagementServersMetrics[0].Version, nil +} + func DestroyOneMachine(client *cloudstack.CloudStackClient, clusterName string, machineType string) { - matcher := clusterName + "-" + machineType + matcher := clusterName + machineType Byf("Listing machines with %q", matcher) listResp, err := client.VirtualMachine.ListVirtualMachines(client.VirtualMachine.NewListVirtualMachinesParams()) @@ -279,12 +305,16 @@ func DestroyOneMachine(client *cloudstack.CloudStackClient, clusterName string, } func CheckAffinityGroupsDeleted(client *cloudstack.CloudStackClient, affinityIds []string) error { + return CheckAffinityGroupsDeletedInProject(client, affinityIds, "") +} + +func CheckAffinityGroupsDeletedInProject(client *cloudstack.CloudStackClient, affinityIds []string, project string) error { if len(affinityIds) == 0 { return errors.New("affinityIds are empty") } for _, affinityId := range affinityIds { - affinity, count, _ := client.AffinityGroup.GetAffinityGroupByID(affinityId) + affinity, count, _ := client.AffinityGroup.GetAffinityGroupByID(affinityId, cloudstack.WithProject(project)) if count > 0 { return errors.New("Affinity group " + affinity.Name + " still exists") } @@ -311,9 +341,21 @@ func GetHostCount(client *cloudstack.CloudStackClient, zoneName string) int { } func CheckAffinityGroup(client *cloudstack.CloudStackClient, clusterName string, affinityType string) []string { + return CheckAffinityGroupInProject(client, clusterName, affinityType, "") +} + +func CheckAffinityGroupInProject(client *cloudstack.CloudStackClient, clusterName string, affinityType string, project string) []string { By("Listing all machines") p := client.VirtualMachine.NewListVirtualMachinesParams() p.SetListall(true) + + if project != "" { + projectID, _, err := client.Project.GetProjectID(project) + if err != nil { + Fail("Failed to get project: " + err.Error()) + } + p.SetProjectid(projectID) + } listResp, err := client.VirtualMachine.ListVirtualMachines(p) if err != nil { Fail("Failed to list machines: " + err.Error()) @@ -333,18 +375,21 @@ func CheckAffinityGroup(client *cloudstack.CloudStackClient, clusterName string, for _, affinity := range vm.Affinitygroup { affinityIds = append(affinityIds, affinity.Id) - affinity, _, _ := client.AffinityGroup.GetAffinityGroupByID(affinity.Id) + // Store the ID before potential nil issues + affinityID := affinity.Id + // Rename the variable to avoid shadowing + affinityGroup, _, err := client.AffinityGroup.GetAffinityGroupByID(affinityID, cloudstack.WithProject(project)) if err != nil { - Fail("Failed to get affinity group for " + affinity.Id + " : " + err.Error()) + Fail("Failed to get affinity group for " + affinityID + " : " + err.Error()) } - if !strings.Contains(affinity.Name, affinityTypeString) { - Fail(affinity.Name + " does not contain " + affinityTypeString) + if !strings.Contains(affinityGroup.Name, affinityTypeString) { + Fail(affinityGroup.Name + " does not contain " + affinityTypeString) } - if affinityType == "pro" && affinity.Type != "host affinity" { - Fail(affinity.Type + " does not match " + affinityType) + if affinityType == "pro" && affinityGroup.Type != "host affinity" { + Fail(affinityGroup.Type + " does not match " + affinityType) } - if affinityType == "anti" && affinity.Type != "host anti-affinity" { - Fail(affinity.Type + " does not match " + affinityType) + if affinityType == "anti" && affinityGroup.Type != "host anti-affinity" { + Fail(affinityGroup.Type + " does not match " + affinityType) } } } @@ -360,11 +405,35 @@ func CheckNetworkExists(client *cloudstack.CloudStackClient, networkName string) } return false, err } else if count > 1 { - return false, fmt.Errorf("Expected 0-1 Network with name %s, but got %d.", networkName, count) + return false, fmt.Errorf("expected 0-1 network with name %s, but got %d", networkName, count) } return count == 1, nil } +func CheckVPCExistsInProject(client *cloudstack.CloudStackClient, vpcName string, project string) (bool, error) { + p := client.VPC.NewListVPCsParams() + p.SetName(vpcName) + + if project != "" { + projectID, _, err := client.Project.GetProjectID(project) + if err != nil { + Fail("Failed to get project: " + err.Error()) + } + p.SetProjectid(projectID) + } + + listResp, err := client.VPC.ListVPCs(p) + if err != nil { + if strings.Contains(err.Error(), "No match found for") { + return false, nil + } + return false, err + } else if listResp.Count > 1 { + return false, fmt.Errorf("expected 0-1 vpc with name %s, but got %d", vpcName, listResp.Count) + } + return listResp.Count == 1, nil +} + func CreateCloudStackClient(ctx context.Context, kubeConfigPath string) *cloudstack.CloudStackClient { By("Getting a CloudStack client secret") secret := &corev1.Secret{} @@ -507,6 +576,95 @@ func IsClusterReady(ctx context.Context, mgmtClient client.Client, cluster *clus return c.Status.ControlPlaneReady && c.Status.InfrastructureReady } +func EnsureSecondaryNetworkExists(client *cloudstack.CloudStackClient, input CommonSpecInput) (*cloudstack.Network, error) { + secondaryNetName := input.E2EConfig.GetVariable("CLOUDSTACK_NEW_NETWORK_NAME") + + By("Fetching secondary network details") + // Try fetching secondary network + secondaryNet, _, err := client.Network.GetNetworkByName(secondaryNetName) + if err == nil && secondaryNet != nil { + By(fmt.Sprintf("Network %q already exists", secondaryNetName)) + return secondaryNet, nil + } + + By("Listing Zone") + zoneName := input.E2EConfig.GetVariable("CLOUDSTACK_ZONE_NAME") + pz := client.Zone.NewListZonesParams() + pz.SetName(zoneName) + listZonesResponse, err := client.Zone.ListZones(pz) + Expect(err).To(BeNil(), "error listing zones") + Expect(listZonesResponse.Count).To(Equal(1), "no zones, or more than one zone resolve to zone name %s", zoneName) + zoneId := listZonesResponse.Zones[0].Id + + By("Listing network offerings") + networkOffering, _, err := client.NetworkOffering.GetNetworkOfferingByName(DefaultNetworkOffering) + Expect(err).To(BeNil(), "error fetching network offering %q", DefaultNetworkOffering) + Expect(networkOffering).ToNot(BeNil(), "network offering %q not found", DefaultNetworkOffering) + + // Create new network using zone and offering from primary + By("Create secondary network") + createParams := client.Network.NewCreateNetworkParams( + secondaryNetName, + networkOffering.Id, + zoneId, + ) + + newNetResp, err := client.Network.CreateNetwork(createParams) + if err != nil { + return nil, fmt.Errorf("failed to create network %q: %w", secondaryNetName, err) + } + + newNet, _, err := client.Network.GetNetworkByID(newNetResp.Id) + if err != nil { + return nil, fmt.Errorf("failed to fetch created network %q by ID: %w", newNetResp.Id, err) + } + + By("Created secondary network") + By(fmt.Sprintf("Created secondary network %q", secondaryNetName)) + return newNet, nil +} + +func CheckIfNodesHaveTwoNICs(client *cloudstack.CloudStackClient, clusterName string, input CommonSpecInput) { + requiredNetworks := map[string]bool{ + input.E2EConfig.GetVariable("CLOUDSTACK_NETWORK_NAME"): false, + input.E2EConfig.GetVariable("CLOUDSTACK_NEW_NETWORK_NAME"): false, + } + + Byf("Listing machines with name containing %q", clusterName) + listResp, err := client.VirtualMachine.ListVirtualMachines(client.VirtualMachine.NewListVirtualMachinesParams()) + Expect(err).NotTo(HaveOccurred(), "Failed to list virtual machines from CloudStack") + for _, vm := range listResp.VirtualMachines { + if !strings.Contains(vm.Name, clusterName) { + continue + } + + if len(vm.Nic) < 2 { + Fail(fmt.Sprintf("VM %q has fewer than 2 NICs. Found: %d", vm.Name, len(vm.Nic))) + } + + foundNetworks := make(map[string]bool) + for _, nic := range vm.Nic { + foundNetworks[nic.Networkname] = true + } + + for required := range requiredNetworks { + if !foundNetworks[required] { + Fail(fmt.Sprintf("VM %q is missing required network %q", vm.Name, required)) + } + } + + By(fmt.Sprintf("VM %q has required NICs: %v", vm.Name, maps.Keys(foundNetworks))) + } +} + +func keys(m map[string]bool) []string { + var list []string + for k := range m { + list = append(list, k) + } + return list +} + func CheckDiskOfferingOfVmInstances(client *cloudstack.CloudStackClient, clusterName string, diskOfferingName string) { Byf("Listing machines with %q", clusterName) listResp, err := client.VirtualMachine.ListVirtualMachines(client.VirtualMachine.NewListVirtualMachinesParams()) diff --git a/test/e2e/config/cloudstack.yaml b/test/e2e/config/cloudstack.yaml index 3b87457b..e80d16e1 100644 --- a/test/e2e/config/cloudstack.yaml +++ b/test/e2e/config/cloudstack.yaml @@ -15,27 +15,27 @@ images: ## PLEASE KEEP THESE UP TO DATE WITH THE COMPONENTS # Cluster API v1beta1 Preloads - - name: gcr.io/k8s-staging-cluster-api/cluster-api-controller:v1.2.12 + - name: gcr.io/k8s-staging-cluster-api/cluster-api-controller:v1.9.6 loadBehavior: tryLoad - - name: gcr.io/k8s-staging-cluster-api/kubeadm-bootstrap-controller:v1.2.12 + - name: gcr.io/k8s-staging-cluster-api/kubeadm-bootstrap-controller:v1.9.6 loadBehavior: tryLoad - - name: gcr.io/k8s-staging-cluster-api/kubeadm-control-plane-controller:v1.2.12 + - name: gcr.io/k8s-staging-cluster-api/kubeadm-control-plane-controller:v1.9.6 loadBehavior: tryLoad - - name: gcr.io/k8s-staging-cluster-api/capd-manager-amd64:v1.0.0 + - name: gcr.io/k8s-staging-cluster-api/capd-manager-amd64:v1.9.6 loadBehavior: tryLoad - - name: quay.io/jetstack/cert-manager-cainjector:v1.5.3 + - name: quay.io/jetstack/cert-manager-cainjector:v1.13.1 loadBehavior: tryLoad - - name: quay.io/jetstack/cert-manager-webhook:v1.5.3 + - name: quay.io/jetstack/cert-manager-webhook:v1.13.1 loadBehavior: tryLoad - - name: quay.io/jetstack/cert-manager-controller:v1.5.3 + - name: quay.io/jetstack/cert-manager-controller:v1.13.1 loadBehavior: tryLoad providers: - name: cluster-api type: CoreProvider versions: - - name: v1.2.12 - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.12/core-components.yaml" + - name: v1.9.6 + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.9.6/core-components.yaml" type: "url" contract: v1beta1 replacements: @@ -47,8 +47,8 @@ providers: - name: kubeadm type: BootstrapProvider versions: - - name: v1.2.12 - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.12/bootstrap-components.yaml" + - name: v1.9.6 + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.9.6/bootstrap-components.yaml" type: "url" contract: v1beta1 replacements: @@ -60,8 +60,8 @@ providers: - name: kubeadm type: ControlPlaneProvider versions: - - name: v1.2.12 - value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.2.12/control-plane-components.yaml" + - name: v1.9.6 + value: "https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.9.6/control-plane-components.yaml" type: "url" contract: v1beta1 replacements: @@ -73,30 +73,36 @@ providers: - name: cloudstack type: InfrastructureProvider files: - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-zone.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-account.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-domain.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-template.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-cp-offering.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-insufficient-compute-resources.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-insufficient-compute-resources-for-upgrade.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-node-drain.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-machine-remediation.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-affinity-group-pro.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-affinity-group-anti.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-resource-cleanup.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-second-cluster.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-shared-network-kubevip.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-disk-offering.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-disk-offering-size-for-non-customized.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-disk-offering-size-for-customized.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-disk-offering.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-custom-disk-offering.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-subdomain.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-invalid-ip.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-kubernetes-version-upgrade-before.yaml" - - sourcePath: "../data/infrastructure-cloudstack/v1beta2/cluster-template-kubernetes-version-upgrade-after.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-project.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks.yaml" + - sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks.yaml" - sourcePath: "../data/shared/v1beta1_provider/metadata.yaml" versions: - name: v1.0.0 @@ -105,14 +111,12 @@ providers: replacements: - old: --metrics-bind-addr=localhost:8080 new: --metrics-bind-addr=:8080 - - old: "--leader-elect" - new: "--leader-elect\n - --metrics-bind-addr=:8080" variables: - KUBERNETES_VERSION_MANAGEMENT: "v1.25.3" - KUBERNETES_VERSION: "v1.24.14" - KUBERNETES_VERSION_UPGRADE_FROM: "v1.23.3" - KUBERNETES_VERSION_UPGRADE_TO: "v1.24.14" + KUBERNETES_VERSION_MANAGEMENT: "v1.27.3" + KUBERNETES_VERSION: "v1.29.15" + KUBERNETES_VERSION_UPGRADE_FROM: "v1.28.15" + KUBERNETES_VERSION_UPGRADE_TO: "v1.29.15" CNI: "./data/cni/kindnet.yaml" IP_FAMILY: "IPv4" NODE_DRAIN_TIMEOUT: "60s" @@ -124,10 +128,26 @@ variables: CLOUDSTACK_INVALID_ZONE_NAME: zoneXXXX CLOUDSTACK_INVALID_NETWORK_NAME: networkXXXX CLOUDSTACK_ACCOUNT_NAME: admin + CLOUDSTACK_PROJECT_NAME: capc-e2e-test CLOUDSTACK_INVALID_ACCOUNT_NAME: accountXXXX CLOUDSTACK_DOMAIN_NAME: ROOT CLOUDSTACK_INVALID_DOMAIN_NAME: domainXXXX + CLOUDSTACK_NETWORK_NAME: isolated-for-e2e-1 + CLOUDSTACK_NETWORK_NAME_WITH_CUSTOM_OFFERING: isolated-for-e2e-1-with-custom-offering + CLOUDSTACK_NETWORK_CUSTOM_OFFERING_EGRESS_POLICY_TRUE_NAME: CustomNetworkOfferingWithEgressPolicyTrue + + CLOUDSTACK_VPC_NETWORK_NAME: vpc-isolated-for-e2e-1 + CLOUDSTACK_VPC_NAME: vpc-for-e2e-1 + CLOUDSTACK_VPC_CIDR: 10.10.0.0/16 + + CLOUDSTACK_VPC_NAME_WITH_CUSTOM_OFFERING: vpc-for-e2e-1-with-custom-offering + CLOUDSTACK_VPC_NETWORK_NAME_WITH_CUSTOM_OFFERING: vpc-isolated-for-e2e-1-with-custom-offering + CLOUDSTACK_CUSTOM_VPC_OFFERING_NAME: "CustomVPCOffering" + CLOUDSTACK_CUSTOM_VPC_NETWORK_OFFERING_NAME: "CustomVPCNetworkOffering" + + CLOUDSTACK_GATEWAY: 10.10.0.1 + CLOUDSTACK_NETMASK: 255.255.255.0 CLOUDSTACK_NEW_NETWORK_NAME: isolated-for-e2e-new CLOUDSTACK_SHARED_NETWORK_NAME: Shared1 CLUSTER_ENDPOINT_IP: 172.16.2.199 @@ -159,14 +179,15 @@ variables: CONFORMANCE_CONFIGURATION: "./data/kubetest/conformance.yaml" CONFORMANCE_WORKER_MACHINE_COUNT: "3" CONFORMANCE_CONTROL_PLANE_MACHINE_COUNT: "1" + CAPC_CLOUDSTACKMACHINE_CKS_SYNC: "true" intervals: conformance/wait-control-plane: ["20m", "10s"] conformance/wait-worker-nodes: ["20m", "10s"] default/wait-errors: ["5m", "10s"] - default/wait-controllers: ["5m", "10s"] - default/wait-cluster: ["5m", "10s"] + default/wait-controllers: ["10m", "10s"] + default/wait-cluster: ["10m", "10s"] default/wait-control-plane: ["20m", "10s"] default/wait-worker-nodes: ["20m", "10s"] default/wait-delete-cluster: ["20m", "10s"] diff --git a/test/e2e/custom_disk_offering.go b/test/e2e/custom_disk_offering.go index 8d26e7cd..675932f9 100644 --- a/test/e2e/custom_disk_offering.go +++ b/test/e2e/custom_disk_offering.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/custom_disk_offering_test.go b/test/e2e/custom_disk_offering_test.go index a9cea700..8a834ca8 100644 --- a/test/e2e/custom_disk_offering_test.go +++ b/test/e2e/custom_disk_offering_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing with custom disk offering", func() { diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/bases/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/bases/cluster-with-kcp.yaml new file mode 100644 index 00000000..2b719aa9 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/bases/cluster-with-kcp.yaml @@ -0,0 +1,80 @@ +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - 192.168.0.0/16 + serviceDomain: "cluster.local" + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackCluster + name: ${CLUSTER_NAME} + controlPlaneRef: + kind: KubeadmControlPlane + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + name: ${CLUSTER_NAME}-control-plane +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + controlPlaneEndpoint: + host: "" + port: 6443 + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + syncWithACS: false +--- +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + clusterConfiguration: + imageRepository: "" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: ${CLUSTER_NAME}-control-plane + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/bases/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/bases/md.yaml new file mode 100644 index 00000000..d8c8bd06 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/bases/md.yaml @@ -0,0 +1,51 @@ +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: "${CLUSTER_NAME}-md-0" +spec: + clusterName: "${CLUSTER_NAME}" + replicas: ${WORKER_MACHINE_COUNT} + selector: + matchLabels: null + template: + spec: + clusterName: "${CLUSTER_NAME}" + version: ${KUBERNETES_VERSION} + bootstrap: + configRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + infrastructureRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti/md.yaml new file mode 100644 index 00000000..4d4a1375 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-anti/md.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: anti +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: anti diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro/md.yaml new file mode 100644 index 00000000..a1ee84b4 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-affinity-group-pro/md.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/cluster-with-custom-disk-offering.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/cluster-with-custom-disk-offering.yaml new file mode 100644 index 00000000..1f95810d --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/cluster-with-custom-disk-offering.yaml @@ -0,0 +1,44 @@ +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + clusterConfiguration: + imageRepository: "" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + diskSetup: + filesystems: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE}1 + overwrite: false + extraOpts: + - -E + - lazy_itable_init=1,lazy_journal_init=1 + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: data_disk + partitions: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + layout: true + overwrite: false + tableType: gpt + mounts: + - - LABEL=${CLOUDSTACK_DISK_OFFERING_LABEL} + - ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: ${CLUSTER_NAME}-control-plane + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/kustomization.yaml new file mode 100644 index 00000000..4764e115 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/kustomization.yaml @@ -0,0 +1,7 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cluster-with-custom-disk-offering.yaml +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/md.yaml new file mode 100644 index 00000000..8d019da7 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-disk-offering/md.yaml @@ -0,0 +1,71 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_CUSTOM_DISK_OFFERING_NAME} + customSizeInGB: ${CLOUDSTACK_DISK_OFFERING_CUSTOM_SIZE} + mountPath: ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} + device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: ${CLOUDSTACK_DISK_OFFERING_LABEL} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_CUSTOM_DISK_OFFERING_NAME} + customSizeInGB: ${CLOUDSTACK_DISK_OFFERING_CUSTOM_SIZE} + mountPath: ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} + device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: ${CLOUDSTACK_DISK_OFFERING_LABEL} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + diskSetup: + filesystems: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE}1 + overwrite: false + extraOpts: + - -E + - lazy_itable_init=1,lazy_journal_init=1 + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: data_disk + partitions: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + layout: true + overwrite: false + tableType: gpt + mounts: + - - LABEL=${CLOUDSTACK_DISK_OFFERING_LABEL} + - ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering/cluster-with-custom-vpc-offering.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering/cluster-with-custom-vpc-offering.yaml new file mode 100644 index 00000000..d007915c --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering/cluster-with-custom-vpc-offering.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_VPC_NETWORK_NAME_WITH_CUSTOM_OFFERING} + gateway: ${CLOUDSTACK_GATEWAY} + netmask: ${CLOUDSTACK_NETMASK} + offering: ${CLOUDSTACK_CUSTOM_VPC_NETWORK_OFFERING_NAME} + vpc: + name: ${CLOUDSTACK_VPC_NAME_WITH_CUSTOM_OFFERING} + cidr: ${CLOUDSTACK_VPC_CIDR} + offering: ${CLOUDSTACK_CUSTOM_VPC_OFFERING_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering/kustomization.yaml new file mode 100644 index 00000000..0da8be4e --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-custom-vpc-offering/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cluster-with-custom-vpc-offering.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/cluster-with-disk-offering.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/cluster-with-disk-offering.yaml new file mode 100644 index 00000000..1f95810d --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/cluster-with-disk-offering.yaml @@ -0,0 +1,44 @@ +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + clusterConfiguration: + imageRepository: "" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + diskSetup: + filesystems: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE}1 + overwrite: false + extraOpts: + - -E + - lazy_itable_init=1,lazy_journal_init=1 + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: data_disk + partitions: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + layout: true + overwrite: false + tableType: gpt + mounts: + - - LABEL=${CLOUDSTACK_DISK_OFFERING_LABEL} + - ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: ${CLUSTER_NAME}-control-plane + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/kustomization.yaml new file mode 100644 index 00000000..b6e59811 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/kustomization.yaml @@ -0,0 +1,7 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cluster-with-disk-offering.yaml +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/md.yaml new file mode 100644 index 00000000..c7c6ce8a --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-disk-offering/md.yaml @@ -0,0 +1,69 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_DISK_OFFERING_NAME} + mountPath: ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} + device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: ${CLOUDSTACK_DISK_OFFERING_LABEL} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_DISK_OFFERING_NAME} + mountPath: ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} + device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: ${CLOUDSTACK_DISK_OFFERING_LABEL} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + diskSetup: + filesystems: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE}1 + overwrite: false + extraOpts: + - -E + - lazy_itable_init=1,lazy_journal_init=1 + filesystem: ${CLOUDSTACK_DISK_OFFERING_FILESYSTEM} + label: data_disk + partitions: + - device: ${CLOUDSTACK_DISK_OFFERING_DEVICE} + layout: true + overwrite: false + tableType: gpt + mounts: + - - LABEL=${CLOUDSTACK_DISK_OFFERING_LABEL} + - ${CLOUDSTACK_DISK_OFFERING_MOUNT_PATH} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade/kustomization.yaml new file mode 100644 index 00000000..a3e6cbe6 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade/kustomization.yaml @@ -0,0 +1,4 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + - upgrade.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade/upgrade.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade/upgrade.yaml new file mode 100644 index 00000000..e63874db --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources-for-upgrade/upgrade.yaml @@ -0,0 +1,25 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-upgrade-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_IMPOSSIBLE_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-upgrade-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_IMPOSSIBLE_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources/md.yaml new file mode 100644 index 00000000..719c1893 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-insufficient-compute-resources/md.yaml @@ -0,0 +1,12 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_IMPOSSIBLE_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account/cloudstack-cluster.yaml new file mode 100644 index 00000000..c033a00f --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account/cloudstack-cluster.yaml @@ -0,0 +1,19 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + account: ${CLOUDSTACK_INVALID_ACCOUNT_NAME} + domain: ${CLOUDSTACK_DOMAIN_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account/kustomization.yaml new file mode 100644 index 00000000..7a6d2984 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-account/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering/md.yaml new file mode 100644 index 00000000..9365771e --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-cp-offering/md.yaml @@ -0,0 +1,12 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_INVALID_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized/md.yaml new file mode 100644 index 00000000..64ac7d3b --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized/md.yaml @@ -0,0 +1,18 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_CUSTOM_DISK_OFFERING_NAME} + mountPath: /disk + device: /dev/vdb + filesystem: ext4 + label: my_disk + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized/md.yaml new file mode 100644 index 00000000..3bb828f3 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized/md.yaml @@ -0,0 +1,19 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_DISK_OFFERING_NAME} + customSizeInGB: 1 + mountPath: /disk + device: /dev/vdb + filesystem: ext4 + label: my_disk + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering/md.yaml new file mode 100644 index 00000000..49bd4b56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering/md.yaml @@ -0,0 +1,18 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + diskOffering: + name: ${CLOUDSTACK_INVALID_DISK_OFFERING_NAME} + mountPath: /disk + device: /dev/vdb + filesystem: ext4 + label: my_disk + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain/cloudstack-cluster.yaml new file mode 100644 index 00000000..5861204a --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain/cloudstack-cluster.yaml @@ -0,0 +1,19 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + account: ${CLOUDSTACK_ACCOUNT_NAME} + domain: ${CLOUDSTACK_INVALID_DOMAIN_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain/kustomization.yaml new file mode 100644 index 00000000..7a6d2984 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-domain/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip/cloudstack-cluster.yaml new file mode 100644 index 00000000..ab3f33b4 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip/cloudstack-cluster.yaml @@ -0,0 +1,17 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + controlPlaneEndpoint: + host: ${CLOUDSTACK_INVALID_IP} + port: 6443 \ No newline at end of file diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip/kustomization.yaml new file mode 100644 index 00000000..7a6d2984 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-ip/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template/kustomization.yaml new file mode 100644 index 00000000..0b64be56 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template/md.yaml new file mode 100644 index 00000000..77369e01 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-template/md.yaml @@ -0,0 +1,12 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_INVALID_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone/cloudstack-cluster.yaml new file mode 100644 index 00000000..2b12a5e0 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone/cloudstack-cluster.yaml @@ -0,0 +1,17 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + name: ${CLOUDSTACK_FD1_NAME} + zone: + name: ${CLOUDSTACK_INVALID_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone/kustomization.yaml new file mode 100644 index 00000000..7a6d2984 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-zone/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks/cloudstack-cluster.yaml new file mode 100644 index 00000000..7bb1a966 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks/cloudstack-cluster.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + controlPlaneEndpoint: + host: "" + port: 6443 + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + syncWithACS: true diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks/kustomization.yaml new file mode 100644 index 00000000..7a6d2984 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-k8s-cks/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/cluster.yaml new file mode 100644 index 00000000..54c57589 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/cluster.yaml @@ -0,0 +1,36 @@ +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - 192.168.0.0/16 + serviceDomain: "cluster.local" + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackCluster + name: ${CLUSTER_NAME} + controlPlaneRef: + kind: KubeadmControlPlane + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + name: ${CLUSTER_NAME}-control-plane +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + controlPlaneEndpoint: + host: ${CLUSTER_ENDPOINT_IP} + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/kustomization.yaml new file mode 100644 index 00000000..6e3485a9 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/kustomization.yaml @@ -0,0 +1,4 @@ +bases: + - ./cluster.yaml + - ./upgrade-md.yaml + - ./upgrade-cp.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/upgrade-cp.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/upgrade-cp.yaml new file mode 100644 index 00000000..14fd73b7 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/upgrade-cp.yaml @@ -0,0 +1,41 @@ +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: cloudstack:///'{{ ds.meta_data.instance_id }}' + clusterConfiguration: + imageRepository: "" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: cloudstack:///'{{ ds.meta_data.instance_id }}' + preKubeadmCommands: + - swapoff -a + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: ${CLUSTER_NAME}-control-plane-upgraded + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION_UPGRADE_TO} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane-upgraded +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_UPGRADE_TO_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- \ No newline at end of file diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/upgrade-md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/upgrade-md.yaml new file mode 100644 index 00000000..b889a339 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-after/upgrade-md.yaml @@ -0,0 +1,51 @@ +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: "${CLUSTER_NAME}-md-0" +spec: + clusterName: "${CLUSTER_NAME}" + replicas: ${WORKER_MACHINE_COUNT} + selector: + matchLabels: null + template: + spec: + clusterName: "${CLUSTER_NAME}" + version: ${KUBERNETES_VERSION_UPGRADE_TO} + bootstrap: + configRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + infrastructureRef: + name: "${CLUSTER_NAME}-md-1" + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-1 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_UPGRADE_TO_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + preKubeadmCommands: + - swapoff -a + diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/before.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/before.yaml new file mode 100644 index 00000000..c207f68a --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/before.yaml @@ -0,0 +1,77 @@ +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: cloudstack:///'{{ ds.meta_data.instance_id }}' + clusterConfiguration: + imageRepository: "" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: cloudstack:///'{{ ds.meta_data.instance_id }}' + preKubeadmCommands: + - swapoff -a + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: ${CLUSTER_NAME}-control-plane + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION_UPGRADE_FROM} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_UPGRADE_FROM_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: "${CLUSTER_NAME}-md-0" +spec: + clusterName: "${CLUSTER_NAME}" + replicas: ${WORKER_MACHINE_COUNT} + selector: + matchLabels: null + template: + spec: + clusterName: "${CLUSTER_NAME}" + version: ${KUBERNETES_VERSION_UPGRADE_FROM} + bootstrap: + configRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 + kind: KubeadmConfigTemplate + infrastructureRef: + name: "${CLUSTER_NAME}-md-0" + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_UPGRADE_FROM_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/cloudstack-cluster.yaml new file mode 100644 index 00000000..0a691a6c --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/cloudstack-cluster.yaml @@ -0,0 +1,17 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + controlPlaneEndpoint: + host: ${CLUSTER_ENDPOINT_IP} + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/kustomization.yaml new file mode 100644 index 00000000..61a94460 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-kubernetes-version-upgrade-before/kustomization.yaml @@ -0,0 +1,8 @@ + +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: + - ./cloudstack-cluster.yaml + - ./before.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/kustomization.yaml new file mode 100644 index 00000000..157b18f8 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/kustomization.yaml @@ -0,0 +1,7 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + - mhc.yaml + +patchesStrategicMerge: +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/md.yaml new file mode 100644 index 00000000..3d5f253e --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/md.yaml @@ -0,0 +1,9 @@ +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: "${CLUSTER_NAME}-md-0" +spec: + template: + metadata: + labels: + "e2e.mhc.label": "" diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/mhc.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/mhc.yaml new file mode 100644 index 00000000..fe70e1f7 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-machine-remediation/mhc.yaml @@ -0,0 +1,36 @@ +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineHealthCheck +metadata: + name: "${CLUSTER_NAME}-mhc-md-0" +spec: + clusterName: "${CLUSTER_NAME}" + maxUnhealthy: 100% + selector: + matchLabels: + e2e.mhc.label: "" + unhealthyConditions: + - type: Ready + status: Unknown + timeout: 60s + - type: Ready + status: "False" + timeout: 60s +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineHealthCheck +metadata: + name: "${CLUSTER_NAME}-mhc-control-plane-0" +spec: + clusterName: "${CLUSTER_NAME}" + maxUnhealthy: 100% + selector: + matchLabels: + cluster.x-k8s.io/control-plane: "" + unhealthyConditions: + - type: Ready + status: Unknown + timeout: 60s + - type: Ready + status: "False" + timeout: 60s \ No newline at end of file diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks/kustomization.yaml new file mode 100644 index 00000000..9f6b29fd --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./multiple-networks.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks/multiple-networks.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks/multiple-networks.yaml new file mode 100644 index 00000000..ba6c32f9 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-multiple-networks/multiple-networks.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro + networks: + - name: ${CLOUDSTACK_NETWORK_NAME} + - name: ${CLOUDSTACK_NEW_NETWORK_NAME} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + networks: + - name: ${CLOUDSTACK_NETWORK_NAME} + - name: ${CLOUDSTACK_NEW_NETWORK_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering/cluster-with-network-with-custom-offering.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering/cluster-with-network-with-custom-offering.yaml new file mode 100644 index 00000000..91110b94 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering/cluster-with-network-with-custom-offering.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME_WITH_CUSTOM_OFFERING} + gateway: ${CLOUDSTACK_GATEWAY} + netmask: ${CLOUDSTACK_NETMASK} + offering: ${CLOUDSTACK_NETWORK_CUSTOM_OFFERING_EGRESS_POLICY_TRUE_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering/kustomization.yaml new file mode 100644 index 00000000..c84bde4b --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-network-with-custom-offering/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cluster-with-network-with-custom-offering.yaml \ No newline at end of file diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/cluster-with-kcp.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/cluster-with-kcp.yaml new file mode 100644 index 00000000..91a5a7f4 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/cluster-with-kcp.yaml @@ -0,0 +1,9 @@ +# KubeadmControlPlane referenced by the Cluster object with +# - the label kcp-adoption.step2, because it should be created in the second step of the kcp-adoption test. +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: "${CLUSTER_NAME}-control-plane" +spec: + machineTemplate: + nodeDrainTimeout: ${NODE_DRAIN_TIMEOUT} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/kustomization.yaml new file mode 100644 index 00000000..51025359 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/kustomization.yaml @@ -0,0 +1,7 @@ +bases: +- ../bases/md.yaml +- ../bases/cluster-with-kcp.yaml + +patchesStrategicMerge: +- ./md.yaml +- ./cluster-with-kcp.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/md.yaml new file mode 100644 index 00000000..bc457776 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-node-drain/md.yaml @@ -0,0 +1,8 @@ +apiVersion: cluster.x-k8s.io/v1beta1 +kind: MachineDeployment +metadata: + name: "${CLUSTER_NAME}-md-0" +spec: + template: + spec: + nodeDrainTimeout: "${NODE_DRAIN_TIMEOUT}" diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml new file mode 100644 index 00000000..a64b61fa --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/cloudstack-cluster.yaml @@ -0,0 +1,23 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_PROJECT_NAME}-${CLOUDSTACK_VPC_NETWORK_NAME} + gateway: ${CLOUDSTACK_GATEWAY} + netmask: ${CLOUDSTACK_NETMASK} + vpc: + name: ${CLOUDSTACK_PROJECT_NAME}-${CLOUDSTACK_VPC_NAME} + cidr: ${CLOUDSTACK_VPC_CIDR} + project: ${CLOUDSTACK_PROJECT_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml new file mode 100644 index 00000000..9953367a --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/kustomization.yaml @@ -0,0 +1,7 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml new file mode 100644 index 00000000..87a44f35 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-project/md.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup/cloudstack-cluster.yaml new file mode 100644 index 00000000..c9d49d66 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup/cloudstack-cluster.yaml @@ -0,0 +1,17 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + name: ${CLOUDSTACK_FD1_NAME} + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NEW_NETWORK_NAME} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup/kustomization.yaml new file mode 100644 index 00000000..7a6d2984 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster/cloudstack-cluster.yaml new file mode 100644 index 00000000..cdc89f6d --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster/cloudstack-cluster.yaml @@ -0,0 +1,19 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + account: ${CLOUDSTACK_ACCOUNT_NAME} + domain: ${CLOUDSTACK_DOMAIN_NAME} + controlPlaneEndpoint: + host: ${CLUSTER_ENDPOINT_IP_2} + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster/kustomization.yaml new file mode 100644 index 00000000..2b51575e --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: + - ./cloudstack-cluster.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip/cluster-with-shared-network-and-kubevip.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip/cluster-with-shared-network-and-kubevip.yaml new file mode 100644 index 00000000..e1c12a73 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip/cluster-with-shared-network-and-kubevip.yaml @@ -0,0 +1,129 @@ +--- +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} +spec: + clusterNetwork: + pods: + cidrBlocks: + - 192.168.0.0/16 + serviceDomain: "cluster.local" + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackCluster + name: ${CLUSTER_NAME} + controlPlaneRef: + kind: KubeadmControlPlane + apiVersion: controlplane.cluster.x-k8s.io/v1beta1 + name: ${CLUSTER_NAME}-control-plane +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + controlPlaneEndpoint: + host: ${CLUSTER_ENDPOINT_IP} + port: 6443 + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_SHARED_NETWORK_NAME} +--- +kind: KubeadmControlPlane +apiVersion: controlplane.cluster.x-k8s.io/v1beta1 +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + clusterConfiguration: + imageRepository: "" + joinConfiguration: + nodeRegistration: + name: '{{ local_hostname }}' + kubeletExtraArgs: + provider-id: "cloudstack:///'{{ ds.meta_data.instance_id }}'" + ignorePreflightErrors: + - DirAvailable--etc-kubernetes-manifests + preKubeadmCommands: + - swapoff -a + files: + - content: | + apiVersion: v1 + kind: Pod + metadata: + creationTimestamp: null + name: kube-vip + namespace: kube-system + spec: + containers: + - args: + - start + env: + - name: vip_arp + value: "true" + - name: vip_leaderelection + value: "true" + - name: vip_address + value: ${CLUSTER_ENDPOINT_IP} + - name: vip_interface + value: ens3 + - name: vip_leaseduration + value: "15" + - name: vip_renewdeadline + value: "10" + - name: vip_retryperiod + value: "2" + image: public.ecr.aws/i3w0y7q3/plunder-app/kube-vip:v0.3.7-eks-a-v0.0.0-dev-build.0 + imagePullPolicy: IfNotPresent + name: kube-vip + resources: {} + securityContext: + capabilities: + add: + - NET_ADMIN + - SYS_TIME + volumeMounts: + - mountPath: /etc/kubernetes/admin.conf + name: kubeconfig + hostNetwork: true + volumes: + - hostPath: + path: /etc/kubernetes/admin.conf + type: FileOrCreate + name: kubeconfig + status: {} + owner: root:root + path: /etc/kubernetes/manifests/kube-vip.yaml + machineTemplate: + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 + kind: CloudStackMachineTemplate + name: ${CLUSTER_NAME}-control-plane + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} +--- diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip/kustomization.yaml new file mode 100644 index 00000000..2b1c550e --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip/kustomization.yaml @@ -0,0 +1,3 @@ +bases: + - ./cluster-with-shared-network-and-kubevip.yaml + - ../bases/md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/cloudstack-cluster.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/cloudstack-cluster.yaml new file mode 100644 index 00000000..fb108cc9 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/cloudstack-cluster.yaml @@ -0,0 +1,19 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name : ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_NETWORK_NAME} + account: ${CLOUDSTACK_SUBDOMAIN_ACCOUNT_NAME} + domain: ${CLOUDSTACK_SUBDOMAIN_PATH} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/kustomization.yaml new file mode 100644 index 00000000..9953367a --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/kustomization.yaml @@ -0,0 +1,7 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cloudstack-cluster.yaml +- ./md.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/md.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/md.yaml new file mode 100644 index 00000000..a1ee84b4 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-subdomain/md.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 +spec: + template: + spec: + offering: + name: ${CLOUDSTACK_WORKER_MACHINE_OFFERING} + template: + name: ${CLOUDSTACK_TEMPLATE_NAME} + sshKey: ${CLOUDSTACK_SSH_KEY_NAME} + affinity: pro diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network/cluster-with-vpc-network.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network/cluster-with-vpc-network.yaml new file mode 100644 index 00000000..b2e72734 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network/cluster-with-vpc-network.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta3 +kind: CloudStackCluster +metadata: + name: ${CLUSTER_NAME} +spec: + failureDomains: + - name: ${CLOUDSTACK_FD1_NAME} + acsEndpoint: + name: ${CLOUDSTACK_FD1_SECRET_NAME} + namespace: default + zone: + name: ${CLOUDSTACK_ZONE_NAME} + network: + name: ${CLOUDSTACK_VPC_NETWORK_NAME} + gateway: ${CLOUDSTACK_GATEWAY} + netmask: ${CLOUDSTACK_NETMASK} + vpc: + name: ${CLOUDSTACK_VPC_NAME} + cidr: ${CLOUDSTACK_VPC_CIDR} + controlPlaneEndpoint: + host: "" + port: 6443 diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network/kustomization.yaml new file mode 100644 index 00000000..288f5fb1 --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network/kustomization.yaml @@ -0,0 +1,6 @@ +bases: + - ../bases/cluster-with-kcp.yaml + - ../bases/md.yaml + +patchesStrategicMerge: +- ./cluster-with-vpc-network.yaml diff --git a/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template/kustomization.yaml b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template/kustomization.yaml new file mode 100644 index 00000000..446899dc --- /dev/null +++ b/test/e2e/data/infrastructure-cloudstack/v1beta3/cluster-template/kustomization.yaml @@ -0,0 +1,4 @@ +bases: +- ../bases/cluster-with-kcp.yaml +- ../bases/md.yaml + diff --git a/test/e2e/data/shared/v1beta1/metadata.yaml b/test/e2e/data/shared/v1beta1/metadata.yaml index aeccb56b..4fbe4824 100644 --- a/test/e2e/data/shared/v1beta1/metadata.yaml +++ b/test/e2e/data/shared/v1beta1/metadata.yaml @@ -1,6 +1,33 @@ apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3 kind: Metadata releaseSeries: + - major: 1 + minor: 9 + contract: v1beta1 + - major: 1 + minor: 8 + contract: v1beta1 + - major: 1 + minor: 7 + contract: v1beta1 + - major: 1 + minor: 6 + contract: v1beta1 + - major: 1 + minor: 5 + contract: v1beta1 + - major: 1 + minor: 4 + contract: v1beta1 + - major: 1 + minor: 3 + contract: v1beta1 - major: 1 minor: 2 contract: v1beta1 + - major: 1 + minor: 1 + contract: v1beta1 + - major: 1 + minor: 0 + contract: v1beta1 diff --git a/test/e2e/deploy_app.go b/test/e2e/deploy_app.go index a22e53a8..81e5190a 100644 --- a/test/e2e/deploy_app.go +++ b/test/e2e/deploy_app.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/deploy_app_test.go b/test/e2e/deploy_app_test.go index e89c5197..62cf1945 100644 --- a/test/e2e/deploy_app_test.go +++ b/test/e2e/deploy_app_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing app deployment to the workload cluster [TC1][PR-Blocking]", func() { diff --git a/test/e2e/deploy_app_toxi.go b/test/e2e/deploy_app_toxi.go index 766d26b4..9b3af63d 100644 --- a/test/e2e/deploy_app_toxi.go +++ b/test/e2e/deploy_app_toxi.go @@ -19,13 +19,14 @@ package e2e import ( "context" "fmt" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" "os" "path/filepath" "runtime" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" "sigs.k8s.io/cluster-api-provider-cloudstack-staging/test/e2e/toxiproxy" "sigs.k8s.io/cluster-api/test/framework/clusterctl" "sigs.k8s.io/cluster-api/util" diff --git a/test/e2e/deploy_app_toxi_test.go b/test/e2e/deploy_app_toxi_test.go index f018a4fa..335c6423 100644 --- a/test/e2e/deploy_app_toxi_test.go +++ b/test/e2e/deploy_app_toxi_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing app deployment to the workload cluster with slow network [ToxiProxy]", func() { diff --git a/test/e2e/disk_offering.go b/test/e2e/disk_offering.go index c00ba123..c9dd859b 100644 --- a/test/e2e/disk_offering.go +++ b/test/e2e/disk_offering.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/disk_offering_test.go b/test/e2e/disk_offering_test.go index 546bebbd..b2ee38e2 100644 --- a/test/e2e/disk_offering_test.go +++ b/test/e2e/disk_offering_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing with disk offering", func() { diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 00799aff..760c9004 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -25,15 +25,17 @@ import ( "os" "path/filepath" go_runtime "runtime" - "sigs.k8s.io/cluster-api-provider-cloudstack-staging/test/e2e/toxiproxy" "strings" "testing" "time" - . "github.com/onsi/ginkgo" + "sigs.k8s.io/cluster-api-provider-cloudstack-staging/test/e2e/toxiproxy" + + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -94,10 +96,10 @@ func TestE2E(t *testing.T) { artifactFolder = prowArtifactFolder } + ctrl.SetLogger(klog.Background()) RegisterFailHandler(Fail) - junitReporter := framework.CreateJUnitReporterForProw(artifactFolder) - RunSpecsWithDefaultAndCustomReporters(t, "capi-e2e", []Reporter{junitReporter}) + RunSpecs(t, "capi-e2e") } // Using a SynchronizedBeforeSuite for controlling how to create resources shared across ParallelNodes (~ginkgo threads). diff --git a/test/e2e/go.mod b/test/e2e/go.mod index c6bc0384..46af34b7 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -1,127 +1,150 @@ module sigs.k8s.io/cluster-api-provider-cloudstack-staging/test/e2e -go 1.19 +go 1.23 + +toolchain go1.23.2 require ( github.com/Shopify/toxiproxy/v2 v2.5.0 - github.com/apache/cloudstack-go/v2 v2.13.0 + github.com/apache/cloudstack-go/v2 v2.17.0 github.com/blang/semver v3.5.1+incompatible - github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.20.1 + github.com/onsi/ginkgo/v2 v2.22.2 + github.com/onsi/gomega v1.36.2 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.25.3 - k8s.io/apimachinery v0.25.3 - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed - sigs.k8s.io/cluster-api v1.2.12 - sigs.k8s.io/cluster-api/test v1.2.12 - sigs.k8s.io/controller-runtime v0.12.3 + k8s.io/api v0.31.3 + k8s.io/apimachinery v0.31.3 + k8s.io/klog/v2 v2.130.1 + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + sigs.k8s.io/cluster-api v1.9.6 + sigs.k8s.io/cluster-api/test v1.9.6 + sigs.k8s.io/controller-runtime v0.19.6 ) require ( - github.com/BurntSushi/toml v1.0.0 // 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/semver/v3 v3.2.0 // indirect - github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/alessio/shellescape v1.4.1 // indirect - github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/alessio/shellescape v1.4.2 // 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/cespare/xxhash/v2 v2.1.2 // indirect - github.com/coredns/caddy v1.1.0 // indirect - github.com/coredns/corefile-migration v1.0.20 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker v23.0.5+incompatible // indirect - github.com/docker/go-connections v0.4.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.3.7 // 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 v27.3.1+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 - github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/gobuffalo/flect v0.2.5 // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gobuffalo/flect v1.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/cel-go v0.12.4 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.8 // indirect - github.com/google/go-github/v45 v45.2.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.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/uuid v1.2.0 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // 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.20.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect + github.com/imdario/mergo v0.3.13 // 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 - github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/docker-image-spec v1.3.1 // 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 - github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.12.0 // indirect + github.com/spf13/viper v1.19.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect - github.com/subosito/gotenv v1.3.0 // indirect - github.com/valyala/fastjson v1.6.3 // indirect - golang.org/x/crypto v0.3.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect - google.golang.org/protobuf v1.28.1 // 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/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/mock v0.5.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.28.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.1 // indirect + google.golang.org/protobuf v1.36.1 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/apiserver v0.25.0 // indirect - k8s.io/client-go v0.25.0 // indirect - k8s.io/cluster-bootstrap v0.24.0 // indirect - k8s.io/component-base v0.25.0 // indirect - k8s.io/klog/v2 v2.70.1 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/kind v0.14.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/apiextensions-apiserver v0.31.3 // indirect + k8s.io/apiserver v0.31.3 // indirect + k8s.io/client-go v0.31.3 // indirect + k8s.io/cluster-bootstrap v0.31.3 // indirect + k8s.io/component-base v0.31.3 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kind v0.25.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) - -replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.2.12 diff --git a/test/e2e/go.sum b/test/e2e/go.sum index 67f0ebb3..fc0015ee 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -1,1006 +1,406 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= -github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= 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.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +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/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= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc= github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= -github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed h1:ue9pVfIcP+QMEjfgo/Ez4ZjNZfonGgR6NgjMaJMu1Cg= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/apache/cloudstack-go/v2 v2.13.0 h1:t0uj7QxQpnzD/LSTP6a4w2NTuZXisxIM/mIDNkF44lc= -github.com/apache/cloudstack-go/v2 v2.13.0/go.mod h1:aosD8Svfu5nhH5Sp4zcsVV1hT5UGt3mTgRXM8YqTKe0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +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/apache/cloudstack-go/v2 v2.17.0 h1:jueNPcm8g7ReY34xoTVubMyRcVrpuPO/tZbzN68DCLA= +github.com/apache/cloudstack-go/v2 v2.17.0/go.mod h1:E11d4GJG/ZG1R+i1/q7p5/D5S+XVqCluFVd3sjcp8U0= 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 v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0= -github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.20 h1:MdOkT6F3ehju/n9tgxlGct8XAajOX2vN+wG7To4BWSI= -github.com/coredns/corefile-migration v1.0.20/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +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.25 h1:/XexFhM8FFlFLTS/zKNEWgIZ8Gl5GaWrHsMarGj/PRQ= +github.com/coredns/corefile-migration v1.0.25/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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v23.0.5+incompatible h1:DaxtlTJjFSnLOXVNUBU1+6kXGz2lpDoEAH6QoxaSg8k= -github.com/docker/docker v23.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= +github.com/docker/docker v27.3.1+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= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 h1:7QPwrLT79GlD5sizHf27aoY2RTvw62mO6x7mxkScNk0= github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46/go.mod h1:esf2rsHFNlZlxsqsZDojNBcnNs5REqIvRrWRHqX0vEU= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/flect v0.2.5 h1:H6vvsv2an0lalEaCDRThvtBfmg44W/QHXBCYUXf/6S4= -github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +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/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= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/cel-go v0.12.4 h1:YINKfuHZ8n72tPOqSPZBwGiDpew2CJS48mdM5W8LZQU= -github.com/google/cel-go v0.12.4/go.mod h1:Av7CU6r6X3YmcHR9GXqVDaEJYfEtSxl6wvIjUQTriCw= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI= -github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= +github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +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/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 v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +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/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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= 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= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +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= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= -github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.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/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.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= -go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w= +go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4= +go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw= +go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w= +go.etcd.io/etcd/client/v3 v3.5.17 h1:o48sINNeWz5+pjy/Z0+HKpj/xSnBkuVhVvXkjEXbqZY= +go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +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.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +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.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb h1:8tDJ3aechhddbdPAxpycgXHJRMLpk/Ab+aa4OgdN5/g= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.1 h1:toSN4j5/Xju+HVovfaY5g1YZVuJeHzQZhP8eJ0L0f1I= +google.golang.org/grpc v1.65.1/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU= -k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= -k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= -k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4= -k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= -k8s.io/cluster-bootstrap v0.24.0 h1:MTs2x3Vfcl/PWvB5bfX7gzTFRyi4ZSbNSQgGJTCb6Sw= -k8s.io/cluster-bootstrap v0.24.0/go.mod h1:xw+IfoaUweMCAoi+VYhmqkcjii2G7gNg59dmGn7hi0g= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/cluster-api v1.2.12 h1:/1iFgWUR3Z+KZU9B4PJegmlQQo6uzQXBZNgOk63SLu4= -sigs.k8s.io/cluster-api v1.2.12/go.mod h1:9+fLUuyRsKediKJYrbsyFj6Bmk59oopXpzj3g6/REM8= -sigs.k8s.io/cluster-api/test v1.2.12 h1:ylD8lUW6aZpmdkaAvRs6vBpN1zjiQuBr/kl6pya5FbY= -sigs.k8s.io/cluster-api/test v1.2.12/go.mod h1:7rOSOBPkq5yqii/d/uf4EN/Ve4MWyYHGfQaCTTmlKsQ= -sigs.k8s.io/controller-runtime v0.12.3 h1:FCM8xeY/FI8hoAfh/V4XbbYMY20gElh9yh+A98usMio= -sigs.k8s.io/controller-runtime v0.12.3/go.mod h1:qKsk4WE6zW2Hfj0G4v10EnNB2jMG1C+NTb8h+DwCoU0= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kind v0.14.0 h1:cNmI3jGBvp7UegEGbC5we8plDtCUmaNRL+bod7JoSCE= -sigs.k8s.io/kind v0.14.0/go.mod h1:UrFRPHG+2a5j0Q7qiR4gtJ4rEyn8TuMQwuOPf+m4oHg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= +k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= +k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= +k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= +k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= +k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= +k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= +k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/cluster-bootstrap v0.31.3 h1:O1Yxk1bLaxZvmQCXLaJjj5iJD+lVMfJdRUuKgbUHPlA= +k8s.io/cluster-bootstrap v0.31.3/go.mod h1:TI6TCsQQB4FfcryWgNO3SLXSKWBqHjx4DfyqSFwixj8= +k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= +k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +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-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/cluster-api v1.9.6 h1:2jZ434qC0bzrPQzmRDm4/b0PZWVMnOocoCjsAonQN5Q= +sigs.k8s.io/cluster-api v1.9.6/go.mod h1:DyqyZ4jRvKGKewDRn1Q4OCHaVjsdTogymbO6mrgHEDI= +sigs.k8s.io/cluster-api/test v1.9.6 h1:bnplAOqKRBFWP2q2L8kbY2DZTLBY8JTpucnGSLgS/Kw= +sigs.k8s.io/cluster-api/test v1.9.6/go.mod h1:YL2wANe8TFWFBka9CDkxjPj7KALqUtK+PtKa4ChNIok= +sigs.k8s.io/controller-runtime v0.19.6 h1:fuq53qTLQ7aJTA7aNsklNnu7eQtSFqJUomOyM+phPLk= +sigs.k8s.io/controller-runtime v0.19.6/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kind v0.25.0 h1:ugUvgesHKKA0yKmD6QtYTiEev+kPUpGxdTPbMGf8VTU= +sigs.k8s.io/kind v0.25.0/go.mod h1:t7ueEpzPYJvHA8aeLtI52rtFftNgUYUaCwvxjk7phfw= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/test/e2e/horizontal_scale.go b/test/e2e/horizontal_scale.go index f2e1ad86..6a1f852f 100644 --- a/test/e2e/horizontal_scale.go +++ b/test/e2e/horizontal_scale.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/horizontal_scale_test.go b/test/e2e/horizontal_scale_test.go index 7370cf2d..5e4f7029 100644 --- a/test/e2e/horizontal_scale_test.go +++ b/test/e2e/horizontal_scale_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing horizontal scale out/in [TC17][TC18][TC20][TC21]", func() { diff --git a/test/e2e/invalid_resource.go b/test/e2e/invalid_resource.go index d1fba410..720611aa 100644 --- a/test/e2e/invalid_resource.go +++ b/test/e2e/invalid_resource.go @@ -22,9 +22,10 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strings" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -40,12 +41,14 @@ import ( ) var ( - specName = "invalid-resource" - input CommonSpecInput - namespace *corev1.Namespace - cancelWatches context.CancelFunc - clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult - instanceStateError = "\"instanceState\":\"Error\"" + specName = "invalid-resource" + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + MachineInErrorMessage = "CloudStackMachine VM in error state. Deleting associated Machine" + // Pattern that matches start of Kubernetes log entries + logEntryPattern = `[IWEF]\d{4} \d{2}:\d{2}:\d{2}\.\d{6}` ) // InvalidResourceSpec implements a test that verifies that creating a new cluster fails when the specified resource does not exist @@ -90,7 +93,7 @@ func InvalidResourceSpec(ctx context.Context, inputGetter func() CommonSpecInput }) It("Should fail due to the compute resources are not sufficient for the specified offering [TC8]", func() { - testInvalidResource(ctx, input, "insufficient-compute-resources", instanceStateError) + testInvalidResource(ctx, input, "insufficient-compute-resources", MachineInErrorMessage) }) It("Should fail due to the specified disk offer is not customized but the disk size is specified", func() { @@ -135,7 +138,7 @@ func InvalidResourceSpec(ctx context.Context, inputGetter func() CommonSpecInput It("Should fail to upgrade worker machine due to insufficient compute resources", func() { By("Making sure the expected error didn't occur yet") - Expect(errorExistsInLog(logFolder, instanceStateError)).To(BeFalse()) + Expect(errorExistsInLog(logFolder, MachineInErrorMessage)).To(BeFalse()) By("Increasing the machine deployment instance size") deployment := clusterResources.MachineDeployments[0] @@ -144,12 +147,12 @@ func InvalidResourceSpec(ctx context.Context, inputGetter func() CommonSpecInput upgradeMachineDeploymentInfrastructureRef(ctx, deployment) By("Checking for the expected error") - waitForErrorInLog(logFolder, instanceStateError) + waitForErrorInLog(logFolder, MachineInErrorMessage) }) It("Should fail to upgrade control plane machine due to insufficient compute resources", func() { By("Making sure the expected error didn't occur yet") - Expect(errorExistsInLog(logFolder, instanceStateError)).To(BeFalse()) + Expect(errorExistsInLog(logFolder, MachineInErrorMessage)).To(BeFalse()) By("Increasing the machine deployment instance size") cp := clusterResources.ControlPlane @@ -158,7 +161,7 @@ func InvalidResourceSpec(ctx context.Context, inputGetter func() CommonSpecInput upgradeControlPlaneInfrastructureRef(ctx, cp) By("Checking for the expected error") - waitForErrorInLog(logFolder, instanceStateError) + waitForErrorInLog(logFolder, MachineInErrorMessage) }) }) @@ -188,7 +191,7 @@ func testInvalidResource(ctx context.Context, input CommonSpecInput, flavor stri }) By("Applying...") - Expect(input.BootstrapClusterProxy.Apply(ctx, workloadClusterTemplate)).ShouldNot(HaveOccurred()) + Expect(input.BootstrapClusterProxy.CreateOrUpdate(ctx, workloadClusterTemplate)).ShouldNot(HaveOccurred()) clusterResources.Cluster = framework.GetClusterByName(ctx, framework.GetClusterByNameInput{ Getter: input.BootstrapClusterProxy.GetClient(), @@ -216,7 +219,7 @@ func generateClusterName() string { // it is generated randomly per test case. func errorExistsInLog(logFolder string, expectedError string) (bool, error) { expectedErrorFound := errors.New("expected error found") - controllerLogPath := filepath.Join(logFolder, "controllers", "capc-controller-manager") + controllerLogPath := filepath.Join(logFolder, "logs/capc-system/capc-controller-manager") err := filepath.Walk(controllerLogPath, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -224,17 +227,21 @@ func errorExistsInLog(logFolder string, expectedError string) (bool, error) { } if strings.Contains(path, "manager.log") { - log, _ := os.ReadFile(path) - logLines := strings.Split(string(log), "\n") - for _, line := range logLines { - if strings.Contains(line, expectedError) && - strings.Contains(line, clusterResources.Cluster.Namespace) { + logContent, err := os.ReadFile(path) + if err != nil { + return err + } + + // Split log content by the log entry pattern + entries := regexp.MustCompile(logEntryPattern).Split(string(logContent), -1) + for _, entry := range entries { + if strings.Contains(entry, expectedError) && + strings.Contains(entry, clusterResources.Cluster.Namespace) { Byf("Found %q error", expectedError) return expectedErrorFound } } } - return nil }) diff --git a/test/e2e/invalid_resource_test.go b/test/e2e/invalid_resource_test.go index f7407b3e..a2157ee5 100644 --- a/test/e2e/invalid_resource_test.go +++ b/test/e2e/invalid_resource_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When the specified resource does not exist", func() { diff --git a/test/e2e/k8s_conformance.go b/test/e2e/k8s_conformance.go index 22bab9ff..271b8651 100644 --- a/test/e2e/k8s_conformance.go +++ b/test/e2e/k8s_conformance.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/k8s_conformance_test.go b/test/e2e/k8s_conformance_test.go index 0d5a9f46..8ff66baa 100644 --- a/test/e2e/k8s_conformance_test.go +++ b/test/e2e/k8s_conformance_test.go @@ -20,7 +20,7 @@ limitations under the License. package e2e import ( - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing K8S conformance [Conformance]", func() { diff --git a/test/e2e/kubeconfig_helper/kubeconfig_test.go b/test/e2e/kubeconfig_helper/kubeconfig_test.go index 6aa4150c..d68b927c 100644 --- a/test/e2e/kubeconfig_helper/kubeconfig_test.go +++ b/test/e2e/kubeconfig_helper/kubeconfig_test.go @@ -17,9 +17,10 @@ limitations under the License. package kubeconfig_helper_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "io/ioutil" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "sigs.k8s.io/cluster-api-provider-cloudstack-staging/test/e2e/kubeconfig_helper" ) diff --git a/test/e2e/kubeconfig_helper/suite_test.go b/test/e2e/kubeconfig_helper/suite_test.go index 42ce0ead..3627898f 100644 --- a/test/e2e/kubeconfig_helper/suite_test.go +++ b/test/e2e/kubeconfig_helper/suite_test.go @@ -3,7 +3,7 @@ package kubeconfig_helper_test import ( "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/test/e2e/kubernetes_cks_cluster.go b/test/e2e/kubernetes_cks_cluster.go new file mode 100644 index 00000000..87d43dd0 --- /dev/null +++ b/test/e2e/kubernetes_cks_cluster.go @@ -0,0 +1,136 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/blang/semver" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/cluster-api/util" +) + +// CksK8SSpec implements a spec that creates a cluster and checks whether an entry is created in ACS. +func CksK8SSpec(ctx context.Context, inputGetter func() CommonSpecInput) { + var ( + specName = "k8s-cks" + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + ) + + BeforeEach(func() { + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + version, err := GetACSVersion(csClient) + + if err != nil || version == "" { + Skip("Failed to get CloudStack's version") + } + + v, err := semver.ParseTolerant(strings.Join(strings.Split(version, ".")[0:3], ".")) + + if err != nil { + Skip("Failed to parse CloudStack version " + version) + } + + expectedRange, _ := semver.ParseRange(">=4.19.0") + + if !expectedRange(v) { + Skip("Cloudstack version " + version + " is less than 4.19.") + } + + Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) + Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) + Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) + + Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion)) + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder) + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + }) + + It("Should create a workload cluster", func() { + By("Creating a workload cluster") + + clusterName := fmt.Sprintf("%s-%s", specName, util.RandomString(6)) + + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.BootstrapClusterProxy, + CNIManifestPath: input.E2EConfig.GetVariable(CNIPath), + ConfigCluster: clusterctl.ConfigClusterInput{ + LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()), + ClusterctlConfigPath: input.ClusterctlConfigPath, + KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(), + InfrastructureProvider: clusterctl.DefaultInfrastructureProvider, + Flavor: specName, + Namespace: namespace.Name, + ClusterName: clusterName, + KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: pointer.Int64Ptr(1), + WorkerMachineCount: pointer.Int64Ptr(1), + }, + WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + By("checking cks resource is created on ACS") + // Get details from ACS + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + lkcp := csClient.Kubernetes.NewListKubernetesClustersParams() + lkcp.SetListall(true) + + clusters, err := csClient.Kubernetes.ListKubernetesClusters(lkcp) + + if err != nil { + Fail("Failed to get Kubernetes clusters from ACS") + } + + var cluster *cloudstack.KubernetesCluster + + for _, d := range clusters.KubernetesClusters { + if strings.HasPrefix(d.Name, fmt.Sprintf("%s - %s", clusterName, clusterName)) { + cluster = d + } + } + + Expect(cluster).ShouldNot(BeNil(), "Couldn't find the external managed kubernetes in ACS") + Expect(len(cluster.Virtualmachines)).Should(Equal(2), "Expected 2 VMs in the cluster, found %d", len(cluster.Virtualmachines)) + By("PASSED!") + }) + + AfterEach(func() { + // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. + dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup) + }) +} diff --git a/test/e2e/kubernetes_cks_cluster_test.go b/test/e2e/kubernetes_cks_cluster_test.go new file mode 100644 index 00000000..4be4d8c7 --- /dev/null +++ b/test/e2e/kubernetes_cks_cluster_test.go @@ -0,0 +1,36 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("When testing creation of CKS cluster in ACS", func() { + CksK8SSpec(ctx, func() CommonSpecInput { + return CommonSpecInput{ + E2EConfig: e2eConfig, + ClusterctlConfigPath: clusterctlConfigPath, + BootstrapClusterProxy: bootstrapClusterProxy, + ArtifactFolder: artifactFolder, + SkipCleanup: skipCleanup, + } + }) +}) diff --git a/test/e2e/kubernetes_version_upgrade.go b/test/e2e/kubernetes_version_upgrade.go index 3914f3ed..b766334a 100644 --- a/test/e2e/kubernetes_version_upgrade.go +++ b/test/e2e/kubernetes_version_upgrade.go @@ -19,7 +19,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/kubernetes_version_upgrade_test.go b/test/e2e/kubernetes_version_upgrade_test.go index 12c96d01..4d683a04 100644 --- a/test/e2e/kubernetes_version_upgrade_test.go +++ b/test/e2e/kubernetes_version_upgrade_test.go @@ -17,7 +17,7 @@ limitations under the License. package e2e import ( - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing Kubernetes version upgrades", func() { diff --git a/test/e2e/machine_remediation.go b/test/e2e/machine_remediation.go index 55da4e4f..92fecddc 100644 --- a/test/e2e/machine_remediation.go +++ b/test/e2e/machine_remediation.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/machine_remediation_test.go b/test/e2e/machine_remediation_test.go index ccde718e..1c897fb8 100644 --- a/test/e2e/machine_remediation_test.go +++ b/test/e2e/machine_remediation_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing machine remediation", func() { diff --git a/test/e2e/md_rollout.go b/test/e2e/md_rollout.go index be5ee98a..b6c1593c 100644 --- a/test/e2e/md_rollout.go +++ b/test/e2e/md_rollout.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/md_rollout_test.go b/test/e2e/md_rollout_test.go index 707f50f4..0d64e5e8 100644 --- a/test/e2e/md_rollout_test.go +++ b/test/e2e/md_rollout_test.go @@ -20,7 +20,7 @@ limitations under the License. package e2e import ( - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing MachineDeployment rolling upgrades", func() { diff --git a/test/e2e/multiple_networks.go b/test/e2e/multiple_networks.go new file mode 100644 index 00000000..1ab88899 --- /dev/null +++ b/test/e2e/multiple_networks.go @@ -0,0 +1,100 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/cluster-api/util" +) + +// MultipleNetworksSpec implements a spec that creates a cluster with nodes having multiple NICs. +func MultipleNetworksSpec(ctx context.Context, inputGetter func() CommonSpecInput) { + var ( + specName = "multiple-networks" + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + ) + + BeforeEach(func() { + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + + Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) + Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) + Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) + + Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion)) + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder) + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + }) + + It("Should create a workload cluster", func() { + By("Creating a workload cluster") + + clusterName := fmt.Sprintf("%s-%s", specName, util.RandomString(6)) + + // Get details from ACS and ensure secondary network exists + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + _, err := EnsureSecondaryNetworkExists(csClient, input) + Expect(err).ToNot(HaveOccurred(), "Failed to ensure secondary network exists") + + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.BootstrapClusterProxy, + CNIManifestPath: input.E2EConfig.GetVariable(CNIPath), + ConfigCluster: clusterctl.ConfigClusterInput{ + LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()), + ClusterctlConfigPath: input.ClusterctlConfigPath, + KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(), + InfrastructureProvider: clusterctl.DefaultInfrastructureProvider, + Flavor: specName, + Namespace: namespace.Name, + ClusterName: clusterName, + KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: pointer.Int64Ptr(1), + WorkerMachineCount: pointer.Int64Ptr(1), + }, + WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + By("Verifying that each VM has two NICs") + CheckIfNodesHaveTwoNICs(csClient, clusterName, input) + + By("PASSED!") + }) + + AfterEach(func() { + // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. + dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup) + }) +} diff --git a/test/e2e/multiple_networks_test.go b/test/e2e/multiple_networks_test.go new file mode 100644 index 00000000..f2342595 --- /dev/null +++ b/test/e2e/multiple_networks_test.go @@ -0,0 +1,40 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("Test with multiple networks for nodes", func() { + + MultipleNetworksSpec(context.TODO(), func() CommonSpecInput { + return CommonSpecInput{ + E2EConfig: e2eConfig, + ClusterctlConfigPath: clusterctlConfigPath, + BootstrapClusterProxy: bootstrapClusterProxy, + ArtifactFolder: artifactFolder, + SkipCleanup: skipCleanup, + } + }) + +}) diff --git a/test/e2e/network_interruption_toxi.go b/test/e2e/network_interruption_toxi.go index 29f690f5..5041360e 100644 --- a/test/e2e/network_interruption_toxi.go +++ b/test/e2e/network_interruption_toxi.go @@ -24,7 +24,7 @@ import ( "runtime" "time" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" @@ -127,7 +127,7 @@ func NetworkInterruptionToxiSpec(ctx context.Context, inputGetter func() CommonS } func networkInterruptor(toxiProxyContext *toxiproxy.Context, shutdownChannel chan bool) { - for { + for { // Wait for ApplyClusterTemplateAndWait() to make some progress helpers.InterruptibleSleep(15*time.Second, time.Second, shutdownChannel) @@ -135,7 +135,7 @@ func networkInterruptor(toxiProxyContext *toxiproxy.Context, shutdownChannel cha toxiProxyContext.Disable() // Leave the network disabled for some period of time - helpers.InterruptibleSleep(30*time.Second, time.Second, shutdownChannel) + helpers.InterruptibleSleep(15*time.Second, time.Second, shutdownChannel) // Restore communications to ACS toxiProxyContext.Enable() diff --git a/test/e2e/network_interruption_toxi_test.go b/test/e2e/network_interruption_toxi_test.go index 1aa9d930..675395f0 100644 --- a/test/e2e/network_interruption_toxi_test.go +++ b/test/e2e/network_interruption_toxi_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing app deployment to the workload cluster with network interruption [ToxiProxy]", func() { diff --git a/test/e2e/network_with_custom_offering.go b/test/e2e/network_with_custom_offering.go new file mode 100644 index 00000000..41079458 --- /dev/null +++ b/test/e2e/network_with_custom_offering.go @@ -0,0 +1,107 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/cluster-api/util" +) + +// NetworkWithCustomOfferingSpec implements a test that verifies that an app deployed to the workload cluster works. +func NetworkWithCustomOfferingSpec(ctx context.Context, inputGetter func() CommonSpecInput) { + var ( + specName = "network-with-custom-offering" + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + networkOfferingName string + networkName string + ) + + BeforeEach(func() { + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) + Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) + Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) + + Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion)) + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder) + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + + networkOfferingName = input.E2EConfig.GetVariable(CustomNetworkOfferingWithEgressPolicyName) + networkName = input.E2EConfig.GetVariable(NetworkNameWithCustomOffering) + + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + exists, err := CheckNetworkExists(csClient, networkName) + Expect(err).To(BeNil()) + Expect(exists).To(BeFalse()) + }) + + It("Should create a new network with a custom offering", func() { + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.BootstrapClusterProxy, + CNIManifestPath: input.E2EConfig.GetVariable(CNIPath), + ConfigCluster: clusterctl.ConfigClusterInput{ + LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()), + ClusterctlConfigPath: input.ClusterctlConfigPath, + KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(), + InfrastructureProvider: clusterctl.DefaultInfrastructureProvider, + Flavor: specName, + Namespace: namespace.Name, + ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)), + KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: pointer.Int64(1), + WorkerMachineCount: pointer.Int64(1), + }, + WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + + Byf("Checking if network %s exists with offering %s with egress policy %t", networkName, networkOfferingName, true) + network, count, err := csClient.Network.GetNetworkByName(networkName) + Expect(err).To(BeNil()) + Expect(count).To(BeEquivalentTo(1)) + Expect(network.Name).To(BeEquivalentTo(networkName)) + Expect(network.Networkofferingname).To(BeEquivalentTo(networkOfferingName)) + Expect(network.Egressdefaultpolicy).To(BeEquivalentTo(true)) + + By("PASSED!") + }) + + AfterEach(func() { + // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. + dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup) + }) +} diff --git a/test/e2e/network_with_custom_offering_test.go b/test/e2e/network_with_custom_offering_test.go new file mode 100644 index 00000000..69b70e73 --- /dev/null +++ b/test/e2e/network_with_custom_offering_test.go @@ -0,0 +1,39 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("When testing network with custom offering", func() { + NetworkWithCustomOfferingSpec(context.TODO(), func() CommonSpecInput { + return CommonSpecInput{ + E2EConfig: e2eConfig, + ClusterctlConfigPath: clusterctlConfigPath, + BootstrapClusterProxy: bootstrapClusterProxy, + ArtifactFolder: artifactFolder, + SkipCleanup: skipCleanup, + } + }) + +}) diff --git a/test/e2e/node_drain_timeout.go b/test/e2e/node_drain_timeout.go index 083a1a0f..75f88924 100644 --- a/test/e2e/node_drain_timeout.go +++ b/test/e2e/node_drain_timeout.go @@ -23,8 +23,9 @@ import ( "path/filepath" "time" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" @@ -95,11 +96,13 @@ func NodeDrainTimeoutSpec(ctx context.Context, inputGetter func() CommonSpecInpu workloadKubeconfigPath := workloadClusterProxy.GetKubeconfigPath() Byf("workload cluster kubeconfig path %s", workloadKubeconfigPath) - framework.DeployUnevictablePod(ctx, framework.DeployUnevictablePodInput{ + framework.DeployUnevictablePod(ctx, framework.DeployPodAndWaitInput{ WorkloadClusterProxy: workloadClusterProxy, + MachineDeployment: machineDeployments[0], DeploymentName: fmt.Sprintf("%s-%s", "unevictable-pod", util.RandomString(3)), Namespace: namespace.Name + "-unevictable-workload", WaitForDeploymentAvailableInterval: input.E2EConfig.GetIntervals(specName, "wait-deployment-available"), + ModifyDeployment: func(deployment *appsv1.Deployment) {}, }) By("Scale the machinedeployment down to zero. If we didn't have the NodeDrainTimeout duration, the node drain process would block this operator.") @@ -116,12 +119,13 @@ func NodeDrainTimeoutSpec(ctx context.Context, inputGetter func() CommonSpecInpu } By("Deploy deployment with unevictable pods on control plane nodes.") - framework.DeployUnevictablePod(ctx, framework.DeployUnevictablePodInput{ + framework.DeployUnevictablePod(ctx, framework.DeployPodAndWaitInput{ WorkloadClusterProxy: workloadClusterProxy, ControlPlane: controlplane, DeploymentName: fmt.Sprintf("%s-%s", "unevictable-pod", util.RandomString(3)), Namespace: namespace.Name + "-unevictable-workload", WaitForDeploymentAvailableInterval: input.E2EConfig.GetIntervals(specName, "wait-deployment-available"), + ModifyDeployment: func(deployment *appsv1.Deployment) {}, }) By("Scale down the controlplane of the workload cluster and make sure that nodes running workload can be deleted even the draining process is blocked.") diff --git a/test/e2e/node_drain_timeout_test.go b/test/e2e/node_drain_timeout_test.go index e135b424..5d8a3210 100644 --- a/test/e2e/node_drain_timeout_test.go +++ b/test/e2e/node_drain_timeout_test.go @@ -20,7 +20,7 @@ limitations under the License. package e2e import ( - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing node drain timeout", func() { diff --git a/test/e2e/project.go b/test/e2e/project.go new file mode 100644 index 00000000..f5000329 --- /dev/null +++ b/test/e2e/project.go @@ -0,0 +1,113 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/cluster-api/util" +) + +// ProjectSpec implements a test that verifies that an app deployed to the workload cluster works. +func ProjectSpec(ctx context.Context, inputGetter func() CommonSpecInput) { + var ( + specName = "project" + projectName string + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + vpcName string + ) + + BeforeEach(func() { + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) + Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) + Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) + + Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion)) + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder) + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + + projectName = input.E2EConfig.GetVariable("CLOUDSTACK_PROJECT_NAME") + vpcName = fmt.Sprintf("%s-%s", projectName, input.E2EConfig.GetVariable("CLOUDSTACK_VPC_NAME")) + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + project, _, err := csClient.Project.GetProjectByName(projectName) + if (err != nil) || (project == nil) { + Skip("Failed to fetch project") + } + + }) + + It("Should create a cluster in a project", func() { + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.BootstrapClusterProxy, + CNIManifestPath: input.E2EConfig.GetVariable(CNIPath), + ConfigCluster: clusterctl.ConfigClusterInput{ + LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()), + ClusterctlConfigPath: input.ClusterctlConfigPath, + KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(), + InfrastructureProvider: clusterctl.DefaultInfrastructureProvider, + Flavor: specName, + Namespace: namespace.Name, + ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)), + KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: pointer.Int64(1), + WorkerMachineCount: pointer.Int64(2), + }, + WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + // Ensure the cluster was created successfully before proceeding with checks + Expect(clusterResources.Cluster).ToNot(BeNil(), "Cluster was not created successfully") + + By("Checking VPC in project") + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + + // Check VPC + By(fmt.Sprintf("Checking if VPC %s exists in project %s", vpcName, projectName)) + exists, err := CheckVPCExistsInProject(csClient, vpcName, projectName) + Expect(err).To(BeNil(), "Error checking VPC existence") + Expect(exists).To(BeTrue(), fmt.Sprintf("VPC %s does not exist in project %s", vpcName, projectName)) + }) + + AfterEach(func() { + // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. + dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup) + + By("PASSED!") + }) +} diff --git a/test/e2e/project_test.go b/test/e2e/project_test.go new file mode 100644 index 00000000..de9605a6 --- /dev/null +++ b/test/e2e/project_test.go @@ -0,0 +1,39 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("When testing project", func() { + ProjectSpec(context.TODO(), func() CommonSpecInput { + return CommonSpecInput{ + E2EConfig: e2eConfig, + ClusterctlConfigPath: clusterctlConfigPath, + BootstrapClusterProxy: bootstrapClusterProxy, + ArtifactFolder: artifactFolder, + SkipCleanup: skipCleanup, + } + }) + +}) diff --git a/test/e2e/resource_cleanup.go b/test/e2e/resource_cleanup.go index 86d907d0..79eee150 100644 --- a/test/e2e/resource_cleanup.go +++ b/test/e2e/resource_cleanup.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" @@ -88,6 +88,14 @@ func ResourceCleanupSpec(ctx context.Context, inputGetter func() CommonSpecInput Expect(err).To(BeNil()) Expect(exists).To(BeTrue()) + Byf("Checking if network %s exists with offering %s with egress policy %t", networkName, DefaultNetworkOffering, false) + network, count, err := csClient.Network.GetNetworkByName(networkName) + Expect(err).To(BeNil()) + Expect(count).To(BeEquivalentTo(1)) + Expect(network.Name).To(BeEquivalentTo(networkName)) + Expect(network.Networkofferingname).To(BeEquivalentTo(DefaultNetworkOffering)) + Expect(network.Egressdefaultpolicy).To(BeEquivalentTo(false)) + By("PASSED!") }) diff --git a/test/e2e/resource_cleanup_test.go b/test/e2e/resource_cleanup_test.go index 321bd0b4..f80d1967 100644 --- a/test/e2e/resource_cleanup_test.go +++ b/test/e2e/resource_cleanup_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing resource cleanup", func() { diff --git a/test/e2e/shared_network_kubevip.go b/test/e2e/shared_network_kubevip.go index 0cf6ed9b..3e901010 100644 --- a/test/e2e/shared_network_kubevip.go +++ b/test/e2e/shared_network_kubevip.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/shared_network_kubevip_test.go b/test/e2e/shared_network_kubevip_test.go index 05382fa3..1783f903 100644 --- a/test/e2e/shared_network_kubevip_test.go +++ b/test/e2e/shared_network_kubevip_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing multiple CPs in a shared network with kubevip", func() { diff --git a/test/e2e/subdomain.go b/test/e2e/subdomain.go index 2fe1fe18..af7a0267 100644 --- a/test/e2e/subdomain.go +++ b/test/e2e/subdomain.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/subdomain_test.go b/test/e2e/subdomain_test.go index 4f5143ad..eb1315b4 100644 --- a/test/e2e/subdomain_test.go +++ b/test/e2e/subdomain_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("When testing subdomain", func() { diff --git a/test/e2e/toxiproxy/toxiProxy.go b/test/e2e/toxiproxy/toxiProxy.go index 34412a4d..0ddba574 100644 --- a/test/e2e/toxiproxy/toxiProxy.go +++ b/test/e2e/toxiproxy/toxiProxy.go @@ -203,11 +203,13 @@ func SetupForToxiProxyTestingACS(ctx context.Context, clusterName string, cluste Expect(err).To(BeNil()) // Override the test config to use this alternate cloud-config secret + originalSecretName := e2eConfig.GetVariable("CLOUDSTACK_FD1_SECRET_NAME") e2eConfig.Variables["CLOUDSTACK_FD1_SECRET_NAME"] = toxiProxyFdEndpointSecret.Name // Overriding e2e config file into a new temp copy, so as not to inadvertently override the other e2e tests. newConfigFilePath := fmt.Sprintf("/tmp/%v.yaml", toxiProxyName) editConfigFile(newConfigFilePath, configPath, "CLOUDSTACK_FD1_SECRET_NAME", toxiProxyFdEndpointSecret.Name) + e2eConfig.Variables["CLOUDSTACK_FD1_SECRET_NAME"] = originalSecretName // Return a context return &Context{ @@ -233,7 +235,7 @@ func TearDownToxiProxyACS(ctx context.Context, clusterProxy framework.ClusterPro } func parseUrl(url string) (string, string, int, string) { - serverRegex := regexp.MustCompilePOSIX("(https?)://([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):([0-9]+)?(.*)") + serverRegex := regexp.MustCompilePOSIX("(https?)://([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+|[a-zA-Z0-9. _-]+):([0-9]+)?(.*)") urlComponents := serverRegex.FindStringSubmatch(url) Expect(len(urlComponents)).To(BeNumerically(">=", 4)) diff --git a/test/e2e/two_clusters.go b/test/e2e/two_clusters.go index 4ee5d8da..1a09c7d0 100644 --- a/test/e2e/two_clusters.go +++ b/test/e2e/two_clusters.go @@ -22,7 +22,7 @@ import ( "os" "path/filepath" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/test/e2e/two_clusters_test.go b/test/e2e/two_clusters_test.go index 925e05ce..7e6a88e1 100644 --- a/test/e2e/two_clusters_test.go +++ b/test/e2e/two_clusters_test.go @@ -21,7 +21,8 @@ package e2e import ( "context" - . "github.com/onsi/ginkgo" + + . "github.com/onsi/ginkgo/v2" ) var _ = Describe("with two clusters", func() { diff --git a/test/e2e/vpc_network.go b/test/e2e/vpc_network.go new file mode 100644 index 00000000..095389dd --- /dev/null +++ b/test/e2e/vpc_network.go @@ -0,0 +1,120 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + "sigs.k8s.io/cluster-api/util" +) + +// VPCNetworkSpec implements a test that verifies cluster creation in a VPC network. +func VPCNetworkSpec(ctx context.Context, inputGetter func() CommonSpecInput) { + var ( + specName = "vpc-network" + input CommonSpecInput + namespace *corev1.Namespace + cancelWatches context.CancelFunc + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult + ) + + BeforeEach(func() { + Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) + input = inputGetter() + Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName) + Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName) + Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName) + Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName) + Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion)) + Expect(input.E2EConfig.Variables).To(HaveValidVersion(input.E2EConfig.GetVariable(KubernetesVersion))) + + // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. + namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder) + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + }) + + It("Should successfully create a cluster in a VPC network with default offering", func() { + By("Creating a workload cluster in a VPC network with default offering") + vpcName := input.E2EConfig.GetVariable(VPCName) + networkName := input.E2EConfig.GetVariable(VPCNetworkName) + setupClusterWithVpcAndVerifyOfferingName(ctx, input, namespace, clusterResources, specName, "vpc-network", vpcName, DefaultVPCOffering, networkName, DefaultVPCNetworkOffering) + By("PASSED!") + }) + + It("Should successfully create a cluster in a VPC network with a custom offering", func() { + By("Creating a workload cluster in a VPC network with a custom offering") + vpcWithCustomOfferingName := input.E2EConfig.GetVariable(VPCWithCustomOfferingName) + networkName := input.E2EConfig.GetVariable(VPCNetworkWithCustomOfferingName) + customVpcOfferingName := input.E2EConfig.GetVariable(CustomVPCOfferingName) + customVpcNetworkOfferingName := input.E2EConfig.GetVariable(CustomVPCNetworkOfferingName) + setupClusterWithVpcAndVerifyOfferingName(ctx, input, namespace, clusterResources, specName, "custom-vpc-offering", vpcWithCustomOfferingName, customVpcOfferingName, networkName, customVpcNetworkOfferingName) + By("PASSED!") + }) + + AfterEach(func() { + // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. + dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup) + }) +} + +func setupClusterWithVpcAndVerifyOfferingName(ctx context.Context, input CommonSpecInput, namespace *corev1.Namespace, + clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult, specName string, flavor string, vpcName string, + vpcOfferingName string, networkName string, networkOfferingName string, +) { + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.BootstrapClusterProxy, + CNIManifestPath: input.E2EConfig.GetVariable(CNIPath), + ConfigCluster: clusterctl.ConfigClusterInput{ + LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()), + ClusterctlConfigPath: input.ClusterctlConfigPath, + KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(), + InfrastructureProvider: clusterctl.DefaultInfrastructureProvider, + Flavor: flavor, + Namespace: namespace.Name, + ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)), + KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion), + ControlPlaneMachineCount: pointer.Int64(1), + WorkerMachineCount: pointer.Int64(1), + }, + WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + csClient := CreateCloudStackClient(ctx, input.BootstrapClusterProxy.GetKubeconfigPath()) + + Byf("Checking if VPC %s exists with offering %s", vpcName, vpcOfferingName) + vpc, count, err := csClient.VPC.GetVPCByName(vpcName) + Expect(err).To(BeNil()) + Expect(count).To(BeEquivalentTo(1)) + Expect(vpc.Vpcofferingname).To(BeEquivalentTo(vpcOfferingName)) + + Byf("Checking if network %s exists with offering %s", networkName, networkOfferingName) + network, count, err := csClient.Network.GetNetworkByName(networkName) + Expect(err).To(BeNil()) + Expect(count).To(BeEquivalentTo(1)) + Expect(network.Networkofferingname).To(BeEquivalentTo(networkOfferingName)) +} diff --git a/test/e2e/vpc_network_test.go b/test/e2e/vpc_network_test.go new file mode 100644 index 00000000..92cab356 --- /dev/null +++ b/test/e2e/vpc_network_test.go @@ -0,0 +1,40 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("When testing clusters in a VPC network", func() { + + VPCNetworkSpec(context.TODO(), func() CommonSpecInput { + return CommonSpecInput{ + E2EConfig: e2eConfig, + ClusterctlConfigPath: clusterctlConfigPath, + BootstrapClusterProxy: bootstrapClusterProxy, + ArtifactFolder: artifactFolder, + SkipCleanup: skipCleanup, + } + }) + +}) diff --git a/test/fakes/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml b/test/fakes/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml index cca2e027..0cc24beb 100644 --- a/test/fakes/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml +++ b/test/fakes/fakes.infrastructure.cluster.x-k8s.io_cloudstackmachineowners.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.16.5 name: cloudstackmachineowners.fakes.infrastructure.cluster.x-k8s.io spec: group: fakes.infrastructure.cluster.x-k8s.io @@ -22,14 +21,19 @@ spec: (machineset, etcdadmcluster, kubeadmcontrolplane) 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' + 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' + 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 @@ -59,9 +63,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/test/fakes/groupversion_info.go b/test/fakes/groupversion_info.go index fb03161a..1f196b79 100644 --- a/test/fakes/groupversion_info.go +++ b/test/fakes/groupversion_info.go @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1beta2 contains API Schema definitions for the infrastructure v1beta2 API group +// Package v1beta3 contains API Schema definitions for the infrastructure v1beta3 API group // +kubebuilder:object:generate=true // +groupName=fakes.infrastructure.cluster.x-k8s.io package fakes diff --git a/test/fakes/zz_generated.deepcopy.go b/test/fakes/zz_generated.deepcopy.go index 5e8a7921..8deb0cf0 100644 --- a/test/fakes/zz_generated.deepcopy.go +++ b/test/fakes/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* Copyright 2022. diff --git a/test/helpers/suite_test.go b/test/helpers/suite_test.go index 992ca524..5af06443 100644 --- a/test/helpers/suite_test.go +++ b/test/helpers/suite_test.go @@ -3,11 +3,11 @@ package helpers_test import ( "testing" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" ) func TestCloud(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Cloud Suite") + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Cloud Suite") } diff --git a/test/helpers/user_test.go b/test/helpers/user_test.go index b666f204..0b1b2ba6 100644 --- a/test/helpers/user_test.go +++ b/test/helpers/user_test.go @@ -17,110 +17,110 @@ limitations under the License. package helpers_test import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" + "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" "sigs.k8s.io/cluster-api-provider-cloudstack/pkg/cloud" "sigs.k8s.io/cluster-api-provider-cloudstack/test/helpers" ) -var _ = Describe("Test helper methods", func() { +var _ = ginkgo.Describe("Test helper methods", func() { csClient, err := helpers.NewCSClient() - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) // Get the root domain's ID. rootDomainID, err, found := helpers.GetDomainByPath(csClient, "ROOT/") - Ω(err).ShouldNot(HaveOccurred()) - Ω(rootDomainID).ShouldNot(BeEmpty()) - Ω(found).Should(BeTrue()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(rootDomainID).ShouldNot(gomega.BeEmpty()) + gomega.Ω(found).Should(gomega.BeTrue()) - AfterEach(func() { + ginkgo.AfterEach(func() { for _, path := range []string{"ROOT/someNewDomain", "ROOT/blah"} { // Delete any created domains. id, err, found := helpers.GetDomainByPath(csClient, path) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) if found { - Ω(helpers.DeleteDomain(csClient, id)).Should(Succeed()) + gomega.Ω(helpers.DeleteDomain(csClient, id)).Should(gomega.Succeed()) } } }) - Context("Domain Creation and Deletion.", func() { - It("Can get the ROOT domain's ID.", func() { + ginkgo.Context("Domain Creation and Deletion.", func() { + ginkgo.It("Can get the ROOT domain's ID.", func() { id, err, found := helpers.GetDomainByPath(csClient, "ROOT/") - Ω(err).ShouldNot(HaveOccurred()) - Ω(id).ShouldNot(BeEmpty()) - Ω(found).Should(BeTrue()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(id).ShouldNot(gomega.BeEmpty()) + gomega.Ω(found).Should(gomega.BeTrue()) }) - It("Doesn't error when unable to get a domain's ID.", func() { + ginkgo.It("Doesn't error when unable to get a domain's ID.", func() { id, err, found := helpers.GetDomainByPath(csClient, "ROOT/blahnotpresent") - Ω(err).ShouldNot(HaveOccurred()) - Ω(found).Should(BeFalse()) - Ω(id).Should(BeEmpty()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + gomega.Ω(found).Should(gomega.BeFalse()) + gomega.Ω(id).Should(gomega.BeEmpty()) }) - It("Can create a domain under a parent domain.", func() { + ginkgo.It("Can create a domain under a parent domain.", func() { id, err := helpers.CreateDomainUnderParent(csClient, rootDomainID, "someNewDomain") - Ω(id).ShouldNot(BeEmpty()) - Ω(err).ShouldNot(HaveOccurred()) + gomega.Ω(id).ShouldNot(gomega.BeEmpty()) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) }) - It("Returns an appropriate error when the domain already exists.", func() { + ginkgo.It("Returns an appropriate error when the domain already exists.", func() { someDomain := &cloud.Domain{Name: "blah", Path: "blah"} - Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(Succeed()) - Ω(someDomain.Name).Should(Equal("blah")) - Ω(someDomain.Path).Should(Equal("ROOT/blah")) - Ω(someDomain.ID).ShouldNot(BeEmpty()) + gomega.Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(gomega.Succeed()) + gomega.Ω(someDomain.Name).Should(gomega.Equal("blah")) + gomega.Ω(someDomain.Path).Should(gomega.Equal("ROOT/blah")) + gomega.Ω(someDomain.ID).ShouldNot(gomega.BeEmpty()) _, err = helpers.CreateDomainUnderParent(csClient, rootDomainID, "blah") - Ω(err).Should(HaveOccurred()) - Ω(err.Error()).Should(ContainSubstring("already exists")) + gomega.Ω(err).Should(gomega.HaveOccurred()) + gomega.Ω(err.Error()).Should(gomega.ContainSubstring("already exists")) }) - It("Doesn't error if the domain already exists.", func() { + ginkgo.It("Doesn't error if the domain already exists.", func() { someDomain := &cloud.Domain{Name: "blah", Path: "blah"} - Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(Succeed()) - Ω(someDomain.Name).Should(Equal("blah")) - Ω(someDomain.Path).Should(Equal("ROOT/blah")) - Ω(someDomain.ID).ShouldNot(BeEmpty()) - - Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(Succeed()) - Ω(someDomain.Name).Should(Equal("blah")) - Ω(someDomain.Path).Should(Equal("ROOT/blah")) - Ω(someDomain.ID).ShouldNot(BeEmpty()) + gomega.Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(gomega.Succeed()) + gomega.Ω(someDomain.Name).Should(gomega.Equal("blah")) + gomega.Ω(someDomain.Path).Should(gomega.Equal("ROOT/blah")) + gomega.Ω(someDomain.ID).ShouldNot(gomega.BeEmpty()) + + gomega.Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(gomega.Succeed()) + gomega.Ω(someDomain.Name).Should(gomega.Equal("blah")) + gomega.Ω(someDomain.Path).Should(gomega.Equal("ROOT/blah")) + gomega.Ω(someDomain.ID).ShouldNot(gomega.BeEmpty()) }) - It("Can create a wholly new multi-level sub-domain path.", func() { + ginkgo.It("Can create a wholly new multi-level sub-domain path.", func() { someDomain := &cloud.Domain{Name: "tooBlah", Path: "ROOT/someNewDomain/tooBlah"} - Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(Succeed()) - Ω(someDomain.Name).Should(Equal("tooBlah")) - Ω(someDomain.Path).Should(Equal("ROOT/someNewDomain/tooBlah")) - Ω(someDomain.ID).ShouldNot(BeEmpty()) + gomega.Ω(helpers.GetOrCreateDomain(csClient, someDomain)).Should(gomega.Succeed()) + gomega.Ω(someDomain.Name).Should(gomega.Equal("tooBlah")) + gomega.Ω(someDomain.Path).Should(gomega.Equal("ROOT/someNewDomain/tooBlah")) + gomega.Ω(someDomain.ID).ShouldNot(gomega.BeEmpty()) }) }) - Context("Account Creation.", func() { - It("Can create a new account in a new domain.", func() { + ginkgo.Context("Account Creation.", func() { + ginkgo.It("Can create a new account in a new domain.", func() { domain := cloud.Domain{Path: "ROOT/someNewDomain/tooBlah"} account := cloud.Account{Name: "TempTestAccount", Domain: domain} - Ω(helpers.GetOrCreateAccount(csClient, &account)).Should(Succeed()) + gomega.Ω(helpers.GetOrCreateAccount(csClient, &account)).Should(gomega.Succeed()) }) // already exists - It("Doesn't fail if the account already exists.", func() { + ginkgo.It("Doesn't fail if the account already exists.", func() { domain := cloud.Domain{Path: "ROOT/someNewDomain/tooBlah"} account := cloud.Account{Name: "TempTestAccount", Domain: domain} - Ω(helpers.GetOrCreateAccount(csClient, &account)).Should(Succeed()) - Ω(helpers.GetOrCreateAccount(csClient, &account)).Should(Succeed()) + gomega.Ω(helpers.GetOrCreateAccount(csClient, &account)).Should(gomega.Succeed()) + gomega.Ω(helpers.GetOrCreateAccount(csClient, &account)).Should(gomega.Succeed()) }) }) - Context("User Creation w/Keys.", func() { - It("Can create a new user with keys.", func() { + ginkgo.Context("User Creation w/Keys.", func() { + ginkgo.It("Can create a new user with keys.", func() { domain := cloud.Domain{Path: "ROOT/someNewDomain/tooBlah"} account := cloud.Account{Name: "TempTestAccount", Domain: domain} user := cloud.User{Account: account} - Ω(helpers.GetOrCreateUserWithKey(csClient, &user)).Should(Succeed()) - Ω(user.ID).ShouldNot(BeEmpty()) - Ω(user.APIKey).ShouldNot(BeEmpty()) + gomega.Ω(helpers.GetOrCreateUserWithKey(csClient, &user)).Should(gomega.Succeed()) + gomega.Ω(user.ID).ShouldNot(gomega.BeEmpty()) + gomega.Ω(user.APIKey).ShouldNot(gomega.BeEmpty()) }) }) }) diff --git a/versions.mk b/versions.mk index 86422bbb..e2f88928 100644 --- a/versions.mk +++ b/versions.mk @@ -13,6 +13,6 @@ # limitations under the License. MDBOOK_VERSION := v0.4.5 -GOLANG_VERSION := 1.17.6 +GOLANG_VERSION := 1.23.2 PLANTUML_VERSION := 1.2020.16 GH_VERSION := 1.2.0 \ No newline at end of file