Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docker/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Ignore common build artifacts and large nix store paths
result
result.tar.gz
result-*.tar.gz
dist
dist-newstyle
dist/*
*.o
*.hi
.stack-work
.ghc.environment.*
.cabal-sandbox
.cabal-sandbox/*
node_modules
.git
*.lock
**/.DS_Store

# Ignore Nix store references and temporary files
/nix
*.drv
*.nar

46 changes: 46 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Lightweight Dockerfile for a static cardano-db-sync binary
#
# Usage (example):
# 1. Build a static executable with Nix:
# nix build .#cardano-db-sync-linux
# 2. Build the image from the project root:
# docker build -f docker/Dockerfile -t cardano-db-sync:lightweight .
#
# Note: Since result/ is a Nix symlink, Docker may have issues following it.
# If this fails, you can dereference the symlink first:
# mkdir -p docker/build && cp -L result/*.tar.gz docker/build/
# docker build -f docker/Dockerfile -t cardano-db-sync:lightweight .
#
# Important: this Dockerfile assumes the binary is a fully static executable
# (statically linked). If it's not static, use a small base image (see docs).

# Stage 1: Extract the binary from the tarball
FROM busybox:latest AS extractor
ARG TARBALL
# ADD automatically extracts tarballs
ADD ${TARBALL} /tmp/extract/
RUN find /tmp/extract -name cardano-db-sync -type f -exec mv {} /cardano-db-sync \; && \
chmod +x /cardano-db-sync

# Stage 2: Minimal runtime image
FROM ubuntu:24.04
ARG SCHEMA_HASH

# copy binary from extractor
COPY --from=extractor /cardano-db-sync /usr/local/bin/cardano-db-sync

# copy schema directory (must be present in build context)
COPY schema /schema
RUN chmod -R 755 /schema

# copy entrypoint template then substitute the baked SCHEMA_HASH into it
COPY docker-entrypoint.sh.template /usr/local/bin/docker-entrypoint.sh.template

# Use sed to bake the SCHEMA_HASH into the entrypoint script, then cleanup template
RUN sed "s|@SCHEMA_HASH@|${SCHEMA_HASH}|g" /usr/local/bin/docker-entrypoint.sh.template \
> /usr/local/bin/docker-entrypoint.sh && \
chmod +x /usr/local/bin/docker-entrypoint.sh && \
rm -f /usr/local/bin/docker-entrypoint.sh.template

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["--help"]
9 changes: 9 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Lightweight Docker image for cardano-db-sync
==========================================

This folder contains a minimal Docker context and `Dockerfile` intended for use with
a statically-built `cardano-db-sync` executable produced by Nix.

## testing

docker run --rm cardano-db-sync:lightweight version
21 changes: 21 additions & 0 deletions docker/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

# Dereference the Nix result symlink and get the tarball basename
TARBALL=$(basename "$(readlink -f result/*.tar.gz)")

