...

Text file src/github.com/datawire/ambassador/v2/builder/builder.mk

Documentation: github.com/datawire/ambassador/v2/builder

     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