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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ container-target/
.local/
.failed-tests
**/proptest-regressions/**
.zainod_config/
Empty file added .zainod_config/gitinclude
Empty file.
41 changes: 5 additions & 36 deletions Dockerfile → Containerfile.build
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
# syntax=docker/dockerfile:1

############################
# Global build args
############################
ARG RUST_VERSION=1.86.0
ARG UID=1000
ARG GID=1000
ARG USER=container_user
ARG HOME=/home/container_user

############################
# Builder
############################
ARG RUST_VERSION

FROM rust:${RUST_VERSION}-bookworm AS builder
SHELL ["/bin/bash", "-euo", "pipefail", "-c"]
WORKDIR /app
Expand Down Expand Up @@ -40,43 +33,19 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
fi

############################
# Runtime (slim, non-root)
# Runtime
############################
FROM debian:bookworm-slim AS runtime
SHELL ["/bin/bash", "-euo", "pipefail", "-c"]

ARG UID
ARG GID
ARG USER
ARG HOME

# Only the dynamic libs needed by a Rust/OpenSSL binary
# Runtime deps
RUN apt-get -qq update && \
apt-get -qq install -y --no-install-recommends \
ca-certificates libssl3 libgcc-s1 \
&& rm -rf /var/lib/apt/lists/*

# Create non-root user
RUN addgroup --gid "${GID}" "${USER}" && \
adduser --uid "${UID}" --gid "${GID}" --home "${HOME}" \
--disabled-password --gecos "" "${USER}"

WORKDIR ${HOME}

# Copy the installed binary from builder
COPY --from=builder /out/bin/zainod /usr/local/bin/zainod

RUN mkdir -p .cache/zaino
RUN chown -R "${UID}:${GID}" "${HOME}"
USER ${USER}
EXPOSE 8137 8237

# Default ports (adjust if your app uses different ones)
ARG ZAINO_GRPC_PORT=8137
ARG ZAINO_JSON_RPC_PORT=8237
EXPOSE ${ZAINO_GRPC_PORT} ${ZAINO_JSON_RPC_PORT}

# Healthcheck that doesn't assume specific HTTP/gRPC endpoints
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD /usr/local/bin/zainod --version >/dev/null 2>&1 || exit 1

CMD ["zainod"]
12 changes: 12 additions & 0 deletions Containerfile.tail
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# syntax=docker/dockerfile:1

ARG RUNTIME_IMAGE
FROM ${RUNTIME_IMAGE}

# Writable home for XDG defaults (config, cache, etc.)
# The actual UID is mapped at runtime via --userns=keep-id or --user.
RUN mkdir -p /home/zaino && chmod 777 /home/zaino
ENV HOME=/home/zaino

ENTRYPOINT ["zainod"]
CMD ["start"]
21 changes: 21 additions & 0 deletions Dockerfile.tail
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1

ARG RUNTIME_IMAGE
FROM ${RUNTIME_IMAGE}

# Docker-specific: baked-in non-root user
ARG UID=1000
ARG GID=1000
ARG USER=container_user
ARG HOME=/home/container_user

RUN addgroup --gid "${GID}" "${USER}" && \
adduser --uid "${UID}" --gid "${GID}" --home "${HOME}" \
--disabled-password --gecos "" "${USER}"

WORKDIR ${HOME}
RUN mkdir -p .cache/zaino
RUN chown -R "${UID}:${GID}" "${HOME}"
USER ${USER}

CMD ["zainod"]
99 changes: 99 additions & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ echo " container-test Run integration tests using the local image"
echo " container-test-save-failures Run tests, save failures to .failed-tests"
echo " container-test-retry-failures Rerun only the previously failed tests"
echo " build-image Build the Docker image with current artifact versions"
echo " build-zainod Build zainod container image (makers build-zainod <podman|docker>)"
echo " run-zainod Build, configure, and run zainod (makers run-zainod <podman|docker>)"
echo " push-image Push the image (used in CI, can be used manually)"
echo " compute-image-tag Compute the tag for the Docker image based on versions"
echo " get-docker-hash Get DOCKER_DIR_HASH value (hash for the image defining files)"
Expand Down Expand Up @@ -603,3 +605,100 @@ info "Filter: $FILTER"
makers container-test -E "$FILTER" "${@}"
'''
script.post = "makers notify"

# -------------------------------------------------------------------

[tasks.base-zainod]
private = true
script_runner = "bash"
script.pre = '''
set -euo pipefail
source "./utils/helpers.sh"

validate_engine "${1:-}"
require_clean_worktree
compute_zainod_tags

TAIL_FILE=$(tail_file_for_engine)
CMD_PREFIX=$(cmd_prefix_for_engine)
RUN_FLAGS=$(run_flags_for_engine)
'''
script.main = "err 'base-zainod: override script.main in the extending task'"

# -------------------------------------------------------------------

[tasks.build-zainod]
description = "Build zainod container image (usage: makers build-zainod <podman|docker>)"
extend = "base-zainod"
private = false
script.main = '''
info "Building zainod runtime image"
info "Engine: $ENGINE"
info "Rust: $RUST_VERSION"
info "Commit: $COMMIT"
info "Tag: $RUNTIME_TAG"

$ENGINE build -f Containerfile.build \
--build-arg RUST_VERSION="$RUST_VERSION" \
-t "$RUNTIME_TAG" \
.

info "Building final zainod image"
info "Tail file: $TAIL_FILE"
info "Tag: $ZAINOD_TAG"

$ENGINE build -f "$TAIL_FILE" \
--build-arg RUNTIME_IMAGE="$RUNTIME_TAG" \
-t "$ZAINOD_TAG" \
.

info "Done. Run with: makers run-zainod $ENGINE"
'''

# -------------------------------------------------------------------

[tasks.run-zainod]
description = "Build, configure, and run zainod (usage: makers run-zainod <podman|docker>)"
extend = "base-zainod"
private = false
script.main = '''
# Skip build if image already exists
if $ENGINE image inspect "$ZAINOD_TAG" > /dev/null 2>&1; then
info "Image $ZAINOD_TAG already exists — skipping build"
else
info "Image $ZAINOD_TAG not found — building..."
makers build-zainod "$ENGINE"
fi

CONFIG_DIR=".zainod_config"
CONFIG_FILE="${CONFIG_DIR}/zainod.toml"

# Generate config if it doesn't exist
if [ ! -f "$CONFIG_FILE" ]; then
info "Generating default config at $CONFIG_FILE"
mkdir -p "$CONFIG_DIR"
$ENGINE run --rm \
$RUN_FLAGS \
-v "$PWD/$CONFIG_DIR:/config" \
"$ZAINOD_TAG" \
$CMD_PREFIX generate-config -o /config/zainod.toml

# Patch gRPC listen address for container networking
sed -i 's/listen_address = "127\.0\.0\.1:8137"/listen_address = "0.0.0.0:8137"/' "$CONFIG_FILE"
info "Patched gRPC listen_address to 0.0.0.0:8137 for container networking"
warn "Review $CONFIG_FILE and set validator_settings for your setup"
fi

info "Starting zainod"
info "Engine: $ENGINE"
info "Image: $ZAINOD_TAG"
info "Config: $CONFIG_FILE"

$ENGINE run --rm \
$RUN_FLAGS \
-p 8137:8137 \
-p 8237:8237 \
-v "$PWD/$CONFIG_DIR:/config" \
"$ZAINOD_TAG" \
$CMD_PREFIX start -c /config/zainod.toml
'''
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "1.92"
channel = "stable"
components = ["rustfmt", "clippy"]
65 changes: 65 additions & 0 deletions utils/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,68 @@ resolve_build_target() {
fi
}

# ------- ZAINOD CONTAINER HELPERS ------------

# Validate the container engine argument.
# Sets ENGINE as a side-effect.
validate_engine() {
ENGINE="${1:?Usage: makers <task> <podman|docker>}"
if [ "$ENGINE" != "podman" ] && [ "$ENGINE" != "docker" ]; then
err "Unknown engine: $ENGINE (use podman or docker)"
exit 1
fi
}

# Abort unless the working tree is clean.
# Respects FORCE=true to override.
require_clean_worktree() {
if [ -n "$(git status --porcelain)" ]; then
if [ "${FORCE:-}" = "true" ]; then
warn "Working directory is dirty — proceeding because FORCE=true"
else
err "Working directory is dirty. Commit your changes or set FORCE=true"
exit 1
fi
fi
}

# Compute image tags from the current rust toolchain and HEAD commit.
# Exports: RUST_VERSION, COMMIT, RUNTIME_TAG, ZAINOD_TAG
compute_zainod_tags() {
RUST_VERSION=$(rustc --version | awk '{print $2}')
COMMIT=$(git rev-parse --short HEAD)
RUNTIME_TAG="zaino-runtime:${RUST_VERSION}-${COMMIT}"
ZAINOD_TAG="zainod:${RUST_VERSION}-${COMMIT}"
}

# Return the engine-specific tail file for the final build stage.
tail_file_for_engine() {
if [ "$ENGINE" = "podman" ]; then
echo "Containerfile.tail"
else
echo "Dockerfile.tail"
fi
}

# Return the command prefix needed to invoke zainod in the container.
# Podman images have ENTRYPOINT ["zainod"], so no prefix is needed.
# Docker images have no entrypoint, so the binary name must be given.
cmd_prefix_for_engine() {
if [ "$ENGINE" = "podman" ]; then
echo ""
else
echo "zainod"
fi
}

# Return extra flags for "$ENGINE run" that differ between engines.
# Podman: --userns=keep-id maps the host UID into the container.
# Docker: --user flag to run as the host user.
run_flags_for_engine() {
if [ "$ENGINE" = "podman" ]; then
echo "--userns=keep-id"
else
echo "--user $(id -u):$(id -g)"
fi
}

Loading