Skip to content
Closed
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
7 changes: 5 additions & 2 deletions deploy/fleet-ctl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ on_node() { # this script, run on the node over ssh
ssh "$NODE_SSH" "FLEET_CTL_ON_NODE=1 FLEET_PREFIX=$NODE_PREFIX bash -s -- $flag $(printf '%q ' "$@")" < "$SELF"
}

compose() { run docker compose --env-file "$PREFIX/compose.env" -p zkm-fleet -f "$PREFIX/deploy/docker-compose.yml" "$@"; }
# The release's compose file, then this site's changes to it, if any: upgrades replace only the release's.
COMPOSE=(docker compose --env-file "$PREFIX/compose.env" -p zkm-fleet -f "$PREFIX/deploy/docker-compose.yml")
[ ! -f "$PREFIX/deploy/docker-compose.override.yml" ] || COMPOSE+=(-f "$PREFIX/deploy/docker-compose.override.yml")
compose() { run "${COMPOSE[@]}" "$@"; }
# Naming a profiled service starts it, so recreate MCP only where install.sh enabled it.
control_services() { echo scheduler; grep -q '^COMPOSE_PROFILES=.*mcp' "$PREFIX/compose.env" 2>/dev/null && echo mcp || true; }
scheduler_healthy() {
Expand Down Expand Up @@ -228,7 +231,7 @@ status() {
return
fi
echo "== control host: healthz $(curl -fs http://127.0.0.1:9191/healthz || echo FAILING)"
docker compose --env-file "$PREFIX/compose.env" -p zkm-fleet -f "$PREFIX/deploy/docker-compose.yml" ps || true
"${COMPOSE[@]}" ps || true
"$PREFIX/bin/fleetctl" node || true
"$PREFIX/bin/fleetctl" app || true
if [ -n "$NODE_SSH" ]; then on_node status; fi
Expand Down
43 changes: 43 additions & 0 deletions deploy/tests/test_fleet_ctl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Exercise fleet-ctl.sh's compose calls with stand-in docker, curl and fleetctl."""
import os
from pathlib import Path
import subprocess

ROOT = Path(__file__).resolve().parents[2]


def executable(path, body):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("#!/bin/bash\n" + body)
path.chmod(0o755)


def status(tmp_path):
"""`fleet-ctl.sh status` on a control prefix; returns the docker command lines it ran."""
prefix, commands, log = tmp_path / "fleet", tmp_path / "commands", tmp_path / "docker.log"
(prefix / "deploy").mkdir(parents=True, exist_ok=True)
(prefix / "deploy/docker-compose.yml").write_text("services: {}\n")
(prefix / "compose.env").write_text("")
executable(commands / "docker", f'echo "$*" >> {log}\n')
executable(commands / "curl", "exit 1\n")
executable(prefix / "bin/fleetctl", "exit 0\n")
environment = {**os.environ, "PATH": f"{commands}:{os.environ['PATH']}", "FLEET_PREFIX": str(prefix),
"FLEET_CTL_ENV": str(tmp_path / "none.env"), "FLEET_NODE_SSH": ""}
result = subprocess.run(["bash", str(ROOT / "deploy/fleet-ctl.sh"), "status"],
env=environment, capture_output=True, text=True, timeout=20)
assert result.returncode == 0, result.stderr
return prefix, log.read_text().splitlines()


def test_compose_uses_the_release_file_alone(tmp_path):
prefix, calls = status(tmp_path)
assert calls == [f"compose --env-file {prefix}/compose.env -p zkm-fleet -f {prefix}/deploy/docker-compose.yml ps"]


def test_compose_applies_the_site_override_after_the_release_file(tmp_path):
override = tmp_path / "fleet/deploy/docker-compose.override.yml"
override.parent.mkdir(parents=True)
override.write_text("services: {}\n")
prefix, calls = status(tmp_path)
assert calls == [f"compose --env-file {prefix}/compose.env -p zkm-fleet -f {prefix}/deploy/docker-compose.yml "
f"-f {override} ps"]
5 changes: 4 additions & 1 deletion docs/maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ deploy/fleet-ctl.sh app cleanup APP-0 # leftovers of a failed app sta
An upgrade copies what it replaces to `backup/` first, then waits for the scheduler's
health check or the node's reconnection. Upgrade the scheduler and nodes together when a
release changes the protocol. The scheduler and MCP are managed with Docker Compose; the
node uses `sudo systemctl`, and `FLEET_NODE_UNIT` names a non-default unit.
node uses `sudo systemctl`, and `FLEET_NODE_UNIT` names a non-default unit. Keep a site's
changes to the release's compose file, such as another gateway or an existing Postgres
volume, in `deploy/docker-compose.override.yml`: an upgrade replaces only
`docker-compose.yml`, and every compose call applies the override after it.
A third `node upgrade` argument also installs `fleet-proof-app` and restarts the app
endpoint units listed in `FLEET_PROOF_APP_UNITS`.

Expand Down
Loading