diff --git a/Dockerfile b/Dockerfile index cd699afb2..ea63ab19d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -205,12 +205,20 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \ # Create shim for pulse-docker-agent to maintain backward compatibility RUN echo '#!/bin/sh' > /usr/local/bin/pulse-docker-agent && \ - echo 'exec /usr/local/bin/pulse-agent --enable-docker "$@"' >> /usr/local/bin/pulse-docker-agent && \ + echo 'exec /usr/local/bin/pulse-agent "$@"' >> /usr/local/bin/pulse-docker-agent && \ chmod +x /usr/local/bin/pulse-docker-agent COPY --from=backend-builder /app/VERSION /VERSION -ENV PULSE_NO_AUTO_UPDATE=true +RUN mkdir -p /var/lib/pulse-agent && chmod 700 /var/lib/pulse-agent + +ENV PULSE_NO_AUTO_UPDATE=true \ + PULSE_DISABLE_AUTO_UPDATE=true \ + PULSE_ENABLE_HOST=false \ + PULSE_ENABLE_DOCKER=true \ + PULSE_AGENT_ID_FILE=/var/lib/pulse-agent/agent-id + +VOLUME ["/var/lib/pulse-agent"] ENTRYPOINT ["/usr/local/bin/pulse-docker-agent"] diff --git a/docs/DOCKER.md b/docs/DOCKER.md index b93ed5d17..8ca45b784 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -147,6 +147,28 @@ When multiple containers have updates available, an **"Update All"** button appe - Agent must have Docker socket access (`/var/run/docker.sock`) - Registry must be accessible for update detection (public registries work automatically) +### Running the agent as a Docker container + +For container-only Docker monitoring, use the Docker-agent image with a persistent state volume. The agent stores its server-bound identity at `/var/lib/pulse-agent/agent-id`; keeping that directory on a named volume prevents token binding failures after the agent container is recreated. + +```yaml +services: + pulse-docker-agent: + image: rcourtman/pulse-docker-agent:latest + restart: unless-stopped + environment: + PULSE_URL: http://:7655 + PULSE_TOKEN: + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - pulse_agent_state:/var/lib/pulse-agent + +volumes: + pulse_agent_state: +``` + +The image defaults to Docker monitoring only (`PULSE_ENABLE_DOCKER=true`, `PULSE_ENABLE_HOST=false`) and disables in-container binary self-updates. Update it by pulling a newer container image. + ### Private Registries For private registries, ensure your Docker daemon has credentials configured: diff --git a/docs/UNIFIED_AGENT.md b/docs/UNIFIED_AGENT.md index 8a9dc4b15..24c52a67d 100644 --- a/docs/UNIFIED_AGENT.md +++ b/docs/UNIFIED_AGENT.md @@ -170,6 +170,7 @@ These flags are accepted by the `pulse-agent` binary directly but are **not** av | `--kube-max-pods` | `PULSE_KUBE_MAX_PODS` | Max pods per report | `200` | | `--disable-auto-update` | `PULSE_DISABLE_AUTO_UPDATE` | Disable auto-updates | `false` | | `--disable-docker-update-checks` | `PULSE_DISABLE_DOCKER_UPDATE_CHECKS` | Disable Docker image update detection | `false` | +| `--agent-id-file` | `PULSE_AGENT_ID_FILE` | Read and persist the agent ID in a file; mount this path when running the agent in a container | *(unset)* | | `--report-ip` | `PULSE_REPORT_IP` | Override reported IP (multi-NIC) | *(auto)* | | `--disable-ceph` | `PULSE_DISABLE_CEPH` | Disable local Ceph status polling | `false` | | `--tag` | `PULSE_TAGS` | Apply tags (repeatable or CSV) | *(none)* | diff --git a/scripts/tests/test-docker-agent-image.sh b/scripts/tests/test-docker-agent-image.sh new file mode 100755 index 000000000..2810ea3c6 --- /dev/null +++ b/scripts/tests/test-docker-agent-image.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# Smoke tests for the pulse-docker-agent image contract. + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +DOCKERFILE="${ROOT_DIR}/Dockerfile" + +if [[ ! -f "${DOCKERFILE}" ]]; then + echo "Dockerfile not found at ${DOCKERFILE}" >&2 + exit 1 +fi + +agent_runtime_block="$( + awk ' + /^FROM .* AS agent_runtime$/ { in_block=1 } + in_block { print } + in_block && /^FROM .* AS runtime$/ { exit } + ' "${DOCKERFILE}" +)" + +failures=0 + +assert_contains() { + local desc="$1" + local needle="$2" + if grep -Fq "${needle}" <<<"${agent_runtime_block}"; then + echo "[PASS] ${desc}" + else + echo "[FAIL] ${desc}" >&2 + echo "Missing: ${needle}" >&2 + ((failures++)) + fi +} + +assert_not_contains() { + local desc="$1" + local needle="$2" + if grep -Fq "${needle}" <<<"${agent_runtime_block}"; then + echo "[FAIL] ${desc}" >&2 + echo "Unexpected: ${needle}" >&2 + ((failures++)) + else + echo "[PASS] ${desc}" + fi +} + +assert_contains "docker-agent image persists agent identity by default" "PULSE_AGENT_ID_FILE=/var/lib/pulse-agent/agent-id" +assert_contains "docker-agent image declares persistent state volume" 'VOLUME ["/var/lib/pulse-agent"]' +assert_contains "docker-agent image disables host metrics by default" "PULSE_ENABLE_HOST=false" +assert_contains "docker-agent image enables docker metrics by default" "PULSE_ENABLE_DOCKER=true" +assert_contains "docker-agent image disables binary self-update" "PULSE_DISABLE_AUTO_UPDATE=true" +assert_contains "legacy entrypoint delegates to pulse-agent" 'exec /usr/local/bin/pulse-agent "$@"' +assert_not_contains "legacy shim does not force docker flag over user args" 'exec /usr/local/bin/pulse-agent --enable-docker "$@"' + +if (( failures > 0 )); then + echo "docker-agent image contract failed (${failures})" >&2 + exit 1 +fi + +echo "All docker-agent image contract tests passed"