build: agent is the image's default entrypoint; drop the old server examples (#7)

This commit is contained in:
2026-10-12 18:45:47 +02:00
parent 601c6da0d4
commit 55f8021b0a
6 changed files with 51 additions and 92 deletions
+3 -3
View File
@@ -8,7 +8,7 @@ COPY . .
ARG VERSION=dev ARG VERSION=dev
ARG COMMIT=unknown ARG COMMIT=unknown
ARG BUILD_TIME=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 \ CGO_ENABLED=0 go build \
-ldflags="-s -w \ -ldflags="-s -w \
-X main.version=${VERSION} \ -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 FROM alpine:3.20
RUN apk add --no-cache docker-cli docker-cli-compose ca-certificates wget RUN apk add --no-cache docker-cli docker-cli-compose ca-certificates wget
COPY --from=build /out/ /usr/local/bin/ COPY --from=build /out/ /usr/local/bin/
EXPOSE 8080 EXPOSE 8080 8090
ENTRYPOINT ["/usr/local/bin/package-updater"] ENTRYPOINT ["/usr/local/bin/updater-agent"]
+29 -2
View File
@@ -16,6 +16,7 @@ import (
"github.com/shcizo/package-updater/internal/agent/config" "github.com/shcizo/package-updater/internal/agent/config"
"github.com/shcizo/package-updater/internal/agent/conn" "github.com/shcizo/package-updater/internal/agent/conn"
"github.com/shcizo/package-updater/internal/agent/inventory" "github.com/shcizo/package-updater/internal/agent/inventory"
"github.com/shcizo/package-updater/internal/agent/updater"
"github.com/shcizo/package-updater/internal/logging" "github.com/shcizo/package-updater/internal/logging"
) )
@@ -42,19 +43,45 @@ func run() error {
} }
defer dockerCli.Close() 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) collector := inventory.New(dockerCli, cfg.SelfID)
agent := conn.New(conn.Config{ agent := conn.New(conn.Config{
URL: cfg.MasterURL, Token: cfg.Token, Fingerprint: cfg.MasterKey, Version: version, 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) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop() defer stop()
logMounts(ctx, dockerCli, cfg.SelfID, logger)
go runner.Run(ctx)
go watchLoop(ctx, dockerCli, agent, logger) 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) 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 // watchLoop keeps a Docker event subscription alive. When the stream breaks
// (daemon restart), it resubscribes and asks for a refresh, because events // (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 // may have been missed in between. It also asks for a full re-inventory every
+19
View File
@@ -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:<fingerprint shown in the master UI>
AGENT_TOKEN: <token shown once when the agent was added>
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
-32
View File
@@ -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=<openssl rand -hex 32>
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
-40
View File
@@ -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
-15
View File
@@ -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 <value>`.
# 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)