From 1d550236926b8d99d4ee546331550710c1983d38 Mon Sep 17 00:00:00 2001 From: Aitor Perez Cedres <1515757+Zerpet@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:30:58 +0000 Subject: [PATCH] Update Makefile - Use a function to verify the existence of required variables The previous approach was always checking the existence of variables, even for targets that did not require them e.g. `make fmt` would fail if any docker/registry variable was not defined. Now only targets that actually use those variable check for their definition state. - Install YTT locally We use some YTT where doing something very simple e.g. updating a container image, is terribly complicated in kustomize. Observe and compare deploy-dev vs deploy-local - Use "local" build and deploy targets "Local" targets that build containers locally and reference them in manifests, without pushing them. This is particularly useful in local environments, e.g. Docker Desktop, Rancher Desktop, where docker context is the same for Kuberentes and Image Building. --- Makefile | 187 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index 55442f6b..99bc8b52 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,37 @@ SHELL := bash platform := $(shell uname | tr A-Z a-z) +### Helper functions +### https://stackoverflow.com/questions/10858261/how-to-abort-makefile-if-variable-not-set +check_defined = \ + $(strip $(foreach 1,$1, \ + $(call __check_defined,$1,$(strip $(value 2))))) +__check_defined = \ + $(if $(value $1),, \ + $(error Undefined $1$(if $2, ($2))$(if $(value @), \ + required by target '$@'))) +### + # runs the target list by default .DEFAULT_GOAL = list # Insert a comment starting with '##' after a target, and it will be printed by 'make' and 'make list' -list: ## list Makefile targets +list: ## List Makefile targets @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' -### Tools -# Allows flexbility to use other build kits, like nerdctl -BUILD_KIT ?= /usr/local/bin/docker +############# +### Tools ### +############# + +# Allows flexibility to use other build kits, like nerdctl +BUILD_KIT ?= docker define get_mod_code_generator echo "Only go get & mod k8s.io/code-generator, but do not install it" echo "⚠️ Keep it at the same version as captured in go.mod, otherwise we may end up with version inconsistencies" awk '/k8s.io\/code-generator/ { system("go get -d " $$1 "@" $$2) }' go.mod endef -install-tools: +install-tools: ## Install tooling required to configure and build this repo go mod download @echo "Install all tools..." @awk -F '"' '/_/ && !/k8s.io\/code-generator/ { system("go install " $$2) }' tools/tools.go @@ -52,8 +66,21 @@ kubebuilder-assets: $(KUBEBUILDER_ASSETS) $(KUBEBUILDER_ASSETS): setup-envtest --os $(platform) --arch $(ARCHITECTURE) --bin-dir $(LOCAL_TESTBIN) use $(ENVTEST_K8S_VERSION) -### Targets - +# https://github.com/carvel-dev/ytt/releases +YTT_VERSION ?= v0.50.0 +YTT = $(LOCAL_BIN)/ytt-$(YTT_VERSION)-$(platform)-$(ARCHITECTURE) +.PHONY: ytt +ytt: | $(YTT) +$(YTT): | $(LOCAL_BIN) + @printf "Downloading and installing Carvel YTT\n" + @curl -sSL -o $(YTT) https://github.com/carvel-dev/ytt/releases/download/$(YTT_VERSION)/ytt-$(platform)-$(ARCHITECTURE) + @chmod +x $(YTT) + @ln -s $(LOCAL_BIN)/ytt-$(YTT_VERSION)-$(platform)-$(ARCHITECTURE) $(LOCAL_BIN)/ytt + @printf "Carvel YTT $(YTT_VERSION) installed locally\n" + +############## +#### Tests ### +############## .PHONY: unit-tests unit-tests::install-tools ## Run unit tests unit-tests::$(KUBEBUILDER_ASSETS) @@ -79,12 +106,18 @@ integration-tests::just-integration-tests just-integration-tests: $(KUBEBUILDER_ASSETS) ginkgo --randomize-all -r -p $(GINKGO_EXTRA) controllers/ +.PHONY: local-tests local-tests: unit-tests integration-tests ## Run all local tests (unit & integration) -system-tests: ## run end-to-end tests against Kubernetes cluster defined in ~/.kube/config. Expects cluster operator and messaging topology operator to be installed in the cluster +.PHONY: system-tests +system-tests: ## Run E2E tests using current context in ~/.kube/config. Expects cluster operator and topology operator to be installed in the cluster NAMESPACE="rabbitmq-system" ginkgo --randomize-all -r $(GINKGO_EXTRA) system_tests/ -# Build manager binary + +################### +### Build & Run ### +################### +.PHONY: manager manager: generate fmt vet vuln go build -o bin/manager main.go @@ -95,49 +128,32 @@ manager: generate fmt vet vuln # https://github.com/telepresenceio/telepresence is one way to do this (just run # `telepresence connect` and services like `test-service.test-namespace.svc.cluster.local` # will resolve properly). +.PHONY: run run: generate fmt vet vuln manifests install just-run +.PHONY: just-run just-run: ## Just runs 'go run main.go' without regenerating any manifests or deploying RBACs KUBE_CONFIG=${HOME}/.kube/config OPERATOR_NAMESPACE=rabbitmq-system ENABLE_WEBHOOKS=false ENABLE_DEBUG_PPROF=true go run ./main.go -metrics-bind-address 127.0.0.1:8080 -# Install CRDs into a cluster +.PHONY: install install: manifests kustomize build config/crd | kubectl apply -f - -# Uninstall CRDs from a cluster +.PHONY: uninstall uninstall: manifests kustomize build config/crd | kubectl delete -f - +.PHONY: deploy-manager deploy-manager: cmctl $(CMCTL) check api --wait=2m kustomize build config/default/overlays/cert-manager/ | kubectl apply -f - -deploy: manifests deploy-rbac deploy-manager - -destroy: - kustomize build config/rbac | kubectl delete --ignore-not-found=true -f - - kustomize build config/default/base | kubectl delete --ignore-not-found=true -f - - -# Deploy operator with local changes -deploy-dev: check-env-docker-credentials cmctl docker-build-dev manifests deploy-rbac docker-registry-secret set-operator-image-repo - $(CMCTL) check api --wait=2m - kustomize build config/default/overlays/dev | sed 's@((operator_docker_image))@"$(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT)"@' | kubectl apply -f - - -# Load operator image and deploy operator into current KinD cluster -deploy-kind: manifests cmctl deploy-rbac - $(BUILD_KIT) buildx build --build-arg=GIT_COMMIT=$(GIT_COMMIT) -t $(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT) . - kind load docker-image $(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT) - $(CMCTL) check api --wait=2m - kustomize build config/default/overlays/kind | sed 's@((operator_docker_image))@"$(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT)"@' | kubectl apply -f - - -deploy-rbac: - kustomize build config/rbac | kubectl apply -f - - # Generate manifests e.g. CRD, RBAC etc. manifests: install-tools controller-gen crd rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases # Generate API reference documentation +.PHONY: api-reference api-reference: crd-ref-docs \ --source-path ./api \ @@ -146,6 +162,14 @@ api-reference: --output-path ./docs/api/rabbitmq.com.ref.asciidoc \ --max-depth 30 +## used in CI pipeline to create release artifact +.PHONY: generate-manifests +generate-manifests: + mkdir -p releases + kustomize build config/installation/ > releases/messaging-topology-operator.bak + sed '/CERTIFICATE_NAMESPACE.*CERTIFICATE_NAME/d' releases/messaging-topology-operator.bak > releases/messaging-topology-operator.yaml + kustomize build config/installation/cert-manager/ > releases/messaging-topology-operator-with-certmanager.yaml + # Run go fmt against code fmt: go fmt ./... @@ -162,61 +186,90 @@ vuln: generate: install-tools api-reference controller-gen object:headerFile="hack/NOTICE.go.txt" paths="./..." +.PHONY: generate-client-set generate-client-set: $(get_mod_code_generator) go mod vendor ./hack/update-codegen.sh -check-env-docker-credentials: check-env-registry-server -ifndef DOCKER_REGISTRY_USERNAME - $(error DOCKER_REGISTRY_USERNAME is undefined: Username for accessing the docker registry) -endif -ifndef DOCKER_REGISTRY_PASSWORD - $(error DOCKER_REGISTRY_PASSWORD is undefined: Password for accessing the docker registry) -endif -ifndef DOCKER_REGISTRY_SECRET - $(error DOCKER_REGISTRY_SECRET is undefined: Name of Kubernetes secret in which to store the Docker registry username and password) -endif - GIT_COMMIT=$(shell git rev-parse --short HEAD)-dev - -docker-build-dev: check-env-docker-repo +OPERATOR_IMAGE ?= rabbitmqoperator/messaging-topology-operator +.PHONY: docker-build-dev +docker-build-dev: + $(call check_defined, DOCKER_REGISTRY_SERVER, URL of docker registry containing the Operator image (e.g. registry.my-company.com)) $(BUILD_KIT) buildx build --build-arg=GIT_COMMIT=$(GIT_COMMIT) -t $(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT) . $(BUILD_KIT) push $(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT) -docker-registry-secret: check-env-docker-credentials operator-namespace - echo "creating registry secret and patching default service account" - @kubectl -n $(K8S_OPERATOR_NAMESPACE) create secret docker-registry $(DOCKER_REGISTRY_SECRET) --docker-server='$(DOCKER_REGISTRY_SERVER)' --docker-username="$$DOCKER_REGISTRY_USERNAME" --docker-password="$$DOCKER_REGISTRY_PASSWORD" || true +# docker-build-local and deploy-local work in local Kubernetes installations where the Kubernetes API +# server runs in the same Docker Context as the build process. This is the case for Rancher Desktop +# and probably for Docker Desktop. These two commands won't have the desired effect if Kubernetes API +# is running remotely e.g. Cloud, or if the build context is not shared with Kubernetes API e.g. containerd +.PHONY: docker-build-local +docker-build-local: + $(BUILD_KIT) buildx build --build-arg=GIT_COMMIT=$(GIT_COMMIT) -t localhost/topology-operator:$(GIT_COMMIT) . + +K8S_OPERATOR_NAMESPACE ?= rabbitmq-system +.PHONY: docker-registry-secret +docker-registry-secret: + $(call check_defined, DOCKER_REGISTRY_USERNAME, Username for accessing the docker registry) + $(call check_defined, DOCKER_REGISTRY_PASSWORD. Password for accessing the docker registry) + $(call check_defined, DOCKER_REGISTRY_SECRET, Name of Kubernetes secret in which to store the Docker registry username and password) + $(call check_defined, DOCKER_REGISTRY_SERVER, URL of docker registry containing the Operator image (e.g. registry.my-company.com)) + @echo "Creating registry secret and patching default service account" + @kubectl -n $(K8S_OPERATOR_NAMESPACE) create secret docker-registry $(DOCKER_REGISTRY_SECRET) \ + --docker-server='$(DOCKER_REGISTRY_SERVER)' \ + --docker-username="$$DOCKER_REGISTRY_USERNAME" \ + --docker-password="$$DOCKER_REGISTRY_PASSWORD" || true @kubectl -n $(K8S_OPERATOR_NAMESPACE) patch serviceaccount messaging-topology-operator -p '{"imagePullSecrets": [{"name": "$(DOCKER_REGISTRY_SECRET)"}]}' -check-env-registry-server: -ifndef DOCKER_REGISTRY_SERVER - $(error DOCKER_REGISTRY_SERVER is undefined: URL of docker registry containing the Operator image (e.g. registry.my-company.com)) -endif +######################### +### Deploy & Teardown ### +######################### +.PHONY: deploy +deploy: manifests deploy-rbac deploy-manager ## Deploy latest version of this Operator -check-env-docker-repo: check-env-registry-server set-operator-image-repo +.PHONY: destroy +destroy: ## Delete all resources of this Operator + kustomize build config/rbac | kubectl delete --ignore-not-found=true -f - + kustomize build config/default/base | kubectl delete --ignore-not-found=true -f - -set-operator-image-repo: -OPERATOR_IMAGE?=p-rabbitmq-for-kubernetes/messaging-topology-operator +# Deploy operator with local changes +.PHONY: deploy-dev +deploy-dev: cmctl docker-build-dev manifests deploy-rbac docker-registry-secret ## Build current code as a Docker image, push the image, and deploy to current Kubernetes context + $(call check_defined, DOCKER_REGISTRY_USERNAME, Username for accessing the docker registry) + $(call check_defined, DOCKER_REGISTRY_PASSWORD. Password for accessing the docker registry) + $(call check_defined, DOCKER_REGISTRY_SECRET, Name of Kubernetes secret in which to store the Docker registry username and password) + $(call check_defined, DOCKER_REGISTRY_SERVER, URL of docker registry containing the Operator image (e.g. registry.my-company.com)) + $(CMCTL) check api --wait=2m + kustomize build config/default/overlays/dev | sed 's@((operator_docker_image))@"$(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT)"@' | kubectl apply -f - -operator-namespace: -ifeq (, $(K8S_OPERATOR_NAMESPACE)) -K8S_OPERATOR_NAMESPACE=rabbitmq-system -endif +# Load operator image and deploy operator into current KinD cluster +.PHONY: deploy-kind +deploy-kind: manifests cmctl deploy-rbac + $(BUILD_KIT) buildx build --build-arg=GIT_COMMIT=$(GIT_COMMIT) -t $(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT) . + kind load docker-image $(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT) + $(CMCTL) check api --wait=2m + kustomize build config/default/overlays/kind | sed 's@((operator_docker_image))@"$(DOCKER_REGISTRY_SERVER)/$(OPERATOR_IMAGE):$(GIT_COMMIT)"@' | kubectl apply -f - + +.PHONY: deploy-local +deploy-local: cmctl deploy-rbac $(YTT) + $(CMCTL) check api --wait=2m + kustomize build config/default/overlays/cert-manager | $(YTT) -f- -f config/ytt_overlays/change_deployment_image.yml \ + --data-value operator_image="localhost/topology-operator:$(GIT_COMMIT)" \ + -f config/ytt_overlays/never_pull.yml | kubectl apply -f- + +.PHONY: deploy-rbac +deploy-rbac: + kustomize build config/rbac | kubectl apply -f - +.PHONY: cluster-operator cluster-operator: @kubectl apply -f https://github.com/rabbitmq/cluster-operator/releases/latest/download/cluster-operator.yml +.PHONY: destroy-cluster-operator destroy-cluster-operator: @kubectl delete -f https://github.com/rabbitmq/cluster-operator/releases/latest/download/cluster-operator.yml --ignore-not-found -## used in CI pipeline to create release artifact -generate-manifests: - mkdir -p releases - kustomize build config/installation/ > releases/messaging-topology-operator.bak - sed '/CERTIFICATE_NAMESPACE.*CERTIFICATE_NAME/d' releases/messaging-topology-operator.bak > releases/messaging-topology-operator.yaml - kustomize build config/installation/cert-manager/ > releases/messaging-topology-operator-with-certmanager.yaml - ################ # Cert Manager # ################ @@ -234,8 +287,10 @@ $(CMCTL): | $(LOCAL_BIN) $(LOCAL_TMP) CERT_MANAGER_VERSION ?= v1.15.1 CERT_MANAGER_MANIFEST ?= https://github.com/jetstack/cert-manager/releases/download/$(CERT_MANAGER_VERSION)/cert-manager.yaml +.PHONY: cert-manager cert-manager: ## Deploys Cert Manager from JetStack repo. Use CERT_MANAGER_VERSION to customise version e.g. v1.2.0 kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/$(CERT_MANAGER_VERSION)/cert-manager.yaml +.PHONY: destroy-cert-manager destroy-cert-manager: ## Deletes Cert Manager deployment created by 'make cert-manager' kubectl delete -f https://github.com/jetstack/cert-manager/releases/download/$(CERT_MANAGER_VERSION)/cert-manager.yaml