# copy tarball into docker build context
cp -L result/*.tar.gz docker/

# copy runtime schema into build context so it gets baked into the image
cp -a schema docker/

# compute a deterministic schema checksum (sorted file list -> combined sha)
SCHEMA_HASH=$(cd schema && find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}')

# Build with the exact tarball name and schema hash as build args
docker build --build-arg TARBALL="$TARBALL" --build-arg SCHEMA_HASH="$SCHEMA_HASH" -t cardano-db-sync:lightweight docker/

# Cleanup
rm -f docker/*.tar.gz
rm -rf docker/schema
13 changes: 13 additions & 0 deletions docker/docker-entrypoint.sh.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -eu

# compute runtime checksum deterministically (sorted file list)
runtime_hash=$(cd /schema && find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}')

# baked checksum will be substituted at build time
if [ "$runtime_hash" != "@SCHEMA_HASH@" ]; then
echo "schema checksum mismatch: runtime=$runtime_hash baked=@SCHEMA_HASH@" >&2
exit 1
fi

exec /usr/local/bin/cardano-db-sync --schema-dir /schema "$@"
131 changes: 131 additions & 0 deletions docker/test-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env bash
# Test the cardano-db-sync Docker image:
# - basic invocation (defaults to --help)
# - running with the original schema directory (should succeed)
# - running with a modified schema (should fail and report checksum mismatch)
# - check that the binary exists by bypassing the ENTRYPOINT
#
# Usage: ./test-image.sh [IMAGE] [SCHEMA_DIR]
# Defaults:
# IMAGE=cardano-db-sync:lightweight
# SCHEMA_DIR=./schema

set -uo pipefail

IMAGE="${1:-cardano-db-sync:lightweight}"
SCHEMA_DIR="${2:-./schema}"

TMPDIR=""
FAILED=0

cleanup() {
rc=$?
if [[ -n "$TMPDIR" && -d "$TMPDIR" ]]; then
rm -rf "$TMPDIR"
fi
exit $rc
}
trap cleanup EXIT

require_cmd() {
command -v "$1" >/dev/null 2>&1 || { echo "required command '$1' not found" >&2; exit 2; }
}

require_cmd docker
require_cmd mktemp
require_cmd cp
require_cmd sed
require_cmd realpath

echo "Image: $IMAGE"
echo "Schema dir: $SCHEMA_DIR"

if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
echo "ERROR: Docker image '$IMAGE' not found locally." >&2
echo "Either build it or pull it before running this test script." >&2
exit 3
fi

run_container_capture() {
# args: container args...
docker run --rm "$@" 2>&1
return ${PIPESTATUS[0]:-0}
}

echo
echo "1) Basic invocation (no extra mounts) - should exit 0 (show help)"
out=$(run_container_capture "$IMAGE")
rc=$?
if [[ $rc -eq 0 ]]; then
echo "PASS: container exited 0"
else
echo "FAIL: container exited with $rc"
echo "Output:"
echo "-----"
echo "$out"
echo "-----"
FAILED=$((FAILED+1))
fi

if [[ ! -d "$SCHEMA_DIR" ]]; then
echo
echo "WARNING: schema directory '$SCHEMA_DIR' not found; skipping schema-related tests."
else
echo
echo "2) Run with host schema mounted to /schema (expected: success)"
out=$(run_container_capture -v "$(realpath "$SCHEMA_DIR")":/schema:ro "$IMAGE")
rc=$?
if [[ $rc -eq 0 ]]; then
echo "PASS: container exited 0 with host schema"
else
echo "FAIL: container exited $rc with host schema"
echo "Output:"
echo "-----"
echo "$out"
echo "-----"
FAILED=$((FAILED+1))
fi

echo
echo "3) Run with a modified schema (expected: schema checksum mismatch -> non-zero and error text)"
TMPDIR=$(mktemp -d)
cp -a "$SCHEMA_DIR"/. "$TMPDIR"/
# Modify the schema to change checksum (add a small file)
echo "injected-change-$(date +%s)" > "$TMPDIR"/.injected-change

out=$(run_container_capture -v "$TMPDIR":/schema:ro "$IMAGE")
rc=$?
if [[ $rc -ne 0 && "$out" == *"schema checksum mismatch"* ]]; then
echo "PASS: schema mismatch detected as expected (exit $rc)"
else
echo "FAIL: expected schema mismatch (non-zero exit and 'schema checksum mismatch' message)."
echo "Exit code: $rc"
echo "Output:"
echo "-----"
echo "$out"
echo "-----"
FAILED=$((FAILED+1))
fi
fi

echo
echo "4) Binary presence check inside image (override ENTRYPOINT so we can inspect fs)"
# IMPORTANT: override the image ENTRYPOINT so our test runs directly in the container
if docker run --rm --entrypoint /bin/sh "$IMAGE" -c 'test -x /usr/local/bin/cardano-db-sync' >/dev/null 2>&1; then
echo "PASS: /usr/local/bin/cardano-db-sync exists and is executable (entrypoint bypassed)"
echo " File info:"
docker run --rm --entrypoint /bin/sh "$IMAGE" -c 'ls -la /usr/local/bin/cardano-db-sync || true; file /usr/local/bin/cardano-db-sync || true'
else
echo "FAIL: /usr/local/bin/cardano-db-sync missing or not executable (even with entrypoint bypassed)"
FAILED=$((FAILED+1))
fi

echo
if [[ $FAILED -eq 0 ]]; then
echo "ALL TESTS PASSED"
else
echo "SOME TESTS FAILED: $FAILED failure(s)"
exit 4
fi

# cleanup happens via trap