Skip to content

integration

integration #34

Workflow file for this run

name: integration
# Live-service integration tests for the feature-gated backends (ADR-0006). These run against
# emulators (MinIO/Azurite/fake-gcs/dind/kind) rather than real clouds, and are env-guarded by
# `CAIRN_IT_*` so the default `cargo test` (and the hermetic CI in ci.yml) never touches the network.
# This is the "dedicated, env-guarded integration job" the depth-vs-breadth contract calls for.
on:
workflow_dispatch:
schedule:
# Weekly drift check (Mondays 06:00 UTC) so SDK/service changes surface even without a PR.
- cron: "0 6 * * 1"
pull_request:
# Validate the job on changes to the object backend or this workflow itself.
paths:
- "crates/cairn-backend-object/**"
- "crates/cairn-backend-docker/**"
- "crates/cairn-backend-k8s/**"
- "crates/cairn-vault/**"
- ".github/workflows/integration.yml"
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
s3-minio:
name: S3 contract (MinIO)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# Run MinIO ourselves (the official image needs the `server` command, which the GitHub
# `services:` block can't supply). aws-lc/hyper compile under `--features s3` on Linux only.
- name: Start MinIO
run: |
docker run -d --name minio -p 9000:9000 \
-e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio:latest server /data
- name: Wait for MinIO to be ready
run: |
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:9000/minio/health/live >/dev/null; then
echo "MinIO ready"; exit 0
fi
sleep 2
done
echo "MinIO did not become ready"; docker logs minio || true; exit 1
- name: Create the test bucket
env:
AWS_ACCESS_KEY_ID: minioadmin
AWS_SECRET_ACCESS_KEY: minioadmin
AWS_REGION: us-east-1
# `aws` is preinstalled on ubuntu-latest; `mb` is idempotent enough for a fresh runner.
run: aws --endpoint-url http://127.0.0.1:9000 s3 mb s3://cairn-it || true
- name: Run the S3 round-trip against MinIO
env:
CAIRN_IT_S3: "1"
CAIRN_IT_S3_ENDPOINT: http://127.0.0.1:9000
CAIRN_IT_S3_BUCKET: cairn-it
CAIRN_IT_S3_ACCESS_KEY: minioadmin
CAIRN_IT_S3_SECRET_KEY: minioadmin
CAIRN_IT_S3_REGION: us-east-1
run: cargo test -p cairn-backend-object --features s3 --test s3_minio -- --nocapture
azure-azurite:
name: Azure contract (Azurite)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Start Azurite
# `--skipApiVersionCheck`: the runner's `az` CLI negotiates a very new Storage API version
# that the pinned Azurite image rejects ("API version ... is not supported"); skipping the
# check lets both the `az` container-create step and the SDK round-trip talk to it. (The
# azure_storage 0.21 SDK itself sends an older, supported version.)
# `--loose`: the 0.21 SDK adds `x-ms-range-get-content-crc64` on ranged GETs, which real
# Azure supports but Azurite's strict mode rejects; loose mode accepts the extra header.
run: |
docker run -d --name azurite -p 10000:10000 \
mcr.microsoft.com/azure-storage/azurite \
azurite-blob --blobHost 0.0.0.0 --skipApiVersionCheck --loose
- name: Wait for Azurite to be ready
run: |
for i in $(seq 1 30); do
# Any HTTP response (even 400/403) means Azurite is listening; only a refused
# connection is a failure, so check the curl exit code, not the status.
if curl -sS -o /dev/null http://127.0.0.1:10000/devstoreaccount1; then
echo "Azurite ready"; exit 0
fi
sleep 2
done
echo "Azurite did not become ready"; docker logs azurite || true; exit 1
- name: Create the test container
# The Azurite well-known account/key connection string (public, not a secret).
env:
AZ_CS: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"
run: az storage container create -n cairn-it --connection-string "$AZ_CS" || true
- name: Run the Azure round-trip against Azurite
env:
CAIRN_IT_AZURE: "1"
CAIRN_IT_AZURE_ACCOUNT: devstoreaccount1
CAIRN_IT_AZURE_KEY: "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
CAIRN_IT_AZURE_CONTAINER: cairn-it
CAIRN_IT_AZURE_ENDPOINT: http://127.0.0.1:10000/devstoreaccount1
run: cargo test -p cairn-backend-object --features azure --test azure_azurite -- --nocapture
docker-dind:
name: Docker contract (dind)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# GitHub Linux runners have a working Docker daemon, so `connect_local()` talks to it
# directly — no docker-in-docker service needed. The test creates/cleans its own container.
- name: Run the Docker fs round-trip against the runner's daemon
env:
CAIRN_IT_DOCKER: "1"
run: cargo test -p cairn-backend-docker --features docker --test docker_dind -- --nocapture
k8s-kind:
name: K8s contract (kind)
runs-on: ubuntu-latest
# Bound the blast radius if `kind-action` ever stalls bringing up the cluster.
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# Spin up a real single-node Kubernetes cluster; the action writes the kubeconfig to the
# default location and sets the current context, which the adapter reads.
- uses: helm/kind-action@v1
with:
cluster_name: cairn-it
- name: Wait for kube-system to be up
run: kubectl wait --for=condition=Ready pods --all -n kube-system --timeout=150s || kubectl get pods -A
- name: Run the K8s tree round-trip against kind
env:
CAIRN_IT_K8S: "1"
run: cargo test -p cairn-backend-k8s --features k8s --test k8s_kind -- --nocapture