From 55f8021b0a930cc54858fcb60d256600b047e0ae Mon Sep 17 00:00:00 2001 From: Samuel Enocsson Date: Mon, 12 Oct 2026 18:45:47 +0200 Subject: [PATCH] build: agent is the image's default entrypoint; drop the old server examples (#7) --- Dockerfile | 6 +++--- cmd/agent/main.go | 31 +++++++++++++++++++++++++++-- deploy/agent.compose.yml | 19 ++++++++++++++++++ docker-compose.example.yml | 32 ------------------------------ docker-compose.local.yml | 40 -------------------------------------- env.sample | 15 -------------- 6 files changed, 51 insertions(+), 92 deletions(-) create mode 100644 deploy/agent.compose.yml delete mode 100644 docker-compose.example.yml delete mode 100644 docker-compose.local.yml delete mode 100644 env.sample diff --git a/Dockerfile b/Dockerfile index 8305278..f017080 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ COPY . . ARG VERSION=dev ARG COMMIT=unknown ARG BUILD_TIME=unknown -RUN for cmd in server:package-updater master:updater-master agent:updater-agent; do \ +RUN for cmd in master:updater-master agent:updater-agent; do \ CGO_ENABLED=0 go build \ -ldflags="-s -w \ -X main.version=${VERSION} \ @@ -20,5 +20,5 @@ RUN for cmd in server:package-updater master:updater-master agent:updater-agent; FROM alpine:3.20 RUN apk add --no-cache docker-cli docker-cli-compose ca-certificates wget COPY --from=build /out/ /usr/local/bin/ -EXPOSE 8080 -ENTRYPOINT ["/usr/local/bin/package-updater"] +EXPOSE 8080 8090 +ENTRYPOINT ["/usr/local/bin/updater-agent"] diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 0833e42..03138ec 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -16,6 +16,7 @@ import ( "github.com/shcizo/package-updater/internal/agent/config" "github.com/shcizo/package-updater/internal/agent/conn" "github.com/shcizo/package-updater/internal/agent/inventory" + "github.com/shcizo/package-updater/internal/agent/updater" "github.com/shcizo/package-updater/internal/logging" ) @@ -42,19 +43,45 @@ func run() error { } defer dockerCli.Close() + auth, err := updater.LoadRegistryAuth(cfg.DockerConfig, logger) + if err != nil { + return fmt.Errorf("registry auth: %w", err) + } + resolver := updater.NewResolver(dockerCli, auth) + exec := updater.NewDispatch( + updater.NewComposeExecutor(dockerCli, resolver, cfg.SelfID, updater.ExecDocker), + updater.NewSwarmExecutor(dockerCli, resolver, auth), + ) + runner := updater.NewRunner(exec, cfg.UpdateTimeout) + collector := inventory.New(dockerCli, cfg.SelfID) agent := conn.New(conn.Config{ URL: cfg.MasterURL, Token: cfg.Token, Fingerprint: cfg.MasterKey, Version: version, - }, collector, logger) + }, collector, runner, logger) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() + logMounts(ctx, dockerCli, cfg.SelfID, logger) + go runner.Run(ctx) go watchLoop(ctx, dockerCli, agent, logger) - logger.Info("agent_starting", "version", version, "master_url", cfg.MasterURL) + logger.Info("agent_starting", "version", version, "master_url", cfg.MasterURL, + "update_timeout", cfg.UpdateTimeout.String(), "registry_auth", auth != nil) return agent.Run(ctx) } +// logMounts warns at startup when Compose updates cannot work. Each job reads +// the mounts again itself; this is only for the operator. +func logMounts(ctx context.Context, cli updater.SelfInspector, selfID string, log *slog.Logger) { + roots, err := updater.MountRoots(ctx, cli, selfID) + if err != nil || len(roots) == 0 { + log.Warn("no_stack_mounts", "error", err, + "hint", "Compose updates will fail; mount each stacks directory at the same path, read-write") + return + } + log.Info("stack_mounts", "roots", roots) +} + // watchLoop keeps a Docker event subscription alive. When the stream breaks // (daemon restart), it resubscribes and asks for a refresh, because events // may have been missed in between. It also asks for a full re-inventory every diff --git a/deploy/agent.compose.yml b/deploy/agent.compose.yml new file mode 100644 index 0000000..a1e30db --- /dev/null +++ b/deploy/agent.compose.yml @@ -0,0 +1,19 @@ +# Example agent deployment, one per machine. The master UI generates this +# snippet with MASTER_URL, MASTER_KEY and AGENT_TOKEN filled in (Agents -> Add +# agent); the token is shown once. +services: + updater-agent: + image: gitea.shcizo.se/shcizo/package-updater:latest + entrypoint: ["/usr/local/bin/updater-agent"] + restart: unless-stopped + environment: + MASTER_URL: wss://10.0.0.5:8090/agent + MASTER_KEY: sha256: + AGENT_TOKEN: + UPDATE_TIMEOUT: 5m + # Optional registry credentials (a config.json written by docker login): + # DOCKER_CONFIG: /docker-config + volumes: + - /var/run/docker.sock:/var/run/docker.sock + # - /path/to/stacks:/path/to/stacks # mount each stacks directory at the same path, read-write + # - /root/.docker/config.json:/docker-config/config.json:ro diff --git a/docker-compose.example.yml b/docker-compose.example.yml deleted file mode 100644 index ba61110..0000000 --- a/docker-compose.example.yml +++ /dev/null @@ -1,32 +0,0 @@ -# Example deployment of package-updater itself. -# Copy to /home/shcizo/self-hosted/package-updater/docker-compose.yml -# and create a .env file alongside containing: -# UPDATER_API_KEY= - -services: - package-updater: - image: registry.example.com/package-updater:latest - container_name: package-updater - restart: unless-stopped - environment: - - UPDATER_API_KEY=${UPDATER_API_KEY} - - STACKS_ROOT=/home/shcizo/self-hosted - - LOG_LEVEL=info - - PORT=8080 - - UPDATE_TIMEOUT=5m - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /home/shcizo/self-hosted:/home/shcizo/self-hosted:ro - labels: - - "se.shcizo.auto-update=true" - networks: - - proxy - healthcheck: - test: ["CMD", "wget", "-q", "-O-", "http://localhost:8080/healthz"] - interval: 30s - timeout: 5s - retries: 3 - -networks: - proxy: - external: true diff --git a/docker-compose.local.yml b/docker-compose.local.yml deleted file mode 100644 index c41b6b2..0000000 --- a/docker-compose.local.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Local development / smoke-test compose file. -# Unlike docker-compose.example.yml (which pulls a pre-built image from a registry), -# this file BUILDS the image from the local Dockerfile so you can iterate without -# pushing anywhere. -# -# Usage: -# 1. Copy .env.example to .env and fill in UPDATER_API_KEY (e.g. `openssl rand -hex 32`) -# 2. Optionally adjust STACKS_ROOT below to point at a directory you actually have -# compose stacks in (default points at this repo dir, which is fine for smoke tests) -# 3. docker compose -f docker-compose.local.yml up --build -# 4. curl -s http://localhost:8080/healthz | jq -# 5. curl -sH "Authorization: Bearer $UPDATER_API_KEY" -d '{"image":"test"}' \ -# http://localhost:8080/update | jq - -services: - package-updater: - build: - context: . - args: - VERSION: dev-local - COMMIT: ${COMMIT:-unknown} - BUILD_TIME: ${BUILD_TIME:-unknown} - container_name: package-updater-local - restart: unless-stopped - environment: - - UPDATER_API_KEY=${UPDATER_API_KEY} - - STACKS_ROOT=${STACKS_ROOT:-/tmp} - - LOG_LEVEL=debug - - PORT=8080 - - UPDATE_TIMEOUT=5m - ports: - - "8080:8080" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ${STACKS_ROOT:-/tmp}:${STACKS_ROOT:-/tmp}:ro - healthcheck: - test: ["CMD", "wget", "-q", "-O-", "http://localhost:8080/healthz"] - interval: 30s - timeout: 5s - retries: 3 diff --git a/env.sample b/env.sample deleted file mode 100644 index 8d4badb..0000000 --- a/env.sample +++ /dev/null @@ -1,15 +0,0 @@ -# Copy to .env (which is gitignored) and fill in real values: -# cp env.sample .env && $EDITOR .env - -# Required: bearer token clients must send as `Authorization: Bearer `. -# Generate with: openssl rand -hex 32 -UPDATER_API_KEY= - -# Optional: root directory the service is allowed to manage stacks under. -# Defaults to /tmp for local smoke testing. For real use, set to your stacks dir, -# e.g. /home/shcizo/self-hosted -# STACKS_ROOT=/home/shcizo/self-hosted - -# Optional: build info baked into the binary via -ldflags. Not required. -# COMMIT=$(git rev-parse --short HEAD) -# BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)