# novox/hq ADR 0006 — the control plane, in Go. # # The image the bundle pins holds the program and nothing else, so the build is static and the # container is built FROM scratch. That is not a size optimisation: this image is fetched by # digest and run on a machine where no mesh exists to check anything, and everything in it is # something a person would have to audit. VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo development) LDFLAGS := -s -w -X main.version=$(VERSION) # Where `make check` raises PostgreSQL. A high port and a throwaway container: nothing here # touches a database anybody else is using. Override PG_PORT if this one is taken -- the first # port chosen was already serving something that had been up for six days. PG_PORT ?= 55532 PG_CONTAINER ?= mesh-controller-check PG_IMAGE ?= postgres:17-alpine export MESH_TEST_POSTGRES ?= postgres://postgres:check@127.0.0.1:$(PG_PORT)/postgres?sslmode=disable .PHONY: build image check test vet fmt postgres postgres-stop clean build: CGO_ENABLED=0 go build -trimpath -ldflags '$(LDFLAGS)' -o build/mesh-controller ./cmd/mesh-controller # Tagged 'development' as well as by version, because the lab places images by name and a # scenario naming a version would have to be edited on every build. The version tag is what a # real bundle pins. IMAGE ?= mesh-controller:$(VERSION) DEV_TAG ?= mesh-controller:development image: docker build --build-arg VERSION=$(VERSION) -t $(IMAGE) -t $(DEV_TAG) . @echo @docker image inspect $(IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' # The builder ships as an image too, because it is a module the mesh assigns rather than a program # somebody starts on a machine by hand. BUILDER_IMAGE ?= mesh-builder:$(VERSION) BUILDER_DEV_TAG ?= mesh-builder:development builder-image: docker build -f cmd/mesh-builder/Dockerfile -t $(BUILDER_IMAGE) -t $(BUILDER_DEV_TAG) . @echo @docker image inspect $(BUILDER_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' # The provisioner ships as an image too, because it is the thing that makes a sealed credential # true on a machine -- and the mesh cannot, having discarded the plaintext. PROVISIONER_IMAGE ?= mesh-provision-postgres:$(VERSION) PROVISIONER_DEV_TAG ?= mesh-provision-postgres:development provisioner-image: docker build -f examples/postgres-provisioner/Dockerfile \ -t $(PROVISIONER_IMAGE) -t $(PROVISIONER_DEV_TAG) . @echo @docker image inspect $(PROVISIONER_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' # The object store's provisioner, for the same reason: a bucket and a policy are not files, and # the mesh cannot make them -- it discarded the credential it would have to use. OBJECTSTORE_IMAGE ?= mesh-provision-objectstore:$(VERSION) OBJECTSTORE_DEV_TAG ?= mesh-provision-objectstore:development objectstore-image: docker build -f examples/objectstore-provisioner/Dockerfile \ -t $(OBJECTSTORE_IMAGE) -t $(OBJECTSTORE_DEV_TAG) . @echo @docker image inspect $(OBJECTSTORE_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' # The cache's provisioner, for the same reason as the database's: an ACL user is not a file, # and the mesh cannot make one -- it discarded the credential it would have to use. REDIS_PROVISIONER_IMAGE ?= mesh-provision-redis:$(VERSION) REDIS_PROVISIONER_DEV_TAG ?= mesh-provision-redis:development redis-provisioner-image: docker build -f examples/redis-provisioner/Dockerfile \ -t $(REDIS_PROVISIONER_IMAGE) -t $(REDIS_PROVISIONER_DEV_TAG) . @echo @docker image inspect $(REDIS_PROVISIONER_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' # The proxy that turns a route grant into traffic reaching a workload. PROXY_IMAGE ?= mesh-route-proxy:$(VERSION) PROXY_DEV_TAG ?= mesh-route-proxy:development proxy-image: docker build -f examples/route-proxy/Dockerfile -t $(PROXY_IMAGE) -t $(PROXY_DEV_TAG) . @echo @docker image inspect $(PROXY_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' # The whole gate. Raises a database, runs everything against it, and takes it down again -- # including when the tests fail, which is why the teardown is not conditional. check: fmt vet postgres @go test ./... ; status=$$? ; $(MAKE) postgres-stop ; exit $$status # Without a database the live tests skip rather than fail, so this is the honest subset and not # the gate. test: go test ./... vet: go vet ./... fmt: @unformatted=$$(gofmt -l . 2>/dev/null) ; \ if [ -n "$$unformatted" ] ; then echo "not gofmt'd:" ; echo "$$unformatted" ; exit 1 ; fi postgres: @docker rm -f $(PG_CONTAINER) >/dev/null 2>&1 || true @docker run -d --name $(PG_CONTAINER) -e POSTGRES_PASSWORD=check \ -p 127.0.0.1:$(PG_PORT):5432 $(PG_IMAGE) >/dev/null @printf 'waiting for postgres' @for i in $$(seq 1 60) ; do \ if docker exec $(PG_CONTAINER) pg_isready -U postgres >/dev/null 2>&1 ; then \ echo ' — ready' ; exit 0 ; fi ; \ printf '.' ; sleep 1 ; \ done ; \ echo ' — never came up' ; docker logs $(PG_CONTAINER) | tail -20 ; exit 1 postgres-stop: @docker rm -f $(PG_CONTAINER) >/dev/null 2>&1 || true clean: rm -rf build/