1# A quick primer on GNU Make syntax
2# =================================
3#
4# This tries to cover the syntax that is hard to ctrl-f for in
5# <https://www.gnu.org/software/make/manual/make.html> (err, hard to
6# C-s for in `M-: (info "Make")`).
7#
8# At the core is a "rule":
9#
10# target: dependency1 dependency2
11# command to run
12#
13# If `target` something that isn't a real file (like 'build', 'lint', or
14# 'test'), then it should be marked as "phony":
15#
16# target: dependency1 dependency2
17# command to run
18# .PHONY: target
19#
20# You can write reusable "pattern" rules:
21#
22# %.o: %.c
23# command to run
24#
25# Of course, if you don't have variables for the inputs and outputs,
26# it's hard to write a "command to run" for a pattern rule. The
27# variables that you should know are:
28#
29# $@ = the target
30# $^ = the list of dependencies (space separated)
31# $< = the first (left-most) dependency
32# $* = the value of the % glob in a pattern rule
33#
34# Each of these have $(@D) and $(@F) variants that are the
35# directory-part and file-part of each value, respectively.
36#
37# I think those are easy enough to remember mnemonically:
38# - $@ is where you shoul direct the output at.
39# - $^ points up at the dependency list
40# - $< points at the left-most member of the dependency list
41# - $* is the % glob; "*" is well-known as the glob char in other languages
42#
43# Make will do its best to guess whether to apply a pattern rule for a
44# given file. Or, you can explicitly tell it by using a 3-field
45# (2-colon) version:
46#
47# foo.o bar.o: %.o: %.c
48# command to run
49#
50# In a non-pattern rule, if there are multiple targets listed, then it
51# is as if rule were duplicated for each target:
52#
53# target1 target2: deps
54# command to run
55#
56# # is the same as
57#
58# target1: deps
59# command to run
60# target2: deps
61# command to run
62#
63# Because of this, if you have a command that generates multiple,
64# outputs, it _must_ be a pattern rule:
65#
66# %.c %.h: %.y
67# command to run
68#
69# Normally, Make crawls the entire tree of dependencies, updating a file
70# if any of its dependencies have been updated. There's a really poorly
71# named feature called "order-only" dependencies:
72#
73# target: normal-deps | order-only-deps
74#
75# Dependencies after the "|" are created if they don't exist, but if
76# they already exist, then don't bother updating them.
77#
78# Tips:
79# -----
80#
81# - Use absolute filenames. It's dumb, but it really does result in
82# fewer headaches. Use $(OSS_HOME) and $(AES_HOME) to spell the
83# absolute filenames.
84#
85# - If you have a multiple-output command where the output files have
86# dissimilar names, have % be just the directory (the above tip makes
87# this easier).
88#
89# - It can be useful to use the 2-colon form of a pattern rule when
90# writing a rule for just one file; it lets you use % and $* to avoid
91# repeating yourself, which can be especially useful with long
92# filenames.
93
94BUILDER_HOME := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
95
96LCNAME := $(shell echo $(NAME) | tr '[:upper:]' '[:lower:]')
97BUILDER_NAME ?= $(LCNAME)
98
99include $(OSS_HOME)/build-aux/prelude.mk
100include $(OSS_HOME)/build-aux/colors.mk
101
102docker.tag.local = $(BUILDER_NAME).local/$(*F)
103docker.tag.remote = $(if $(DEV_REGISTRY),,$(error $(REGISTRY_ERR)))$(DEV_REGISTRY)/$(*F):$(patsubst v%,%,$(VERSION))
104include $(OSS_HOME)/build-aux/docker.mk
105
106include $(OSS_HOME)/build-aux/teleproxy.mk
107
108MODULES :=
109
110module = $(eval MODULES += $(1))$(eval SOURCE_$(1)=$(abspath $(2)))
111
112BUILDER = BUILDER_NAME=$(BUILDER_NAME) $(abspath $(BUILDER_HOME)/builder.sh)
113COPY_GOLD = $(abspath $(BUILDER_HOME)/copy-gold.sh)
114
115AWS_S3_BUCKET ?= datawire-static-files
116
117# the image used for running the Ingress v1 tests with KIND.
118# the current, official image does not support Ingress v1, so we must build our own image with k8s 1.18.
119# build this image with:
120# 1. checkout the Kuberentes sources in a directory like "~/sources/kubernetes"
121# 2. kind build node-image --kube-root ~/sources/kubernetes
122# 3. docker tag kindest/node:latest docker.io/datawire/kindest-node:latest
123# 4. docker push docker.io/datawire/kindest-node:latest
124# This will not be necessary once the KIND images are built for a Kubernetes 1.18 and support Ingress v1beta1 improvements.
125KIND_IMAGE ?= kindest/node:v1.18.0
126#KIND_IMAGE ?= docker.io/datawire/kindest-node:latest
127KIND_KUBECONFIG = /tmp/kind-kubeconfig
128
129# The ingress conformance tests directory
130# build this image with:
131# 1. checkout https://github.com/kubernetes-sigs/ingress-controller-conformance
132# 2. cd ingress-controller-conformance && make image
133# 3. docker tag ingress-controller-conformance:latest docker.io/datawire/ingress-controller-conformance:latest
134# 4. docker push docker.io/datawire/ingress-controller-conformance:latest
135INGRESS_TEST_IMAGE ?= docker.io/datawire/ingress-controller-conformance:latest
136
137# local ports for the Ingress conformance tests
138INGRESS_TEST_LOCAL_PLAIN_PORT = 8000
139INGRESS_TEST_LOCAL_TLS_PORT = 8443
140INGRESS_TEST_LOCAL_ADMIN_PORT = 8877
141
142# directory with the manifests for loading Ambassador for running the Ingress Conformance tests
143# NOTE: these manifests can be slightly different to the regular ones asd they include
144INGRESS_TEST_MANIF_DIR = $(BUILDER_HOME)/../manifests/emissary/
145INGRESS_TEST_MANIFS = emissary-crds.yaml emissary-emissaryns.yaml
146
147# DOCKER_BUILDKIT is _required_ by our Dockerfile, since we use Dockerfile extensions for the
148# Go build cache. See https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md.
149export DOCKER_BUILDKIT := 1
150
151all: help
152.PHONY: all
153
154.NOTPARALLEL:
155
156# the name of the Docker network
157# note: use your local k3d/microk8s/kind network for running tests
158DOCKER_NETWORK ?= $(BUILDER_NAME)
159
160# local host IP address (and not 127.0.0.1)
161ifneq ($(shell which ipconfig 2>/dev/null),)
162 # macOS
163 HOST_IP := $(shell ipconfig getifaddr $$(route get 1.1.1.1 | awk '/interface:/ {print $$2}'))
164else ifneq ($(shell which ip 2>/dev/null),)
165 # modern (iproute2) GNU/Linux
166 #HOST_IP := $(shell ip --json route get to 1.1.1.1 | jq -r '.[0].prefsrc')
167 HOST_IP := $(shell ip route get to 1.1.1.1 | sed -n '1s/.*src \([0-9.]\+\).*/\1/p')
168else
169 $(error I do not know how to get the host IP on this system; it has neither 'ipconfig' (macOS) nor 'ip' (modern GNU/Linux))
170 # ...and I (lukeshu) couldn't figure out a good way to do it on old (net-tools) GNU/Linux.
171endif
172
173GO_ERR = $(RED)ERROR: please update to go 1.13 or newer$(END)
174DOCKER_ERR = $(RED)ERROR: please update to a version of docker built with Go 1.13 or newer$(END)
175
176preflight:
177 @printf "$(CYN)==> $(GRN)Preflight checks$(END)\n"
178
179 @echo "Checking that 'go' is installed and is 1.13 or later"
180 @$(if $(call _prelude.go.VERSION.HAVE,1.13),,printf '%s\n' $(call quote.shell,$(GO_ERR)))
181
182 @echo "Checking that 'docker' is installed and supports the 'slice' function for '--format'"
183 @$(if $(and $(shell which docker 2>/dev/null),\
184 $(call _prelude.go.VERSION.ge,$(patsubst go%,%,$(lastword $(shell go version $$(which docker)))),1.13)),\
185 ,\
186 printf '%s\n' $(call quote.shell,$(DOCKER_ERR)))
187.PHONY: preflight
188
189preflight-cluster: $(tools/kubectl)
190 @test -n "$(DEV_KUBECONFIG)" || (printf "$${KUBECONFIG_ERR}\n"; exit 1)
191 @if [ "$(DEV_KUBECONFIG)" == '-skip-for-release-' ]; then \
192 printf "$(CYN)==> $(RED)Skipping test cluster checks$(END)\n" ;\
193 else \
194 printf "$(CYN)==> $(GRN)Checking for test cluster$(END)\n" ;\
195 success=; \
196 for i in {1..5}; do \
197 $(tools/kubectl) --kubeconfig $(DEV_KUBECONFIG) -n default get service kubernetes > /dev/null && success=true && break || sleep 15 ; \
198 done; \
199 if [ ! "$${success}" ] ; then { printf "$$KUBECTL_ERR\n" ; exit 1; } ; fi; \
200 fi
201.PHONY: preflight-cluster
202
203python/ambassador.version: $(tools/write-ifchanged) FORCE
204 set -e -o pipefail; { \
205 echo $(patsubst v%,%,$(VERSION)); \
206 git rev-parse HEAD; \
207 } | $(tools/write-ifchanged) $@
208clean: python/ambassador.version.rm
209
210# Give Make a hint about which pattern rules to apply. Honestly, I'm
211# not sure why Make isn't figuring it out on its own, but it isn't.
212_images = base-envoy $(LCNAME) kat-client kat-server
213$(foreach i,$(_images), docker/$i.docker.tag.local ): docker/%.docker.tag.local : docker/%.docker
214$(foreach i,$(_images), docker/$i.docker.tag.remote ): docker/%.docker.tag.remote: docker/%.docker
215
216docker/.base-envoy.docker.stamp: FORCE
217 @set -e; { \
218 if docker image inspect $(ENVOY_DOCKER_TAG) --format='{{ .Id }}' >$@ 2>/dev/null; then \
219 printf "${CYN}==> ${GRN}Base Envoy image is already pulled${END}\n"; \
220 else \
221 printf "${CYN}==> ${GRN}Pulling base Envoy image${END}\n"; \
222 TIMEFORMAT=" (docker pull took %1R seconds)"; \
223 time docker pull $(ENVOY_DOCKER_TAG); \
224 unset TIMEFORMAT; \
225 fi; \
226 echo $(ENVOY_DOCKER_TAG) >$@; \
227 }
228clobber: docker/base-envoy.docker.clean
229
230docker/.$(LCNAME).docker.stamp: %/.$(LCNAME).docker.stamp: %/base.docker.tag.local %/base-envoy.docker.tag.local %/base-pip.docker.tag.local python/ambassador.version $(BUILDER_HOME)/Dockerfile $(OSS_HOME)/build-aux/py-version.txt $(tools/dsum) vendor FORCE
231 @printf "${CYN}==> ${GRN}Building image ${BLU}$(LCNAME)${END}\n"
232 @printf " ${BLU}base=$$(sed -n 2p $*/base.docker.tag.local)${END}\n"
233 @printf " ${BLU}envoy=$$(cat $*/base-envoy.docker)${END}\n"
234 @printf " ${BLU}builderbase=$$(sed -n 2p $*/base-pip.docker.tag.local)${END}\n"
235 { $(tools/dsum) '$(LCNAME) build' 3s \
236 docker build -f ${BUILDER_HOME}/Dockerfile . \
237 --platform="$(BUILD_ARCH)" \
238 --build-arg=base="$$(sed -n 2p $*/base.docker.tag.local)" \
239 --build-arg=envoy="$$(cat $*/base-envoy.docker)" \
240 --build-arg=builderbase="$$(sed -n 2p $*/base-pip.docker.tag.local)" \
241 --build-arg=py_version="$$(cat build-aux/py-version.txt)" \
242 --iidfile=$@; }
243clean: docker/$(LCNAME).docker.clean
244
245REPO=$(BUILDER_NAME)
246
247images: docker/$(LCNAME).docker.tag.local
248images: docker/kat-client.docker.tag.local
249images: docker/kat-server.docker.tag.local
250.PHONY: images
251
252REGISTRY_ERR = $(RED)
253REGISTRY_ERR += $(NL)ERROR: please set the DEV_REGISTRY make/env variable to the docker registry
254REGISTRY_ERR += $(NL) you would like to use for development
255REGISTRY_ERR += $(END)
256
257push: docker/$(LCNAME).docker.push.remote
258push: docker/kat-client.docker.push.remote
259push: docker/kat-server.docker.push.remote
260.PHONY: push
261
262# `make push-dev` is meant to be run by CI.
263push-dev: docker/$(LCNAME).docker.tag.local
264 @[[ '$(VERSION)' == *-* ]] || (echo "$(RED)$@: VERSION=$(VERSION) is not a pre-release version$(END)" >&2; exit 1)
265
266 @printf '$(CYN)==> $(GRN)pushing $(BLU)%s$(GRN) as $(BLU)$(GRN)...$(END)\n' '$(LCNAME)' '$(DEV_REGISTRY)/$(LCNAME):$(patsubst v%,%,$(VERSION))'
267 docker tag $$(cat docker/$(LCNAME).docker) '$(DEV_REGISTRY)/$(LCNAME):$(patsubst v%,%,$(VERSION))'
268 docker push '$(DEV_REGISTRY)/$(LCNAME):$(patsubst v%,%,$(VERSION))'
269.PHONY: push-dev
270
271export KUBECONFIG_ERR=$(RED)ERROR: please set the $(BLU)DEV_KUBECONFIG$(RED) make/env variable to the cluster\n you would like to use for development. Note this cluster must have access\n to $(BLU)DEV_REGISTRY$(RED) (currently $(BLD)$(DEV_REGISTRY)$(END)$(RED))$(END)
272export KUBECTL_ERR=$(RED)ERROR: preflight kubectl check failed$(END)
273
274test-ready: push preflight-cluster
275.PHONY: test-ready
276
277PYTEST_ARGS ?=
278export PYTEST_ARGS
279
280PYTEST_GOLD_DIR ?= $(abspath python/tests/gold)
281
282pytest: push-pytest-images
283pytest: $(tools/kubestatus)
284pytest: $(tools/kubectl)
285pytest: $(OSS_HOME)/venv
286pytest: build-output/bin/envoy
287pytest: proxy
288 @printf "$(CYN)==> $(GRN)Running $(BLU)py$(GRN) tests$(END)\n"
289 @echo "AMBASSADOR_DOCKER_IMAGE=$$AMBASSADOR_DOCKER_IMAGE"
290 @echo "DEV_KUBECONFIG=$$DEV_KUBECONFIG"
291 @echo "KAT_RUN_MODE=$$KAT_RUN_MODE"
292 @echo "PYTEST_ARGS=$$PYTEST_ARGS"
293 mkdir -p $(or $(TEST_XML_DIR),/tmp/test-data)
294 set -e; { \
295 . $(OSS_HOME)/venv/bin/activate; \
296 export SOURCE_ROOT=$(CURDIR); \
297 export ENVOY_PATH=$(CURDIR)/build-output/bin/envoy; \
298 export KUBESTATUS_PATH=$(CURDIR)/tools/bin/kubestatus; \
299 pytest --cov-branch --cov=ambassador --cov-report html:/tmp/cov_html --junitxml=$(or $(TEST_XML_DIR),/tmp/test-data)/pytest.xml --tb=short -rP $(PYTEST_ARGS); \
300 }
301.PHONY: pytest
302
303pytest-unit: build-output/bin/envoy $(OSS_HOME)/venv
304 @printf "$(CYN)==> $(GRN)Running $(BLU)py$(GRN) unit tests$(END)\n"
305 mkdir -p $(or $(TEST_XML_DIR),/tmp/test-data)
306 set -e; { \
307 . $(OSS_HOME)/venv/bin/activate; \
308 export SOURCE_ROOT=$(CURDIR); \
309 export ENVOY_PATH=$(CURDIR)/build-output/bin/envoy; \
310 pytest --cov-branch --cov=ambassador --cov-report html:/tmp/cov_html --junitxml=$(or $(TEST_XML_DIR),/tmp/test-data)/pytest.xml --tb=short -rP $(PYTEST_ARGS) python/tests/unit; \
311 }
312.PHONY: pytest-unit
313
314pytest-integration: push-pytest-images
315 @printf "$(CYN)==> $(GRN)Running $(BLU)py$(GRN) integration tests$(END)\n"
316 $(MAKE) pytest PYTEST_ARGS="$$PYTEST_ARGS python/tests/integration"
317.PHONY: pytest-integration
318
319pytest-kat-local: push-pytest-images
320 $(MAKE) pytest PYTEST_ARGS="$$PYTEST_ARGS python/tests/kat"
321
322pytest-kat-envoy2: push-pytest-images # doing this all at once is too much for CI...
323 $(MAKE) pytest KAT_RUN_MODE=envoy AMBASSADOR_ENVOY_API_VERSION=V2 PYTEST_ARGS="$$PYTEST_ARGS python/tests/kat"
324# ... so we have a separate rule to run things split up
325build-aux/.pytest-kat-envoy2.txt.stamp: $(OSS_HOME)/venv push-pytest-images FORCE
326 . venv/bin/activate && set -o pipefail && AMBASSADOR_ENVOY_API_VERSION=V2 pytest --collect-only python/tests/kat 2>&1 | sed -En 's/.*<Function (.*)>/\1/p' | cut -d. -f1 | sort -u > $@
327build-aux/pytest-kat-envoy2.txt: build-aux/%: build-aux/.%.stamp $(tools/copy-ifchanged)
328 $(tools/copy-ifchanged) $< $@
329clean: build-aux/.pytest-kat-envoy2.txt.stamp.rm build-aux/pytest-kat-envoy2.txt.rm
330pytest-kat-envoy2-%: build-aux/pytest-kat-envoy2.txt $(tools/py-split-tests)
331 $(MAKE) pytest KAT_RUN_MODE=envoy AMBASSADOR_ENVOY_API_VERSION=V2 PYTEST_ARGS="$$PYTEST_ARGS -k '$$($(tools/py-split-tests) $(subst -of-, ,$*) <build-aux/pytest-kat-envoy2.txt)' python/tests/kat"
332
333pytest-kat-envoy3: push-pytest-images # doing this all at once is too much for CI...
334 $(MAKE) pytest KAT_RUN_MODE=envoy PYTEST_ARGS="$$PYTEST_ARGS python/tests/kat"
335# ... so we have a separate rule to run things split up
336build-aux/.pytest-kat-envoy3.txt.stamp: $(OSS_HOME)/venv push-pytest-images FORCE
337 . venv/bin/activate && set -o pipefail && pytest --collect-only python/tests/kat 2>&1 | sed -En 's/.*<Function (.*)>/\1/p' | cut -d. -f1 | sort -u > $@
338build-aux/pytest-kat-envoy3.txt: build-aux/%: build-aux/.%.stamp $(tools/copy-ifchanged)
339 $(tools/copy-ifchanged) $< $@
340clean: build-aux/.pytest-kat-envoy3.txt.stamp.rm build-aux/pytest-kat-envoy3.txt.rm
341pytest-kat-envoy3-%: build-aux/pytest-kat-envoy3.txt $(tools/py-split-tests)
342 $(MAKE) pytest KAT_RUN_MODE=envoy PYTEST_ARGS="$$PYTEST_ARGS -k '$$($(tools/py-split-tests) $(subst -of-, ,$*) <build-aux/pytest-kat-envoy3.txt)' python/tests/kat"
343
344.PHONY: pytest-kat-%
345
346build-output/bin/envoy: docker/base-envoy.docker.tag.local
347 mkdir -p $(@D)
348 { \
349 echo '#!/bin/bash'; \
350 echo "docker run --rm -v $(OSS_HOME):$(OSS_HOME) -v /var/:/var/ -v /tmp/:/tmp/ -t --entrypoint /usr/local/bin/envoy-static-stripped $$(cat docker/base-envoy.docker) \"\$$@\""; \
351 } > $@
352 chmod +x $@
353
354pytest-gold:
355 sh $(COPY_GOLD) $(PYTEST_GOLD_DIR)
356
357$(OSS_HOME)/venv: python/requirements.txt python/requirements-dev.txt
358 rm -rf $@
359 python3 -m venv $@
360 $@/bin/pip3 install -r python/requirements.txt
361 $@/bin/pip3 install -r python/requirements-dev.txt
362 $@/bin/pip3 install -e $(OSS_HOME)/python
363clobber: venv.rm-r
364
365GOTEST_ARGS ?= -race -count=1 -timeout 30m
366GOTEST_ARGS += -parallel=150 # The ./pkg/envoy-control-plane/cache/v{2,3}/ tests require high parallelism to reliably work
367GOTEST_PKGS ?= ./...
368gotest: $(OSS_HOME)/venv $(tools/kubectl)
369 @printf "$(CYN)==> $(GRN)Running $(BLU)go$(GRN) tests$(END)\n"
370 { . $(OSS_HOME)/venv/bin/activate && \
371 export PATH=$(tools.bindir):$${PATH} && \
372 export EDGE_STACK=$(GOTEST_AES_ENABLED) && \
373 go test $(GOTEST_ARGS) $(GOTEST_PKGS); }
374.PHONY: gotest
375
376# Ingress v1 conformance tests, using KIND and the Ingress Conformance Tests suite.
377ingresstest: $(tools/kubectl) | docker/$(LCNAME).docker.push.remote
378 @printf "$(CYN)==> $(GRN)Running $(BLU)Ingress v1$(GRN) tests$(END)\n"
379 @[ -n "$(INGRESS_TEST_IMAGE)" ] || { printf "$(RED)ERROR: no INGRESS_TEST_IMAGE defined$(END)\n"; exit 1; }
380 @[ -n "$(INGRESS_TEST_MANIF_DIR)" ] || { printf "$(RED)ERROR: no INGRESS_TEST_MANIF_DIR defined$(END)\n"; exit 1; }
381 @[ -d "$(INGRESS_TEST_MANIF_DIR)" ] || { printf "$(RED)ERROR: $(INGRESS_TEST_MANIF_DIR) does not seem a valid directory$(END)\n"; exit 1; }
382 @[ -n "$(HOST_IP)" ] || { printf "$(RED)ERROR: no IP obtained for host$(END)\n"; ip addr ; exit 1; }
383
384 @printf "$(CYN)==> $(GRN)Creating/recreating KIND cluster with image $(KIND_IMAGE)$(END)\n"
385 @for i in {1..5} ; do \
386 kind delete cluster 2>/dev/null || true ; \
387 kind create cluster --image $(KIND_IMAGE) && break || sleep 10 ; \
388 done
389
390 @printf "$(CYN)==> $(GRN)Saving KUBECONFIG at $(KIND_KUBECONFIG)$(END)\n"
391 @kind get kubeconfig > $(KIND_KUBECONFIG)
392 @sleep 10
393
394 @APISERVER_IP=`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' kind-control-plane` ; \
395 [ -n "$$APISERVER_IP" ] || { printf "$(RED)ERROR: no IP obtained for API server$(END)\n"; docker ps ; docker inspect kind-control-plane ; exit 1; } ; \
396 printf "$(CYN)==> $(GRN)API server at $$APISERVER_IP. Fixing server in $(KIND_KUBECONFIG).$(END)\n" ; \
397 sed -i -e "s|server: .*|server: https://$$APISERVER_IP:6443|g" $(KIND_KUBECONFIG)
398
399 @printf "$(CYN)==> $(GRN)Showing some cluster info:$(END)\n"
400 @$(tools/kubectl) --kubeconfig=$(KIND_KUBECONFIG) cluster-info || { printf "$(RED)ERROR: kubernetes cluster not ready $(END)\n"; exit 1 ; }
401 @$(tools/kubectl) --kubeconfig=$(KIND_KUBECONFIG) version || { printf "$(RED)ERROR: kubernetes cluster not ready $(END)\n"; exit 1 ; }
402
403 @printf "$(CYN)==> $(GRN)Loading Ambassador (from the Ingress conformance tests) with image=$$(sed -n 2p docker/$(LCNAME).docker.push.remote)$(END)\n"
404 @for f in $(INGRESS_TEST_MANIFS) ; do \
405 printf "$(CYN)==> $(GRN)... $$f $(END)\n" ; \
406 cat $(INGRESS_TEST_MANIF_DIR)/$$f | sed -e "s|image:.*ambassador\:.*|image: $$(sed -n 2p docker/$(LCNAME).docker.push.remote)|g" | tee /dev/tty | $(tools/kubectl) apply -f - ; \
407 done
408
409 @printf "$(CYN)==> $(GRN)Waiting for Ambassador to be ready$(END)\n"
410 @$(tools/kubectl) --kubeconfig=$(KIND_KUBECONFIG) wait --for=condition=available --timeout=180s deployment/ambassador || { \
411 printf "$(RED)ERROR: Ambassador was not ready after 3 mins $(END)\n"; \
412 $(tools/kubectl) --kubeconfig=$(KIND_KUBECONFIG) get services --all-namespaces ; \
413 exit 1 ; }
414
415 @printf "$(CYN)==> $(GRN)Exposing Ambassador service$(END)\n"
416 @$(tools/kubectl) --kubeconfig=$(KIND_KUBECONFIG) expose deployment ambassador --type=LoadBalancer --name=ambassador
417
418 @printf "$(CYN)==> $(GRN)Starting the tests container (in the background)$(END)\n"
419 @docker stop -t 3 ingress-tests 2>/dev/null || true && docker rm ingress-tests 2>/dev/null || true
420 @docker run -d --rm --name ingress-tests -e KUBECONFIG=/opt/.kube/config --mount type=bind,source=$(KIND_KUBECONFIG),target=/opt/.kube/config \
421 --entrypoint "/bin/sleep" $(INGRESS_TEST_IMAGE) 600
422
423 @printf "$(CYN)==> $(GRN)Loading the Ingress conformance tests manifests$(END)\n"
424 @docker exec -ti ingress-tests \
425 /opt/ingress-controller-conformance apply --api-version=networking.k8s.io/v1beta1 --ingress-controller=getambassador.io/ingress-controller --ingress-class=ambassador
426 @sleep 10
427
428 @printf "$(CYN)==> $(GRN)Forwarding traffic to Ambassador service$(END)\n"
429 @$(tools/kubectl) --kubeconfig=$(KIND_KUBECONFIG) port-forward --address=$(HOST_IP) svc/ambassador \
430 $(INGRESS_TEST_LOCAL_PLAIN_PORT):8080 $(INGRESS_TEST_LOCAL_TLS_PORT):8443 $(INGRESS_TEST_LOCAL_ADMIN_PORT):8877 &
431 @sleep 10
432
433 @for url in "http://$(HOST_IP):$(INGRESS_TEST_LOCAL_PLAIN_PORT)" "https://$(HOST_IP):$(INGRESS_TEST_LOCAL_TLS_PORT)" "http://$(HOST_IP):$(INGRESS_TEST_LOCAL_ADMIN_PORT)/ambassador/v0/check_ready" ; do \
434 printf "$(CYN)==> $(GRN)Waiting until $$url is ready...$(END)\n" ; \
435 until curl --silent -k "$$url" ; do printf "$(CYN)==> $(GRN)... still waiting.$(END)\n" ; sleep 2 ; done ; \
436 printf "$(CYN)==> $(GRN)... $$url seems to be ready.$(END)\n" ; \
437 done
438 @sleep 30
439
440 @printf "$(CYN)==> $(GRN)Running the Ingress conformance tests against $(HOST_IP)$(END)\n"
441 @docker exec -ti ingress-tests \
442 /opt/ingress-controller-conformance verify \
443 --api-version=networking.k8s.io/v1beta1 \
444 --use-insecure-host=$(HOST_IP):$(INGRESS_TEST_LOCAL_PLAIN_PORT) \
445 --use-secure-host=$(HOST_IP):$(INGRESS_TEST_LOCAL_TLS_PORT)
446
447 @printf "$(CYN)==> $(GRN)Cleaning up...$(END)\n"
448 -@pkill $(tools/kubectl) -9
449 @docker stop -t 3 ingress-tests 2>/dev/null || true && docker rm ingress-tests 2>/dev/null || true
450
451 @if [ -n "$(CLEANUP)" ] ; then \
452 printf "$(CYN)==> $(GRN)We are done. Destroying the cluster now.$(END)\n"; kind delete cluster || true; \
453 else \
454 printf "$(CYN)==> $(GRN)We are done. You should destroy the cluster with 'kind delete cluster'.$(END)\n"; \
455 fi
456
457test: ingresstest gotest pytest
458.PHONY: test
459
460AMB_IMAGE_RC=$(RELEASE_REGISTRY)/$(REPO):$(patsubst v%,%,$(VERSION))
461AMB_IMAGE_RELEASE=$(RELEASE_REGISTRY)/$(REPO):$(patsubst v%,%,$(VERSION))
462
463export RELEASE_REGISTRY_ERR=$(RED)ERROR: please set the RELEASE_REGISTRY make/env variable to the docker registry\n you would like to use for release$(END)
464
465release/promote-oss/.main: $(tools/docker-promote)
466 @[[ '$(PROMOTE_FROM_VERSION)' =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$$ ]] || (echo >&2 'Must set PROMOTE_FROM_VERSION to a vSEMVER value'; exit 1)
467 @[[ '$(PROMOTE_TO_VERSION)' =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$$ ]] || (echo >&2 'Must set PROMOTE_TO_VERSION to a vSEMVER value' ; exit 1)
468 @[[ -n '$(PROMOTE_FROM_REPO)' ]] || (echo >&2 'Must set PROMOTE_FROM_REPO' ; exit 1)
469 @[[ -n '$(PROMOTE_TO_REPO)' ]] || (echo >&2 'Must set PROMOTE_TO_REPO' ; exit 1)
470 @case '$(PROMOTE_CHANNEL)' in \
471 ''|wip|early|test|hotfix) true;; \
472 *) echo >&2 'Unknown PROMOTE_CHANNEL $(PROMOTE_CHANNEL)'; exit 1;; \
473 esac
474
475 @printf "$(CYN)==> $(GRN)Promoting $(BLU)%s$(GRN) to $(BLU)%s$(GRN) (channel='$(BLU)%s$(GRN)')$(END)\n" '$(PROMOTE_FROM_VERSION)' '$(PROMOTE_TO_VERSION)' '$(PROMOTE_CHANNEL)'
476
477 @printf ' pushing $(CYN)$(PROMOTE_TO_REPO):$(patsubst v%,%,$(PROMOTE_FROM_VERSION))$(END)...\n'
478 $(tools/docker-promote) $(PROMOTE_FROM_REPO):$(patsubst v%,%,$(PROMOTE_FROM_VERSION)) $(PROMOTE_TO_REPO):$(patsubst v%,%,$(PROMOTE_TO_VERSION))
479 docker push $(PROMOTE_TO_REPO):$(patsubst v%,%,$(PROMOTE_TO_VERSION))
480
481ifneq ($(IS_PRIVATE),)
482 @echo '$@: not pushing to S3 because this is a private repo'
483else
484 @printf ' pushing $(CYN)https://s3.amazonaws.com/$(AWS_S3_BUCKET)/emissary-ingress/$(PROMOTE_CHANNEL)stable.txt$(END)...\n'
485 printf '%s' "$(patsubst v%,%,$(PROMOTE_TO_VERSION))" | aws s3 cp - s3://$(AWS_S3_BUCKET)/emissary-ingress/$(PROMOTE_CHANNEL)stable.txt
486
487 @printf ' pushing $(CYN)s3://scout-datawire-io/emissary-ingress/$(PROMOTE_CHANNEL)app.json$(END)...\n'
488 printf '{"application":"emissary","latest_version":"%s","notices":[]}' "$(patsubst v%,%,$(PROMOTE_TO_VERSION))" | aws s3 cp - s3://scout-datawire-io/emissary-ingress/$(PROMOTE_CHANNEL)app.json
489
490 { $(MAKE) \
491 push-manifests \
492 publish-docs-yaml; }
493endif
494.PHONY: release/promote-oss/.main
495
496release/promote-oss/to-rc: $(tools/devversion)
497 @test -n "$(RELEASE_REGISTRY)" || (printf "$${RELEASE_REGISTRY_ERR}\n"; exit 1)
498 @[[ "$(VERSION)" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+|-dev)$$ ]] || (printf '$(RED)ERROR: VERSION=%s does not look like an RC or dev tag\n' "$(VERSION)"; exit 1)
499 @set -e; { \
500 dev_version=$$($(tools/devversion)); \
501 printf "$(CYN)==> $(GRN)found version $(BLU)$$dev_version$(GRN).$(END)\n"; \
502 $(MAKE) release/promote-oss/.main \
503 PROMOTE_FROM_VERSION="$$dev_version" \
504 PROMOTE_TO_VERSION='$(VERSION)' \
505 PROMOTE_FROM_REPO='$(DEV_REGISTRY)/$(REPO)' \
506 PROMOTE_TO_REPO='$(RELEASE_REGISTRY)/$(REPO)' \
507 PROMOTE_CHANNEL='test'; \
508 }
509.PHONY: release/promote-oss/to-rc
510
511# To be run from a checkout at the tag you are promoting _from_.
512# This is normally run from CI by creating the GA tag.
513release/promote-oss/to-ga: $(tools/devversion)
514 @test -n "$(RELEASE_REGISTRY)" || (printf "$${RELEASE_REGISTRY_ERR}\n"; exit 1)
515 @[[ "$(VERSION)" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-ea)?$$ ]] || (printf '$(RED)ERROR: VERSION=%s does not look like a GA tag\n' "$(VERSION)"; exit 1)
516 @set -e; { \
517 dev_version=$$($(tools/devversion)); \
518 printf "$(CYN)==> $(GRN)found version $(BLU)$$dev_version$(GRN).$(END)\n"; \
519 $(MAKE) release/promote-oss/.main \
520 PROMOTE_FROM_VERSION="$$dev_version" \
521 PROMOTE_TO_VERSION='$(VERSION)' \
522 PROMOTE_FROM_REPO='$(DEV_REGISTRY)/$(REPO)' \
523 PROMOTE_TO_REPO='$(RELEASE_REGISTRY)/$(REPO)' \
524 PROMOTE_CHANNEL=''; \
525 }
526.PHONY: release/promote-oss/to-ga
527
528# `make release/ga-check` is meant to be run by a human maintainer to
529# check that CI did all the right things.
530release/ga-check:
531 { $(OSS_HOME)/releng/release-ga-check \
532 --ga-version=$(patsubst v%,%,$(VERSION)) \
533 --chart-version=$(patsubst v%,%,$(CHART_VERSION)) \
534 --source-registry=$(RELEASE_REGISTRY) \
535 --image-name=$(LCNAME); }
536
537AMBASSADOR_DOCKER_IMAGE = $(shell sed -n 2p docker/$(LCNAME).docker.push.remote 2>/dev/null)
538export AMBASSADOR_DOCKER_IMAGE
539
540_user-vars = BUILDER_NAME
541_user-vars += DEV_KUBECONFIG
542_user-vars += DEV_REGISTRY
543_user-vars += RELEASE_REGISTRY
544_user-vars += AMBASSADOR_DOCKER_IMAGE
545env:
546 @printf '$(BLD)%s$(END)=$(BLU)%s$(END)\n' $(foreach v,$(_user-vars), $v $(call quote.shell,$(call quote.shell,$($v))) )
547.PHONY: env
548
549export:
550 @printf 'export %s=%s\n' $(foreach v,$(_user-vars), $v $(call quote.shell,$(call quote.shell,$($v))) )
551.PHONY: export
552
553help:
554 @printf '%s\n' $(call quote.shell,$(_help.intro))
555.PHONY: help
556
557targets:
558 @printf '%s\n' $(call quote.shell,$(HELP_TARGETS))
559.PHONY: help
560
561define HELP_TARGETS
562$(BLD)Targets:$(END)
563
564$(_help.targets)
565
566$(BLD)Codebases:$(END)
567 $(foreach MODULE,$(MODULES),$(NL) $(BLD)$(SOURCE_$(MODULE)) ==> $(BLU)$(MODULE)$(END))
568
569endef
570
571# Style note: _help.intro
572# - is wrapped to 72 columns (after stripping the ANSI color codes)
573# - has sentences separated with 2 spaces
574# - uses bold blue ("$(BLU)") when introducing a new variable
575# - uses bold ("$(BLD)") for variables that have already been introduced
576# - uses bold ("$(BLD)") when you would use `backticks` in markdown
577define _help.intro
578This Makefile builds Ambassador using a standard build environment
579inside a Docker container. The $(BLD)$(REPO)$(END), $(BLD)kat-server$(END), and $(BLD)kat-client$(END)
580images are created from this container after the build stage is
581finished.
582
583The build works by maintaining a running build container in the
584background. It gets source code into that container via $(BLD)rsync$(END). The
585$(BLD)/home/dw$(END) directory in this container is a Docker volume, which allows
586files (e.g. the Go build cache and $(BLD)pip$(END) downloads) to be cached across
587builds.
588
589This arrangement also permits building multiple codebases. This is
590useful for producing builds with extended functionality. Each external
591codebase is synced into the container at the $(BLD)/buildroot/<name>$(END) path.
592
593You can control the name of the container and the images it builds by
594setting $(BLU)$$BUILDER_NAME$(END), which defaults to $(BLD)$(LCNAME)$(END). Note well that if
595you want to make multiple clones of this repo and build in more than one
596of them at the same time, you $(BLD)must$(END) set $(BLD)$$BUILDER_NAME$(END) so that each clone
597has its own builder! If you do not do this, your builds will collide
598with confusing results.
599
600The build system doesn't try to magically handle all dependencies. In
601general, if you change something that is not pure source code, you will
602likely need to do a $(BLD)$(MAKE) clean$(END) in order to see the effect. For example,
603Python code only gets set up once, so if you change $(BLD)setup.py$(END), then you
604will need to do a clean build to see the effects. Assuming you didn't
605$(BLD)$(MAKE) clobber$(END), this shouldn't take long due to the cache in the Docker
606volume.
607
608All targets that deploy to a cluster by way of $(BLU)$$DEV_REGISTRY$(END) can be made
609to have the cluster use an imagePullSecret to pull from $(BLD)$$DEV_REGISTRY$(END),
610by setting $(BLU)$$DEV_USE_IMAGEPULLSECRET$(END) to a non-empty value. The
611imagePullSecret will be constructed from $(BLD)$$DEV_REGISTRY$(END),
612$(BLU)$$DOCKER_BUILD_USERNAME$(END), and $(BLU)$$DOCKER_BUILD_PASSWORD$(END).
613
614Use $(BLD)$(MAKE) $(BLU)targets$(END) for help about available $(BLD)make$(END) targets.
615endef
616
617define _help.targets
618 $(BLD)$(MAKE) $(BLU)help$(END) -- displays the main help message.
619
620 $(BLD)$(MAKE) $(BLU)targets$(END) -- displays this message.
621
622 $(BLD)$(MAKE) $(BLU)env$(END) -- display the value of important env vars.
623
624 $(BLD)$(MAKE) $(BLU)export$(END) -- display important env vars in shell syntax, for use with $(BLD)eval$(END).
625
626 $(BLD)$(MAKE) $(BLU)preflight$(END) -- checks dependencies of this makefile.
627
628 $(BLD)$(MAKE) $(BLU)version$(END) -- display source code version.
629
630 $(BLD)$(MAKE) $(BLU)images$(END) -- creates images from the build container.
631
632 $(BLD)$(MAKE) $(BLU)push$(END) -- pushes images to $(BLD)$$DEV_REGISTRY$(END). ($(DEV_REGISTRY))
633
634 $(BLD)$(MAKE) $(BLU)test$(END) -- runs Go and Python tests inside the build container.
635
636 The tests require a Kubernetes cluster and a Docker registry in order to
637 function. These must be supplied via the $(BLD)$(MAKE)$(END)/$(BLD)env$(END) variables $(BLD)$$DEV_KUBECONFIG$(END)
638 and $(BLD)$$DEV_REGISTRY$(END).
639
640 $(BLD)$(MAKE) $(BLU)gotest$(END) -- runs just the Go tests inside the build container.
641
642 Use $(BLD)$$GOTEST_PKGS$(END) to control which packages are passed to $(BLD)gotest$(END). ($(GOTEST_PKGS))
643 Use $(BLD)$$GOTEST_ARGS$(END) to supply additional non-package arguments. ($(GOTEST_ARGS))
644 Example: $(BLD)$(MAKE) gotest GOTEST_PKGS=./cmd/entrypoint GOTEST_ARGS=-v$(END) # run entrypoint tests verbosely
645
646 $(BLD)$(MAKE) $(BLU)pytest$(END) -- runs just the Python tests inside the build container.
647
648 Use $(BLD)$$KAT_RUN_MODE=envoy$(END) to force the Python tests to ignore local caches, and run everything
649 in the cluster.
650
651 Use $(BLD)$$KAT_RUN_MODE=local$(END) to force the Python tests to ignore the cluster, and only run tests
652 with a local cache.
653
654 Use $(BLD)$$PYTEST_ARGS$(END) to pass args to $(BLD)pytest$(END). ($(PYTEST_ARGS))
655
656 Example: $(BLD)$(MAKE) pytest KAT_RUN_MODE=envoy PYTEST_ARGS="-k Lua"$(END) # run only the Lua test, with a real Envoy
657
658 $(BLD)$(MAKE) $(BLU)pytest-gold$(END) -- update the gold files for the pytest cache
659
660 $(BLD)$(MAKE) $(BLU)pytest$(END) uses a local cache to speed up tests. $(BLD)ONCE YOU HAVE SUCCESSFULLY
661 RUN TESTS WITH $(BLU)KAT_RUN_MODE=envoy$(END), you can use $(BLD)$(MAKE) $(BLU)pytest-gold$(END) to update the
662 caches for the passing tests.
663
664 $(BLD)DO NOT$(END) run $(BLD)$(MAKE) $(BLU)pytest-gold$(END) if you have failing tests.
665
666 $(BLD)$(MAKE) $(BLU)release/promote-oss/to-ga$(END) -- promote a release candidate to general availability
667
668 The current commit must be tagged for this to work, and your tree must be clean.
669 Additionally, the tag must be of the form 'vX.Y.Z'. You must also have previously
670 built and promoted the RC that will become GA, using $(BLD)release/bits$(END).
671
672 $(BLD)$(MAKE) $(BLU)clean$(END) -- kills the build container.
673
674 $(BLD)$(MAKE) $(BLU)clobber$(END) -- kills the build container and the cache volume.
675
676 $(BLD)$(MAKE) $(BLU)generate$(END) -- update generated files that get checked in to Git.
677
678 1. Use $(BLD)$$ENVOY_COMMIT$(END) to update the vendored gRPC protobuf files ('api/envoy').
679 2. Run 'protoc' to generate things from the protobuf files (both those from
680 Envoy, and those from 'api/kat').
681 3. Use $(BLD)$$ENVOY_GO_CONTROL_PLANE_COMMIT$(END) to update the vendored+patched copy of
682 envoyproxy/go-control-plane ('pkg/envoy-control-plane/').
683 4. Use the Go CRD definitions in 'pkg/api/getambassador.io/' to generate YAML
684 (and a few 'zz_generated.*.go' files).
685
686 $(BLD)$(MAKE) $(BLU)generate-fast$(END) -- like $(BLD)make generate$(END), but skips the slow Envoy stuff.
687
688 $(BLD)$(MAKE) $(BLU)go-mod-tidy$(END) -- 'go mod tidy', but plays nice with 'make generate'
689
690 $(BLD)$(MAKE) $(BLU)guess-envoy-go-control-plane-commit$(END) -- Make a suggestion for setting ENVOY_GO_CONTROL_PLANE_COMMIT= in generate.mk
691
692 $(BLD)$(MAKE) $(BLU)lint$(END) -- runs golangci-lint.
693
694 $(BLD)$(MAKE) $(BLU)format$(END) -- runs golangci-lint with --fix.
695
696endef
View as plain text