From 50309443e96eb783c8fc5cd3f576d02936771bd8 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Sun, 11 Oct 2026 21:58:20 +0100 Subject: [PATCH 1/5] tests: pull the PostgreSQL image from mirror.gcr.io, share one constant Signed-off-by: Eric Curtin --- cmd/ateapi/internal/authz/authz_test.go | 30 ++----------------- .../internal/store/atepg/cert_auth_test.go | 3 +- cmd/ateapi/internal/store/atepg/main_test.go | 2 +- .../internal/store/dockerenv/dockerenv.go | 8 ++++- .../store/dockerenv/dockerenv_test.go | 7 +++++ .../internal/store/storetest/storetest.go | 2 +- 6 files changed, 20 insertions(+), 32 deletions(-) diff --git a/cmd/ateapi/internal/authz/authz_test.go b/cmd/ateapi/internal/authz/authz_test.go index c391ffb48d..58cd685199 100644 --- a/cmd/ateapi/internal/authz/authz_test.go +++ b/cmd/ateapi/internal/authz/authz_test.go @@ -18,9 +18,6 @@ import ( "context" "errors" "os" - "os/exec" - "runtime" - "strings" "testing" "time" @@ -38,29 +35,6 @@ import ( "go.ate.dev/substrate/internal/principal" ) -func configureDockerEnv(ctx context.Context) error { - if os.Getenv("DOCKER_HOST") != "" { - return nil - } - output, err := exec.CommandContext(ctx, "docker", "context", "inspect", "--format", "{{.Endpoints.docker.Host}}").Output() - if err != nil { - return err - } - host := strings.TrimSpace(string(output)) - if host == "" { - return nil - } - _ = os.Setenv("DOCKER_HOST", host) - if os.Getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE") == "" { - socket := host - if runtime.GOOS == "darwin" { - socket = "/var/run/docker.sock" - } - _ = os.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", socket) - } - return nil -} - func startPostgres(t *testing.T) *pgxpool.Pool { t.Helper() ctx := context.Background() @@ -70,12 +44,12 @@ func startPostgres(t *testing.T) *pgxpool.Pool { if dockerenv.Required() { unavailable = t.Fatalf } - if err := configureDockerEnv(ctx); err != nil { + if err := dockerenv.Configure(ctx); err != nil { unavailable("docker is unavailable: %v", err) } pgContainer, err := postgres.Run(ctx, - "postgres:18-alpine", + dockerenv.PostgresImage, postgres.WithDatabase("testdb"), postgres.WithUsername("postgres"), postgres.WithPassword("postgres"), diff --git a/cmd/ateapi/internal/store/atepg/cert_auth_test.go b/cmd/ateapi/internal/store/atepg/cert_auth_test.go index d20f819f54..4fdbe9e51f 100644 --- a/cmd/ateapi/internal/store/atepg/cert_auth_test.go +++ b/cmd/ateapi/internal/store/atepg/cert_auth_test.go @@ -37,6 +37,7 @@ import ( "github.com/testcontainers/testcontainers-go" tcexec "github.com/testcontainers/testcontainers-go/exec" "github.com/testcontainers/testcontainers-go/modules/postgres" + "go.ate.dev/substrate/cmd/ateapi/internal/store/dockerenv" "go.ate.dev/substrate/internal/localca" "go.ate.dev/substrate/pkg/postgressetup" "sigs.k8s.io/yaml" @@ -88,7 +89,7 @@ func TestBundledPostgresCertificateAuthentication(t *testing.T) { ).Replace(cm.Data["postgresql.conf"]) configPath := filepath.Join(dir, "postgresql.conf") writeCertTestFile(t, configPath, []byte(conf)) - container, err := postgres.Run(t.Context(), "postgres:18-alpine", + container, err := postgres.Run(t.Context(), dockerenv.PostgresImage, postgres.WithDatabase("atepg"), postgres.WithUsername("postgres"), postgres.WithPassword("unused"), postgres.WithSSLCert(clientRoot, serverCert, serverKey), postgres.WithConfigFile(configPath), testcontainers.WithFiles(testcontainers.ContainerFile{Reader: strings.NewReader(cm.Data["pg_hba.conf"]), ContainerFilePath: "/tmp/testcontainers-go/postgres/pg_hba.conf", FileMode: 0644}), diff --git a/cmd/ateapi/internal/store/atepg/main_test.go b/cmd/ateapi/internal/store/atepg/main_test.go index a0ca3b1bf8..d703c9aa7b 100644 --- a/cmd/ateapi/internal/store/atepg/main_test.go +++ b/cmd/ateapi/internal/store/atepg/main_test.go @@ -63,7 +63,7 @@ func requirePool(t *testing.T) *pgxpool.Pool { containerErr = err return } - pgContainer, err := postgres.Run(ctx, "postgres:18-alpine", + pgContainer, err := postgres.Run(ctx, dockerenv.PostgresImage, postgres.WithDatabase("atepg"), postgres.WithUsername("atepg"), postgres.WithPassword("atepg"), diff --git a/cmd/ateapi/internal/store/dockerenv/dockerenv.go b/cmd/ateapi/internal/store/dockerenv/dockerenv.go index b1c5d61dae..3dc8690ea9 100644 --- a/cmd/ateapi/internal/store/dockerenv/dockerenv.go +++ b/cmd/ateapi/internal/store/dockerenv/dockerenv.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package dockerenv points testcontainers at the active Docker context. +// Package dockerenv points testcontainers at the active Docker context and +// names the images they start. // // It lives apart from storetest because storetest imports atepg, so atepg // cannot import storetest back, and both need this before starting a @@ -29,6 +30,11 @@ import ( "strings" ) +// PostgresImage is the PostgreSQL testcontainer image. It comes from Google's +// Docker Hub mirror, because anonymous pulls from Docker Hub are rate limited +// per runner IP and fail every PostgreSQL-backed test at once. +const PostgresImage = "mirror.gcr.io/library/postgres:18-alpine" + // Configure sets the environment testcontainers reads to find Docker, unless // it is already set. testcontainers-go resolves the daemon from DOCKER_HOST // and its own probe list; it never consults the Docker CLI context, so a diff --git a/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go b/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go index 96a5c6dc01..3b8a78f172 100644 --- a/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go +++ b/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go @@ -19,6 +19,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "testing" ) @@ -85,3 +86,9 @@ func TestRequired(t *testing.T) { }) } } + +func TestPostgresImageAvoidsDockerHub(t *testing.T) { + if !strings.HasPrefix(PostgresImage, "mirror.gcr.io/") { + t.Errorf("PostgresImage = %q, want a mirror.gcr.io reference", PostgresImage) + } +} diff --git a/cmd/ateapi/internal/store/storetest/storetest.go b/cmd/ateapi/internal/store/storetest/storetest.go index 7bc219b3d2..0c84532ac7 100644 --- a/cmd/ateapi/internal/store/storetest/storetest.go +++ b/cmd/ateapi/internal/store/storetest/storetest.go @@ -180,7 +180,7 @@ func requireAdminPool(t *testing.T) *pgxpool.Pool { containerErr = err return } - containerPG, containerErr = postgres.Run(ctx, "postgres:18-alpine", + containerPG, containerErr = postgres.Run(ctx, dockerenv.PostgresImage, postgres.WithDatabase("postgres"), postgres.WithUsername("postgres"), postgres.WithPassword("postgres"), From 718e88f66afeb46f773f9bf37c7fbe38ab0beda1 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Thu, 15 Oct 2026 10:15:05 +0100 Subject: [PATCH 2/5] Use mirror.gcr.io as a daemon registry mirror in CI, not an image prefix Signed-off-by: Eric Curtin --- .github/workflows/pr-workflow.yaml | 10 ++++++++++ cmd/ateapi/internal/store/dockerenv/dockerenv.go | 7 +++---- cmd/ateapi/internal/store/dockerenv/dockerenv_test.go | 7 ------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pr-workflow.yaml b/.github/workflows/pr-workflow.yaml index 4584f1057b..24bf85beed 100644 --- a/.github/workflows/pr-workflow.yaml +++ b/.github/workflows/pr-workflow.yaml @@ -51,6 +51,16 @@ jobs: uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: 'go.mod' + # Anonymous Docker Hub pulls are rate limited per runner IP. mirror.gcr.io + # is meant to be used as a daemon mirror; images it lacks still come from + # Docker Hub. + - name: Use the Docker Hub mirror + run: | + cfg=/etc/docker/daemon.json + [ -s "$cfg" ] || echo '{}' | sudo tee "$cfg" >/dev/null + sudo jq '. + {"registry-mirrors": ["https://mirror.gcr.io"]}' "$cfg" | sudo tee "$cfg.new" >/dev/null + sudo mv "$cfg.new" "$cfg" + sudo systemctl restart docker # we only test on amd64, make sure everything still builds for arm64 - name: build for arm64 run: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build ./... diff --git a/cmd/ateapi/internal/store/dockerenv/dockerenv.go b/cmd/ateapi/internal/store/dockerenv/dockerenv.go index 3dc8690ea9..e3d98e1d3d 100644 --- a/cmd/ateapi/internal/store/dockerenv/dockerenv.go +++ b/cmd/ateapi/internal/store/dockerenv/dockerenv.go @@ -30,10 +30,9 @@ import ( "strings" ) -// PostgresImage is the PostgreSQL testcontainer image. It comes from Google's -// Docker Hub mirror, because anonymous pulls from Docker Hub are rate limited -// per runner IP and fail every PostgreSQL-backed test at once. -const PostgresImage = "mirror.gcr.io/library/postgres:18-alpine" +// PostgresImage is the PostgreSQL testcontainer image, shared by every test +// that starts one. +const PostgresImage = "postgres:18-alpine" // Configure sets the environment testcontainers reads to find Docker, unless // it is already set. testcontainers-go resolves the daemon from DOCKER_HOST diff --git a/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go b/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go index 3b8a78f172..96a5c6dc01 100644 --- a/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go +++ b/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go @@ -19,7 +19,6 @@ import ( "os" "path/filepath" "runtime" - "strings" "testing" ) @@ -86,9 +85,3 @@ func TestRequired(t *testing.T) { }) } } - -func TestPostgresImageAvoidsDockerHub(t *testing.T) { - if !strings.HasPrefix(PostgresImage, "mirror.gcr.io/") { - t.Errorf("PostgresImage = %q, want a mirror.gcr.io reference", PostgresImage) - } -} From 21286be1d9b5340a45e135de01eea2427aa2a2a3 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Sat, 17 Oct 2026 21:44:16 +0100 Subject: [PATCH 3/5] tests: pin the PostgreSQL image by digest Signed-off-by: Eric Curtin --- cmd/ateapi/internal/store/dockerenv/dockerenv.go | 4 ++-- cmd/ateapi/internal/store/dockerenv/dockerenv_test.go | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cmd/ateapi/internal/store/dockerenv/dockerenv.go b/cmd/ateapi/internal/store/dockerenv/dockerenv.go index e3d98e1d3d..83ecae5108 100644 --- a/cmd/ateapi/internal/store/dockerenv/dockerenv.go +++ b/cmd/ateapi/internal/store/dockerenv/dockerenv.go @@ -31,8 +31,8 @@ import ( ) // PostgresImage is the PostgreSQL testcontainer image, shared by every test -// that starts one. -const PostgresImage = "postgres:18-alpine" +// that starts one. It is digest-pinned and matches the install manifest. +const PostgresImage = "postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15" // Configure sets the environment testcontainers reads to find Docker, unless // it is already set. testcontainers-go resolves the daemon from DOCKER_HOST diff --git a/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go b/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go index 96a5c6dc01..b44854b7de 100644 --- a/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go +++ b/cmd/ateapi/internal/store/dockerenv/dockerenv_test.go @@ -19,9 +19,20 @@ import ( "os" "path/filepath" "runtime" + "strings" "testing" ) +func TestPostgresImageMatchesManifest(t *testing.T) { + manifest, err := os.ReadFile("../../../../../manifests/ate-install/postgres/postgres.yaml") + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(manifest), "image: "+PostgresImage) { + t.Errorf("postgres.yaml does not use image %s", PostgresImage) + } +} + func TestConfigureFromContext(t *testing.T) { t.Setenv("DOCKER_HOST", "") t.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "") From 56061c011a68b27fbbed9027caea4ae7d007e541 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Fri, 23 Oct 2026 02:55:18 +0100 Subject: [PATCH 4/5] ci: move the Docker Hub mirror step into a script Signed-off-by: Eric Curtin --- .github/workflows/pr-workflow.yaml | 10 +--------- hack/ci-docker-registry-mirror.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100755 hack/ci-docker-registry-mirror.sh diff --git a/.github/workflows/pr-workflow.yaml b/.github/workflows/pr-workflow.yaml index 24bf85beed..82026a858c 100644 --- a/.github/workflows/pr-workflow.yaml +++ b/.github/workflows/pr-workflow.yaml @@ -51,16 +51,8 @@ jobs: uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: 'go.mod' - # Anonymous Docker Hub pulls are rate limited per runner IP. mirror.gcr.io - # is meant to be used as a daemon mirror; images it lacks still come from - # Docker Hub. - name: Use the Docker Hub mirror - run: | - cfg=/etc/docker/daemon.json - [ -s "$cfg" ] || echo '{}' | sudo tee "$cfg" >/dev/null - sudo jq '. + {"registry-mirrors": ["https://mirror.gcr.io"]}' "$cfg" | sudo tee "$cfg.new" >/dev/null - sudo mv "$cfg.new" "$cfg" - sudo systemctl restart docker + run: hack/ci-docker-registry-mirror.sh # we only test on amd64, make sure everything still builds for arm64 - name: build for arm64 run: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build ./... diff --git a/hack/ci-docker-registry-mirror.sh b/hack/ci-docker-registry-mirror.sh new file mode 100755 index 0000000000..6b0b6ce8d5 --- /dev/null +++ b/hack/ci-docker-registry-mirror.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Points the Docker daemon at mirror.gcr.io. It rewrites /etc/docker/daemon.json +# and restarts dockerd, so it is for CI runners only. +# +# Anonymous Docker Hub pulls are rate limited per runner IP. mirror.gcr.io is +# meant to be used as a daemon mirror; images it lacks still come from Docker Hub. + +set -o errexit -o nounset -o pipefail + +cfg=/etc/docker/daemon.json +[[ -s "${cfg}" ]] || echo '{}' | sudo tee "${cfg}" >/dev/null +sudo jq '. + {"registry-mirrors": ["https://mirror.gcr.io"]}' "${cfg}" | sudo tee "${cfg}.new" >/dev/null +sudo mv "${cfg}.new" "${cfg}" +sudo systemctl restart docker From 641dd1d36481ce0034fe545fecf8330b890275fe Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Sun, 8 Nov 2026 12:13:13 +0000 Subject: [PATCH 5/5] Use Agent Substrate Authors copyright Signed-off-by: Eric Curtin --- hack/ci-docker-registry-mirror.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/ci-docker-registry-mirror.sh b/hack/ci-docker-registry-mirror.sh index 6b0b6ce8d5..6c440f0f5a 100755 --- a/hack/ci-docker-registry-mirror.sh +++ b/hack/ci-docker-registry-mirror.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2026 Google LLC +# Copyright 2026 The Agent Substrate Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.