11
22# Image URL to use all building/pushing image targets
33IMG ?= controller:latest
4+
5+ # Version stamped into the standalone CLIs (etcd-migrate, kubectl-etcd) via
6+ # -ldflags. Defaults to `git describe`; the release workflow passes the tag.
7+ VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
8+ CLI_LDFLAGS ?= -X main.version=$(VERSION )
49# ENVTEST_K8S_VERSION is derived from the k8s.io/api version in go.mod so a
510# dependency bump automatically pulls the matching envtest assets — no need
611# to remember to update this in two places. (Pattern stolen from
@@ -42,8 +47,20 @@ help: ## Display this help.
4247# #@ Development
4348
4449.PHONY : manifests
45- manifests : controller-gen # # Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
46- $(CONTROLLER_GEN ) rbac:roleName=manager-role crd webhook paths=" ./..." output:crd:artifacts:config=config/crd/bases
50+ manifests : controller-gen yq # # Generate CRDs and the manager RBAC rules straight into the Helm chart.
51+ # CRDs land in charts/etcd-operator/crd-bases/ (templates/crds.yaml renders
52+ # them, with the helm.sh/resource-policy:keep annotation); the manager
53+ # ClusterRole rules land in charts/etcd-operator/files/ for templates/rbac.yaml
54+ # to pull in via .Files.Get. ci.yml's codegen-drift gate (make manifests +
55+ # git diff) then guards BOTH against drift from the API types and the
56+ # +kubebuilder:rbac markers — no second source of truth, no grep guard.
57+ $(CONTROLLER_GEN ) rbac:roleName=manager-role crd paths=" ./..." \
58+ output:crd:artifacts:config=charts/etcd-operator/crd-bases \
59+ output:rbac:artifacts:config=charts/etcd-operator/files
60+ # controller-gen emits a whole ClusterRole; the chart only needs its rules
61+ # (it wraps them in a release-named, labelled ClusterRole of its own).
62+ $(YQ ) eval ' .rules' charts/etcd-operator/files/role.yaml > charts/etcd-operator/files/manager-role-rules.yaml
63+ rm -f charts/etcd-operator/files/role.yaml
4764
4865.PHONY : generate
4966generate : controller-gen # # Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
@@ -74,7 +91,7 @@ release-smoke: ## Smoke-test the tag-release manifest install path on kind: buil
7491 hack/release-smoke.sh
7592
7693.PHONY : helm-smoke
77- helm-smoke : helm-sync # # Smoke-test the Helm chart install path on kind: build image -> helm install chart -> assert operator Available and a 1-node cluster READY. KEEP_CLUSTER=1 keeps the cluster.
94+ helm-smoke : # # Smoke-test the Helm chart install path on kind: build image -> helm install chart -> assert operator Available and a 1-node cluster READY. KEEP_CLUSTER=1 keeps the cluster.
7895 INSTALL_MODE=helm hack/release-smoke.sh
7996
8097# #@ Build
@@ -85,11 +102,27 @@ build: manifests generate fmt vet ## Build manager binary.
85102
86103.PHONY : kubectl-etcd
87104kubectl-etcd : fmt vet # # Build the kubectl-etcd plugin binary.
88- go build -o bin/kubectl-etcd ./cmd/kubectl-etcd
105+ go build -ldflags " $( CLI_LDFLAGS ) " - o bin/kubectl-etcd ./cmd/kubectl-etcd
89106
90107.PHONY : etcd-migrate
91108etcd-migrate : fmt vet # # Build the etcd-migrate (legacy v1alpha1 -> v1alpha2) CLI binary.
92- go build -o bin/etcd-migrate ./cmd/etcd-migrate
109+ go build -ldflags " $( CLI_LDFLAGS) " -o bin/etcd-migrate ./cmd/etcd-migrate
110+
111+ .PHONY : dist-cli
112+ dist-cli : # # Cross-compile etcd-migrate and kubectl-etcd into dist/ for release (linux/darwin x amd64/arm64). VERSION stamps the binary version.
113+ # Produces the standalone CLIs the release-assets workflow attaches to a
114+ # release, named <cmd>-<os>-<arch>, plus a SHA256 checksum file. These are
115+ # client-side tools (kubectl-etcd is a kubectl plugin, etcd-migrate is an
116+ # admin-run migration CLI), so they ship as binaries, not in the operator image.
117+ mkdir -p dist
118+ for os in linux darwin; do for arch in amd64 arm64; do \
119+ for cmd in etcd-migrate kubectl-etcd; do \
120+ echo " building dist/$$ cmd-$$ os-$$ arch" ; \
121+ CGO_ENABLED=0 GOOS=$$ os GOARCH=$$ arch \
122+ go build -ldflags " $( CLI_LDFLAGS) " -o dist/$$ cmd-$$ os-$$ arch ./cmd/$$ cmd; \
123+ done ; \
124+ done ; done
125+ cd dist && { command -v sha256sum > /dev/null 2>&1 && sha256sum etcd-migrate-* kubectl-etcd-* || shasum -a 256 etcd-migrate-* kubectl-etcd-* ; } > cli-SHA256SUMS.txt
93126
94127.PHONY : run
95128run : manifests generate fmt vet # # Run a controller from your host.
@@ -124,51 +157,46 @@ docker-buildx: test ## Build and push docker image for the manager for cross-pla
124157 rm Dockerfile.cross
125158
126159.PHONY : build-dist-manifests
127- build-dist-manifests : manifests generate kustomize yq # # Render the release install manifests into dist/ for IMG.
128- # Produces the YAMLs the release-assets workflow attaches to a tag:
129- # dist/etcd-operator.yaml – everything (CRDs + deployment)
160+ build-dist-manifests : manifests generate require-helm yq # # Render the release install manifests into dist/ for IMG.
161+ # Produces the YAMLs the release-assets workflow attaches to a tag, for users
162+ # who kubectl-apply instead of helm-install:
163+ # dist/etcd-operator.yaml – everything (Namespace + CRDs + operator)
130164 # dist/etcd-operator.crds.yaml – CRDs only
131165 # dist/etcd-operator.non-crds.yaml – everything except CRDs
132- # Setting the `controller` image also propagates into the manager's
133- # OPERATOR_IMAGE env via the kustomize replacement in config/default, so
134- # the snapshot/restore agent runs the same ref. Pass IMG=<registry>/etcd-operator:<tag>.
166+ # This is just `helm template` of the chart, so the rendered manifest IS the
167+ # chart: the image == OPERATOR_IMAGE wiring and the RBAC come from one source.
168+ # namespace.create emits the Namespace so a bare `kubectl apply -f
169+ # etcd-operator.yaml` is self-contained. Rendering is pure — no tracked file
170+ # is mutated. Pass IMG=<registry>/etcd-operator:<tag>.
135171 mkdir -p dist
136- cd config/manager && $(KUSTOMIZE ) edit set image controller=${IMG}
137- $(KUSTOMIZE ) build config/default > dist/etcd-operator.yaml
138- $(KUSTOMIZE ) build config/default | $(YQ ) eval ' select(.kind != "CustomResourceDefinition")' - > dist/etcd-operator.non-crds.yaml
139- $(KUSTOMIZE ) build config/default | $(YQ ) eval ' select(.kind == "CustomResourceDefinition")' - > dist/etcd-operator.crds.yaml
140-
141- .PHONY : helm-sync
142- helm-sync : manifests # # Vendor the generated CRDs into the Helm chart's crds/ directory.
143- # The chart's RBAC/Deployment templates are hand-authored from config/, but
144- # the CRDs (large, schema-bearing, regenerated by controller-gen) are copied
145- # verbatim so they never drift from the API types. helm-publish.yml re-runs
146- # this before packaging so a published chart always carries current CRDs.
147- mkdir -p charts/etcd-operator/crds
148- cp config/crd/bases/* .yaml charts/etcd-operator/crds/
172+ img=' $(IMG)' ; $(HELM ) template etcd-operator charts/etcd-operator \
173+ --namespace etcd-operator-system \
174+ --set image.repository=" $$ {img%:*}" --set image.tag=" $$ {img##*:}" \
175+ --set namespace.create=true \
176+ > dist/etcd-operator.yaml
177+ $(YQ ) eval ' select(.kind != "CustomResourceDefinition")' dist/etcd-operator.yaml > dist/etcd-operator.non-crds.yaml
178+ $(YQ ) eval ' select(.kind == "CustomResourceDefinition")' dist/etcd-operator.yaml > dist/etcd-operator.crds.yaml
149179
150180# #@ Deployment
151181
152- ifndef ignore-not-found
153- ignore-not-found = false
154- endif
155-
156- .PHONY : install
157- install : manifests kustomize # # Install CRDs into the K8s cluster specified in ~/.kube/config.
158- $(KUSTOMIZE ) build config/crd | kubectl apply -f -
159-
160- .PHONY : uninstall
161- uninstall : manifests kustomize # # Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
162- $(KUSTOMIZE ) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found ) -f -
182+ # The Helm-driven install targets below. The chart is the single source of
183+ # truth for CRDs, RBAC, and the manager Deployment.
184+ HELM_RELEASE ?= etcd-operator
185+ NAMESPACE ?= etcd-operator-system
163186
164187.PHONY : deploy
165- deploy : manifests kustomize # # Deploy controller to the K8s cluster specified in ~/.kube/config.
166- cd config/manager && $(KUSTOMIZE ) edit set image controller=${IMG}
167- $(KUSTOMIZE ) build config/default | kubectl apply -f -
188+ deploy : manifests require-helm # # Install/upgrade the operator (CRDs + RBAC + manager) via Helm. Pass IMG=<registry>/etcd-operator:<tag>.
189+ # The chart renders image == OPERATOR_IMAGE, so there is no separate image-
190+ # replacement step; CRDs are templated into the release so `helm upgrade`
191+ # keeps them current. The IMG split into repository:tag handles registry ports.
192+ img=' $(IMG)' ; $(HELM ) upgrade --install $(HELM_RELEASE ) charts/etcd-operator \
193+ --namespace $(NAMESPACE ) --create-namespace \
194+ --set image.repository=" $$ {img%:*}" --set image.tag=" $$ {img##*:}" \
195+ --wait --timeout 5m
168196
169197.PHONY : undeploy
170- undeploy : # # Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion .
171- $(KUSTOMIZE ) build config/default | kubectl delete --ignore-not-found= $( ignore-not-found ) -f -
198+ undeploy : require-helm # # Uninstall the operator release. CRDs carry resource-policy:keep, so EtcdClusters survive — delete them (and the CRDs) by hand to wipe data .
199+ $(HELM ) uninstall $( HELM_RELEASE ) --namespace $( NAMESPACE )
172200
173201# #@ Build Dependencies
174202
@@ -178,16 +206,17 @@ $(LOCALBIN):
178206 mkdir -p $(LOCALBIN )
179207
180208# # Tool Versions
181- KUSTOMIZE_VERSION ?= v5.6.0
182209CONTROLLER_TOOLS_VERSION ?= v0.18.0
183210YQ_VERSION ?= v4.44.1
184211
185212# # Tool Binaries (version-suffixed so a version bump auto-triggers reinstall
186213# # and stale builds of an old version stay on disk alongside the new one).
187- KUSTOMIZE ?= $(LOCALBIN ) /kustomize-$(KUSTOMIZE_VERSION )
188214CONTROLLER_GEN ?= $(LOCALBIN ) /controller-gen-$(CONTROLLER_TOOLS_VERSION )
189215ENVTEST ?= $(LOCALBIN ) /setup-envtest
190216YQ ?= $(LOCALBIN ) /yq-$(YQ_VERSION )
217+ # Helm is the one tool we don't vendor (no clean `go install`); it must be on
218+ # PATH. release-smoke/e2e and the publish workflows install it via setup-helm.
219+ HELM ?= helm
191220
192221# go-install-tool installs $2@$3 under $1. `go install` drops the binary at
193222# $LOCALBIN/<basename>, so we rename it after install to the version-suffixed
@@ -202,10 +231,12 @@ mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\
202231}
203232endef
204233
205- .PHONY : kustomize
206- kustomize : $(KUSTOMIZE ) # # Download kustomize locally if necessary.
207- $(KUSTOMIZE ) : $(LOCALBIN )
208- $(call go-install-tool,$(KUSTOMIZE ) ,sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION ) )
234+ .PHONY : require-helm
235+ require-helm : # # Assert Helm is on PATH (used by deploy/undeploy/build-dist-manifests).
236+ @command -v $(HELM ) > /dev/null 2>&1 || { \
237+ echo " ERROR: helm not found on PATH. Install Helm v3.16+ (https://helm.sh/docs/intro/install/)." ; \
238+ exit 1; \
239+ }
209240
210241.PHONY : controller-gen
211242controller-gen : $(CONTROLLER_GEN ) # # Download controller-gen locally if necessary.
0 commit comments