Files
smallwebwaf/Dockerfile
T
clawbot e7fb88af9e
check / check (push) Failing after 45s
Spend less of make test writing the image and waiting (closes #56)
The test phase spends most of its time compiling with the race
detector from an empty build cache; then come writing the test image
and the internal/proxy tests.

- Go's build cache is on a tmpfs in the test phase, so its 137 MB are
  no longer written into the test image.
- TestUpgradedConnectionOutlastsTheTimeouts waits until just past
  shortTimeout after the answer to the upgrade was read, by when every
  timeout has started, rather than 7.5 s, so it ends with the other
  timing tests.
- shortTimeout is written as waitLimit / 2, as its comment says it is.

Model: opus-5-5
2026-10-04 11:13:32 +02:00

153 lines
6.7 KiB
Docker

# Lint phase. The linter is invoked directly rather than through `make
# lint` or `script/lint`, which are themselves a docker build and would
# recurse into a daemon that does not exist in a build step.
#
# golangci/golangci-lint v2.12.2 (built with go1.26.2), 2026-05-06
FROM golangci/golangci-lint@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS lint
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN golangci-lint run --config .golangci.yml ./...
# Test phase, same shape and for the same reason. The go directive in
# go.mod is a minimum, so this Go may be newer than the linter's. The
# Debian image rather than the Alpine one, because the race detector
# needs the C compiler it carries.
#
# golang 1.27.1-trixie, 2026-09-19
FROM golang@sha256:3b77fc618ec235a1ab412de7737f120dd507c57e8d87de4cbb7994fb94275ed5 AS test
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Go's build cache is kept on a tmpfs, out of the image: nothing uses it
# after this step, and writing it into the image takes seconds.
RUN --mount=type=tmpfs,target=/root/.cache/go-build \
go test -count=1 -timeout 90s -race -cover ./... || \
{ echo "--- Rerunning with -v for details ---"; \
go test -count=1 -timeout 90s -race -v ./...; exit 1; }
# Build stage. Nothing is wanted from the two phases above; the copies
# are what make BuildKit build them first, so the image, which needs this
# stage, cannot be produced unless lint and test passed.
#
# golang 1.27.1-trixie, 2026-09-19
FROM golang@sha256:3b77fc618ec235a1ab412de7737f120dd507c57e8d87de4cbb7994fb94275ed5 AS builder
COPY --from=lint /src/go.sum /dev/null
COPY --from=test /src/go.sum /dev/null
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# The version is computed on the host and passed in, because
# .dockerignore excludes .git.
ARG VERSION=dev
RUN CGO_ENABLED=0 go build -trimpath \
-ldflags="-s -w -X main.Version=${VERSION}" \
-o /usr/local/bin/smallwebwaf ./cmd/smallwebwaf
# runsvinit, the image's entrypoint, built at the last commit of its
# archived repository. It has no go.mod, and `go build` of its directory
# needs one; it uses only the standard library, so the one written here
# names nothing else.
#
# golang 1.27.1-trixie, 2026-09-19
FROM golang@sha256:3b77fc618ec235a1ab412de7737f120dd507c57e8d87de4cbb7994fb94275ed5 AS runsvinit
RUN git clone --quiet https://github.com/peterbourgon/runsvinit /src
WORKDIR /src
# runsvinit v2.0.0-8-gb4b2c78, 2015-10-07
RUN git checkout --quiet --detach b4b2c785308b1ce785b6155c7fe5f16879080193 \
&& go mod init github.com/peterbourgon/runsvinit \
&& CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" \
-o /usr/local/bin/runsvinit .
# The image an app's Dockerfile builds FROM, described under "Deployment"
# in SPEC.md. It is the last stage, so a plain `docker build .` builds it.
#
# ubuntu 26.04, 2026-09-27
FROM ubuntu@sha256:f144425ff09be612d6d9ad965196e9cdc23dae1f42110a8a11a3e9a8198759f7
# ca-certificates, nix-bin and runit, from Ubuntu's archive as it was at
# the snapshot moment, which is never earlier than the Ubuntu image above.
# apt checks every package against the snapshot's InRelease files, and
# this step checks those against the hashes named here, which are those
# of the amd64 archive: other architectures use Ubuntu's ports archive.
# apt also fetches the live archive's InRelease files, which change daily
# and which the install does not use. The snapshot service is HTTPS only
# and this image has no CA certificates yet, so this step uses the Go
# image's.
RUN --mount=type=bind,from=builder,source=/etc/ssl/certs/ca-certificates.crt,target=/tmp/go-image-ca.crt \
apt-get update --snapshot 20261001T000000Z \
-o Acquire::https::CaInfo=/tmp/go-image-ca.crt \
&& printf '%s\n' \
'45f95ce276cdba3e41870516a130e03c58b8b7a79e9546b0efe9e526d255740c snapshot.ubuntu.com_ubuntu_20261001T000000Z_dists_resolute_InRelease' \
'802e675dd9de4c7f3916434a95e7c1d8eec0e82886622d7805ab19a2c6fe0365 snapshot.ubuntu.com_ubuntu_20261001T000000Z_dists_resolute-updates_InRelease' \
'64b3353f0bd4970b4f7271962245bcea9ff24d4cc7bea16b433f8a60e42ca3dd snapshot.ubuntu.com_ubuntu_20261001T000000Z_dists_resolute-backports_InRelease' \
'1d5041572116a8b23aabf79ac7439ad8af83d57ad3fb0f9aa0d4523ec10c5908 snapshot.ubuntu.com_ubuntu_20261001T000000Z_dists_resolute-security_InRelease' \
| (cd /var/lib/apt/lists && sha256sum --check --strict) \
&& DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
--snapshot 20261001T000000Z \
-o Acquire::https::CaInfo=/tmp/go-image-ca.crt \
ca-certificates nix-bin runit \
&& rm -rf /var/lib/apt/lists/*
# Nix run by root expects a group of build users, which nix-bin does not
# create; with the setting empty, root's builds run without them.
RUN mkdir /etc/nix && echo 'build-users-group =' > /etc/nix/nix.conf
# nixpkgs, from its release file, checked by SHA-256, and set up for root
# as `nixpkgs`, so that an app's Dockerfile installs a package with
# `nix-env -iA nixpkgs.<name>`. curl and xz come with nix-bin.
#
# nixpkgs nixos-26.05.11045.774debe7a0d1, 2026-10-02
RUN curl -fsSL -o /tmp/nixexprs.tar.xz \
https://releases.nixos.org/nixos/26.05/nixos-26.05.11045.774debe7a0d1/nixexprs.tar.xz \
&& echo 'b2994104605601690023a5a6a3bb5a07b2bd1716b4e3b208cba1056dacd2ab08 /tmp/nixexprs.tar.xz' \
| sha256sum --check --strict \
&& mkdir -p /root/.nix-defexpr/nixpkgs \
&& tar -xJf /tmp/nixexprs.tar.xz -C /root/.nix-defexpr/nixpkgs --strip-components=1 \
&& rm /tmp/nixexprs.tar.xz
# What root installs with nix-env lands in root's profile. This path to
# it works for every user, unlike /root/.nix-profile: only root can
# enter /root. It comes last, so that no package shadows the image's
# own tools: busybox, for one, brings an sv that looks for services
# elsewhere.
ENV PATH=${PATH}:/nix/var/nix/profiles/default/bin
COPY --from=runsvinit /usr/local/bin/runsvinit /usr/local/bin/runsvinit
COPY --from=builder /usr/local/bin/smallwebwaf /usr/local/bin/smallwebwaf
RUN groupadd --system --gid 65532 smallwebwaf \
&& useradd --system --uid 65532 --gid smallwebwaf --no-create-home \
--shell /usr/sbin/nologin smallwebwaf
# runsvinit starts runit's runsvdir on /etc/service, where Ubuntu's sv
# looks too.
COPY --chmod=755 share/smallwebwaf.run /etc/service/smallwebwaf/run
EXPOSE 8080
# traefik sends a container no requests until it is healthy, so the
# check runs every second from the start until it first passes, for up
# to a minute, and every 30 seconds after that.
HEALTHCHECK --start-period=1m --start-interval=1s \
CMD ["/usr/local/bin/smallwebwaf", "healthcheck"]
ENTRYPOINT ["/usr/local/bin/runsvinit"]