Files
spinifex/tests/e2e/ecr/docker_push_test.go
T
Josh Siviour f9858d4e5d test(e2e): ECR end-to-end suite (control plane + docker/crane/skopeo) (#398)
* test(e2e): add ECR end-to-end suite (control plane + docker/crane/skopeo)

New tests/e2e/ecr/ suite plus harness support:
- harness: ECR SDK client, control-plane helpers (repo CRUD, get-login-password,
  describe-images), and docker/crane/skopeo CLI wrappers that skip when the
  binary or registry-host resolution is absent.
- suite: control-plane CRUD + policy + lifecycle round-trip; docker login/push/
  pull; crane append/pull and skopeo copy exercising the Bearer /v2/token flow.
- registered e2e-ecr in the Makefile + service manifest.

Predastore capacity/load leg is a skipped placeholder pending mulga-siv-359/356.

* test(e2e): carry awsgw port in ECR registry host for dev clusters

Dev clusters bind awsgw on a non-standard port (e.g. 9999); production uses
:443. ECRRegistryHost now appends SPINIFEX_ECR_PORT when set so docker/crane/
skopeo address the right endpoint, and RequireRegistryResolves strips the port
before DNS lookup.
2026-06-19 11:34:38 +10:00

81 lines
2.6 KiB
Go

//go:build e2e
package ecr
import (
"crypto/rand"
"os"
"path/filepath"
"testing"
"time"
"github.com/mulgadc/spinifex/tests/e2e/harness"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const dockerCmdTimeout = 2 * time.Minute
// buildScratchContext writes a hermetic FROM-scratch build context (no network
// pulls) with a random payload layer, returning the context dir.
func buildScratchContext(t *testing.T, dir string) string {
t.Helper()
payload := make([]byte, 4096)
_, err := rand.Read(payload)
require.NoError(t, err)
require.NoError(t, os.WriteFile(filepath.Join(dir, "payload"), payload, 0o600))
require.NoError(t, os.WriteFile(filepath.Join(dir, "Dockerfile"),
[]byte("FROM scratch\nCOPY payload /payload\n"), 0o600))
return dir
}
// TestDockerPushPull drives the canonical workflow: aws ecr get-login-password
// -> docker login -> docker push -> DescribeImages -> docker pull (clean daemon)
// against the cluster registry.
func TestDockerPushPull(t *testing.T) {
f := requireECRFixture(t)
host := harness.ECRRegistryHost(f.Account)
harness.RequireRegistryResolves(t, host)
dockerConfig := filepath.Join(f.TmpDir, "docker")
require.NoError(t, os.MkdirAll(dockerConfig, 0o700))
docker := harness.NewDocker(t, dockerConfig)
repo := uniqueRepo("docker")
harness.CreateECRRepository(t, f.AWS, repo)
ref := harness.ECRRepositoryURI(f.Account, repo) + ":v1"
t.Run("docker login", func(t *testing.T) {
pass := harness.ECRGetLoginPassword(t, f.AWS)
out, err := docker.RunStdin(dockerCmdTimeout, pass,
"login", host, "-u", "AWS", "--password-stdin")
require.NoErrorf(t, err, "docker login: %s", out)
assert.Contains(t, out, "Login Succeeded")
})
t.Run("docker build + push", func(t *testing.T) {
buildDir := filepath.Join(f.TmpDir, "build")
require.NoError(t, os.MkdirAll(buildDir, 0o700))
ctxDir := buildScratchContext(t, buildDir)
out, err := docker.Run(dockerCmdTimeout, "build", "-t", ref, ctxDir)
require.NoErrorf(t, err, "docker build: %s", out)
out, err = docker.Run(dockerCmdTimeout, "push", ref)
require.NoErrorf(t, err, "docker push: %s", out)
})
t.Run("DescribeImages shows the pushed tag", func(t *testing.T) {
require.True(t, harness.ECRWaitImageTag(t, f.AWS, repo, "v1", 30*time.Second),
"pushed tag v1 not visible via DescribeImages")
})
t.Run("docker pull round-trips from a clean daemon", func(t *testing.T) {
out, err := docker.Run(dockerCmdTimeout, "rmi", "-f", ref)
require.NoErrorf(t, err, "docker rmi: %s", out)
out, err = docker.Run(dockerCmdTimeout, "pull", ref)
require.NoErrorf(t, err, "docker pull: %s", out)
})
}