1- # Image URL to use all building/pushing image targets
2- # if :latest then Kubernetes defaults imagePullPolicy: Always
3- # which fails if the image tag is intended to be used only locally
4- # so default to :dev for improved DX and override when needed in pipelines
5- IMG ?= controller:dev
1+ # ##----------------------------------------
2+ # # Variables
3+ # ------------------------------------------
4+ # Multi-module paths
5+ # Each module must be tagged with its directory path prefix for Go module resolution
6+ # Example tags: v0.1.0 (root), api/v0.1.0, pkg/cluster-handler/v0.1.0, etc.
7+ MODULES := . ./api ./pkg/cluster-handler ./pkg/data-handler ./pkg/resource-handler
8+
9+ # Version from git tags (for root module - operator binary)
10+ # Root module uses tags like v0.1.0 (without prefix)
11+ VERSION ?= $(shell git describe --tags --match "v* " --always --dirty 2>/dev/null || echo "v0.0.1-dev")
12+ VERSION_SHORT ?= $(shell echo $(VERSION ) | sed 's/^v//')
13+
14+ # Image configuration
15+ IMG_PREFIX ?= ghcr.io/numtide
16+ IMG_REPO ?= multigres-operator
17+ IMG ?= $(IMG_PREFIX ) /$(IMG_REPO ) :$(VERSION_SHORT )
18+
19+ # Build metadata
20+ BUILD_DATE ?= $(shell date -u '+% Y-% m-% dT% H:% M:% SZ')
21+ GIT_COMMIT ?= $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
22+
23+ # LDFLAGS for version info
24+ LDFLAGS := -X main.version=$(VERSION ) \
25+ -X main.buildDate=$(BUILD_DATE ) \
26+ -X main.gitCommit=$(GIT_COMMIT )
627
728# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
829ifeq (,$(shell go env GOBIN) )
@@ -17,11 +38,49 @@ endif
1738# tools. (i.e. podman)
1839CONTAINER_TOOL ?= docker
1940
41+ # Kind cluster name for local development
42+ KIND_CLUSTER ?= multigres-operator-dev
43+
44+ # Local kubeconfig for kind cluster (doesn't modify user's ~/.kube/config)
45+ KIND_KUBECONFIG ?= $(shell pwd) /kubeconfig.yaml
46+
2047# Setting SHELL to bash allows bash commands to be executed by recipes.
2148# Options are set to exit when a recipe line exits non-zero or a piped command fails.
2249SHELL = /usr/bin/env bash -o pipefail
2350.SHELLFLAGS = -ec
2451
52+ # # Location to install dependencies to
53+ LOCALBIN ?= $(shell pwd) /bin
54+ $(LOCALBIN ) :
55+ mkdir -p $(LOCALBIN )
56+
57+ # # Tool Binaries
58+ KUBECTL ?= kubectl
59+ KIND ?= kind
60+ KUSTOMIZE ?= $(LOCALBIN ) /kustomize
61+ CONTROLLER_GEN ?= $(LOCALBIN ) /controller-gen
62+ ENVTEST ?= $(LOCALBIN ) /setup-envtest
63+ GOLANGCI_LINT = $(LOCALBIN ) /golangci-lint
64+
65+ # # Tool Versions
66+ # renovate: datasource=github-releases depName=kubernetes-sigs/kustomize
67+ KUSTOMIZE_VERSION ?= v5.6.0
68+ # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools
69+ CONTROLLER_TOOLS_VERSION ?= v0.18.0
70+ # renovate: datasource=github-releases depName=golangci/golangci-lint
71+ GOLANGCI_LINT_VERSION ?= v2.3.0
72+
73+ # # Envtest
74+ # ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20)
75+ ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-% d.% d", $$2, $$3}')
76+ # ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
77+ # NOTE: This version should match the version defined in devshell.nix
78+ ENVTEST_K8S_VERSION ?= 1.33
79+
80+ # ##----------------------------------------
81+ # # Comamnds
82+ # ------------------------------------------
83+
2584.PHONY : all
2685all : build
2786
@@ -42,28 +101,93 @@ all: build
42101help : # # Display this help.
43102 @awk ' BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST )
44103
104+ # #@ Multi-Module Operations
105+
106+ .PHONY : modules-tidy
107+ modules-tidy : # # Run go mod tidy on all modules
108+ @for mod in $(MODULES ) ; do \
109+ echo " ==> Tidying $$ mod..." ; \
110+ (cd $$ mod && go mod tidy) || exit 1; \
111+ done
112+
113+ .PHONY : modules-download
114+ modules-download : # # Download dependencies for all modules
115+ @for mod in $(MODULES ) ; do \
116+ echo " ==> Downloading dependencies for $$ mod..." ; \
117+ (cd $$ mod && go mod download) || exit 1; \
118+ done
119+
120+ .PHONY : modules-verify
121+ modules-verify : # # Verify dependencies for all modules
122+ @for mod in $(MODULES ) ; do \
123+ echo " ==> Verifying $$ mod..." ; \
124+ (cd $$ mod && go mod verify) || exit 1; \
125+ done
126+
45127# #@ Development
46128
47129.PHONY : manifests
48130manifests : controller-gen # # Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
49- $(CONTROLLER_GEN ) rbac:roleName=manager-role crd webhook paths=" ./..." output:crd:artifacts:config=config/crd/bases
131+ $(CONTROLLER_GEN ) rbac:roleName=manager-role crd webhook paths=" ./api/ ..." output:crd:artifacts:config=config/crd/bases
50132
51133.PHONY : generate
52134generate : controller-gen # # Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
53135 $(CONTROLLER_GEN ) object paths=" ./api/..."
54136# $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
55137
56138.PHONY : fmt
57- fmt : # # Run go fmt against code.
58- go fmt ./...
139+ fmt : # # Run go fmt against code in all modules
140+ @for mod in $(MODULES ) ; do \
141+ echo " ==> Formatting $$ mod..." ; \
142+ (cd $$ mod && go fmt ./...) || exit 1; \
143+ done
59144
60145.PHONY : vet
61- vet : # # Run go vet against code.
62- go vet ./...
146+ vet : # # Run go vet against code in all modules
147+ @for mod in $(MODULES ) ; do \
148+ echo " ==> Vetting $$ mod..." ; \
149+ (cd $$ mod && go vet ./...) || exit 1; \
150+ done
63151
64152.PHONY : test
65- test : manifests generate fmt vet setup-envtest # # Run tests.
66- KUBEBUILDER_ASSETS=" $( shell $( ENVTEST) use $( ENVTEST_K8S_VERSION) --bin-dir $( LOCALBIN) -p path) " go test $$(go list ./... | grep -v /e2e ) -coverprofile cover.out
153+ test : manifests generate fmt vet setup-envtest # # Run tests for all modules
154+ @echo " ==> Running tests across all modules"
155+ @for mod in $(MODULES ) ; do \
156+ echo " ==> Testing $$ mod..." ; \
157+ (cd $$ mod && \
158+ KUBEBUILDER_ASSETS=" $( shell $( ENVTEST) use $( ENVTEST_K8S_VERSION) --bin-dir $( LOCALBIN) -p path) " \
159+ go test $$(go list ./... | grep -v /e2e ) -coverprofile=cover.out) || exit 1; \
160+ done
161+
162+ .PHONY : test-unit
163+ test-unit : manifests generate fmt vet setup-envtest # # Run unit tests for all modules (fast, no e2e)
164+ @echo " ==> Running unit tests across all modules"
165+ @for mod in $(MODULES ) ; do \
166+ echo " ==> Unit testing $$ mod..." ; \
167+ (cd $$ mod && \
168+ KUBEBUILDER_ASSETS=" $( shell $( ENVTEST) use $( ENVTEST_K8S_VERSION) --bin-dir $( LOCALBIN) -p path) " \
169+ go test $$(go list ./... | grep -v /e2e ) -short -v) || exit 1; \
170+ done
171+
172+ .PHONY : test-coverage
173+ test-coverage : manifests generate fmt vet setup-envtest # # Generate coverage report with HTML
174+ @mkdir -p coverage
175+ @echo " ==> Generating coverage across all modules"
176+ @for mod in $(MODULES ) ; do \
177+ modname=$$(basename $$mod) ; \
178+ [ " $$ modname" = " ." ] && modname=" root" ; \
179+ echo " ==> Coverage for $$ mod..." ; \
180+ (cd $$ mod && \
181+ KUBEBUILDER_ASSETS=" $( shell $( ENVTEST) use $( ENVTEST_K8S_VERSION) --bin-dir $( LOCALBIN) -p path) " \
182+ go test ./... -coverprofile=../coverage/$$ modname.out -covermode=atomic -coverpkg=./...) || exit 1; \
183+ done
184+ @echo " ==> Generating HTML reports"
185+ @for out in coverage/* .out; do \
186+ html=$$ {out%.out}.html; \
187+ go tool cover -html=$$ out -o=$$ html; \
188+ echo " Generated: $$ html" ; \
189+ done
190+ @echo " Coverage reports in coverage/"
67191
68192# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
69193# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
@@ -95,22 +219,49 @@ cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
95219 @$(KIND ) delete cluster --name $(KIND_CLUSTER )
96220
97221.PHONY : lint
98- lint : golangci-lint # # Run golangci-lint linter
99- $(GOLANGCI_LINT ) run
222+ lint : golangci-lint # # Run golangci-lint linter across all modules
223+ @for mod in $(MODULES ) ; do \
224+ echo " ==> Linting $$ mod..." ; \
225+ (cd $$ mod && $( GOLANGCI_LINT) run) || exit 1; \
226+ done
100227
101228.PHONY : lint-fix
102229lint-fix : golangci-lint # # Run golangci-lint linter and perform fixes
103- $(GOLANGCI_LINT ) run --fix
230+ @for mod in $(MODULES ) ; do \
231+ echo " ==> Fixing lint issues in $$ mod..." ; \
232+ (cd $$ mod && $( GOLANGCI_LINT) run --fix) || exit 1; \
233+ done
104234
105235.PHONY : lint-config
106236lint-config : golangci-lint # # Verify golangci-lint linter configuration
107237 $(GOLANGCI_LINT ) config verify
108238
239+ # #@ Code Quality
240+
241+ .PHONY : check
242+ check : lint test # # Run all checks before committing (lint + test)
243+ @echo " ==> All checks passed!"
244+
245+ .PHONY : verify
246+ verify : manifests generate # # Verify generated files are up to date
247+ @echo " ==> Verifying generated files are committed"
248+ @git diff --exit-code config/crd api/ || { \
249+ echo " ERROR: Generated files are out of date." ; \
250+ echo " Run 'make manifests generate' and commit the changes." ; \
251+ exit 1; \
252+ }
253+ @echo " ==> Verification passed!"
254+
255+ .PHONY : pre-commit
256+ pre-commit : modules-tidy fmt vet lint test # # Run full pre-commit checks (tidy, fmt, vet, lint, test)
257+ @echo " ==> Pre-commit checks passed!"
258+
109259# #@ Build
110260
111261.PHONY : build
112- build : manifests generate fmt vet # # Build manager binary.
113- go build -o bin/manager cmd/multigres-operator/main.go
262+ build : manifests generate fmt vet # # Build manager binary with version metadata
263+ @echo " ==> Building operator binary (version: $( VERSION) )"
264+ go build -ldflags=" $( LDFLAGS) " -o bin/multigres-operator cmd/multigres-operator/main.go
114265
115266.PHONY : run
116267run : manifests generate fmt vet # # Run a controller from your host.
@@ -119,16 +270,16 @@ run: manifests generate fmt vet ## Run a controller from your host.
119270# If you wish to build the manager image targeting other platforms you can use the --platform flag.
120271# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
121272# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
122- .PHONY : docker-build
123- docker-build : # # Build docker image with the manager.
273+ .PHONY : container
274+ container : # # Build container image
124275 $(CONTAINER_TOOL ) build -t ${IMG} .
125276
126277.PHONY : minikube-load
127278minikube-load :
128279 minikube image load ${IMG}
129280
130- .PHONY : docker -push
131- docker -push : # # Push docker image with the manager.
281+ .PHONY : container -push
282+ container -push : # # Push container image
132283 $(CONTAINER_TOOL ) push ${IMG}
133284
134285# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
@@ -177,29 +328,53 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in
177328undeploy : kustomize # # Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
178329 $(KUSTOMIZE ) build config/default | $(KUBECTL ) delete --ignore-not-found=$(ignore-not-found ) -f -
179330
180- # #@ Dependencies
181-
182- # # Location to install dependencies to
183- LOCALBIN ?= $(shell pwd) /bin
184- $(LOCALBIN ) :
185- mkdir -p $(LOCALBIN )
331+ # #@ Kind Cluster (Local Development)
186332
187- # # Tool Binaries
188- KUBECTL ?= kubectl
189- KIND ?= kind
190- KUSTOMIZE ?= $(LOCALBIN ) /kustomize
191- CONTROLLER_GEN ?= $(LOCALBIN ) /controller-gen
192- ENVTEST ?= $(LOCALBIN ) /setup-envtest
193- GOLANGCI_LINT = $(LOCALBIN ) /golangci-lint
333+ .PHONY : kind-up
334+ kind-up : # # Create a kind cluster for local development
335+ @command -v $(KIND ) > /dev/null 2>&1 || { \
336+ echo " ERROR: kind is not installed." ; \
337+ echo " Install it from: https://kind.sigs.k8s.io/docs/user/quick-start/" ; \
338+ exit 1; \
339+ }
340+ @if $(KIND ) get clusters | grep -q " ^$( KIND_CLUSTER) $$ " ; then \
341+ echo " Kind cluster '$( KIND_CLUSTER) ' already exists." ; \
342+ else \
343+ echo " Creating kind cluster '$( KIND_CLUSTER) '..." ; \
344+ $(KIND ) create cluster --name $(KIND_CLUSTER ) ; \
345+ fi
346+ @echo " ==> Exporting kubeconfig to $( KIND_KUBECONFIG) "
347+ @$(KIND ) get kubeconfig --name $(KIND_CLUSTER ) > $(KIND_KUBECONFIG )
348+ @echo " ==> Cluster ready. Use: export KUBECONFIG=$( KIND_KUBECONFIG) "
349+
350+ .PHONY : kind-load
351+ kind-load : container # # Build and load image into kind cluster
352+ @echo " ==> Loading image $( IMG) into kind cluster..."
353+ $(KIND ) load docker-image $(IMG ) --name $(KIND_CLUSTER )
354+
355+ .PHONY : kind-deploy
356+ kind-deploy : kind-up manifests kustomize kind-load # # Deploy operator to kind cluster
357+ @echo " ==> Installing CRDs..."
358+ KUBECONFIG=$(KIND_KUBECONFIG ) $(KUSTOMIZE ) build config/crd | KUBECONFIG=$(KIND_KUBECONFIG ) $(KUBECTL ) apply -f -
359+ @echo " ==> Deploying operator..."
360+ cd config/manager && $(KUSTOMIZE ) edit set image controller=$(IMG )
361+ KUBECONFIG=$(KIND_KUBECONFIG ) $(KUSTOMIZE ) build config/default | KUBECONFIG=$(KIND_KUBECONFIG ) $(KUBECTL ) apply -f -
362+ @echo " ==> Deployment complete!"
363+ @echo " Check status: KUBECONFIG=$( KIND_KUBECONFIG) kubectl get pods -n multigres-operator-system"
364+
365+ .PHONY : kind-redeploy
366+ kind-redeploy : kind-load # # Rebuild image, reload to kind, and restart pods
367+ @echo " ==> Restarting operator pods..."
368+ KUBECONFIG=$(KIND_KUBECONFIG ) $(KUBECTL ) rollout restart deployment -n multigres-operator-system
369+
370+ .PHONY : kind-down
371+ kind-down : # # Delete the kind cluster
372+ @echo " ==> Deleting kind cluster '$( KIND_CLUSTER) '..."
373+ $(KIND ) delete cluster --name $(KIND_CLUSTER )
374+ @rm -f $(KIND_KUBECONFIG )
375+ @echo " ==> Cluster and kubeconfig deleted"
194376
195- # # Tool Versions
196- KUSTOMIZE_VERSION ?= v5.6.0
197- CONTROLLER_TOOLS_VERSION ?= v0.18.0
198- # ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20)
199- ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-% d.% d", $$2, $$3}')
200- # ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
201- ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.% d", $$3}')
202- GOLANGCI_LINT_VERSION ?= v2.3.0
377+ # #@ Dependencies
203378
204379.PHONY : kustomize
205380kustomize : $(KUSTOMIZE ) # # Download kustomize locally if necessary.
@@ -252,3 +427,11 @@ check-coverage:
252427 go test ./... -coverprofile=./cover.out -covermode=atomic -coverpkg=./...
253428 go tool cover -html=cover.out -o=cover.html
254429 echo now open cover.html
430+
431+ # #@ Backward Compatibility Aliases
432+
433+ .PHONY : docker-build
434+ docker-build : container # # Alias for container (backward compatibility)
435+
436+ .PHONY : docker-push
437+ docker-push : container-push # # Alias for container-push (backward compatibility)
0 commit comments