Skip to content

Commit

Permalink
Update Makefile
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
Zerpet committed Nov 4, 2024
1 parent 26f1f59 commit 1d55023
Showing 1 changed file with 121 additions and 66 deletions.
187 changes: 121 additions & 66 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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

Expand All @@ -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 \
Expand All @@ -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 ./...
Expand All @@ -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 #
################
Expand All @@ -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

0 comments on commit 1d55023

Please sign in to comment.