From e5becfd87fb0f0887a7cde37b21ac1dca138ffe0 Mon Sep 17 00:00:00 2001 From: jarek Date: Mon, 16 Feb 2026 08:16:29 +0100 Subject: [PATCH] 1.0.18 updater --- updater/Dockerfile | 49 ++++++++++++++++++++++++++++++++++++ updater/update.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 updater/Dockerfile create mode 100644 updater/update.sh diff --git a/updater/Dockerfile b/updater/Dockerfile new file mode 100644 index 0000000..f0e544a --- /dev/null +++ b/updater/Dockerfile @@ -0,0 +1,49 @@ +# syntax=docker/dockerfile:1.4 +# Dockhand Updater - Minimal sidecar for self-updates +# Dockhand pre-creates the new container, this sidecar just does +# stop/rm/rename/network-connect/start via Docker CLI. + +# Stage 1: Build minimal Wolfi rootfs with apko +FROM alpine:3.21 AS os-builder + +ARG TARGETARCH + +WORKDIR /work + +ARG APKO_VERSION=0.30.34 +RUN apk add --no-cache curl \ + && ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \ + && curl -sL "https://github.com/chainguard-dev/apko/releases/download/v${APKO_VERSION}/apko_${APKO_VERSION}_linux_${ARCH}.tar.gz" \ + | tar -xz --strip-components=1 -C /usr/local/bin \ + && chmod +x /usr/local/bin/apko + +RUN APKO_ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "x86_64") \ + && printf '%s\n' \ + "contents:" \ + " repositories:" \ + " - https://packages.wolfi.dev/os" \ + " keyring:" \ + " - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub" \ + " packages:" \ + " - docker-cli" \ + " - busybox" \ + "entrypoint:" \ + " command: /bin/sh -l" \ + "archs:" \ + " - ${APKO_ARCH}" \ + > apko.yaml + +RUN apko build apko.yaml dockhand-updater:latest output.tar \ + && mkdir -p rootfs \ + && tar -xf output.tar \ + && LAYER=$(tar -tf output.tar | grep '.tar.gz$' | head -1) \ + && tar -xzf "$LAYER" -C rootfs + +# Stage 2: Scratch + minimal rootfs +FROM scratch + +COPY --from=os-builder /work/rootfs/ / +COPY update.sh /update.sh +RUN chmod +x /update.sh + +ENTRYPOINT ["/update.sh"] diff --git a/updater/update.sh b/updater/update.sh new file mode 100644 index 0000000..62408fe --- /dev/null +++ b/updater/update.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# Dockhand Self-Update Sidecar +# Dockhand pre-creates the new container. This script just does: +# stop old → rm old → rename new → connect networks → start → verify +# +# Required env vars: +# OLD_CONTAINER_ID - Container ID of the running Dockhand to replace +# NEW_CONTAINER_ID - Container ID of the pre-created replacement +# CONTAINER_NAME - Original container name to restore after rename +# NETWORKS - Space-separated network names (optional) +# NETWORK_OPTS_ - Per-network flags for docker network connect (optional) +# +# Optional: +# STOP_TIMEOUT - Timeout for stopping container (default: 30) + +set -e + +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; } +error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2; } + +[ -z "$OLD_CONTAINER_ID" ] && { error "OLD_CONTAINER_ID not set"; exit 1; } +[ -z "$NEW_CONTAINER_ID" ] && { error "NEW_CONTAINER_ID not set"; exit 1; } +[ -z "$CONTAINER_NAME" ] && { error "CONTAINER_NAME not set"; exit 1; } + +STOP_TIMEOUT="${STOP_TIMEOUT:-30}" +log "Starting Dockhand update" +log " Old: ${OLD_CONTAINER_ID:0:12}, New: ${NEW_CONTAINER_ID:0:12}, Name: $CONTAINER_NAME" + +log "Stopping container (timeout: ${STOP_TIMEOUT}s)..." +docker stop -t "$STOP_TIMEOUT" "$OLD_CONTAINER_ID" || { error "Failed to stop container"; exit 1; } +log "Container stopped" + +log "Removing old container..." +docker rm "$OLD_CONTAINER_ID" || { error "Failed to remove old container"; exit 1; } +log "Old container removed" + +log "Renaming container..." +docker rename "$NEW_CONTAINER_ID" "$CONTAINER_NAME" || { error "Failed to rename container"; exit 1; } +log "Container renamed to $CONTAINER_NAME" + +if [ -n "$NETWORKS" ]; then + for NET in $NETWORKS; do + OPTS_VAR="NETWORK_OPTS_$(echo "$NET" | tr '.-' '__')" + OPTS=$(eval echo "\$$OPTS_VAR" 2>/dev/null || true) + log "Connecting to network $NET ${OPTS:+($OPTS)}" + # shellcheck disable=SC2086 + docker network connect $OPTS "$NET" "$NEW_CONTAINER_ID" || log " Warning: failed to connect to $NET" + done + log "Networks connected" +fi + +log "Starting container..." +docker start "$NEW_CONTAINER_ID" || { error "Failed to start container"; exit 1; } + +sleep 2 +STATE=$(docker inspect -f '{{.State.Status}}' "$NEW_CONTAINER_ID" 2>/dev/null) +if [ "$STATE" = "running" ]; then + log "Container is running" + log "Update completed successfully!" +else + error "Container state: $STATE (expected running)" + exit 1 +fi