135 lines
6.7 KiB
Markdown
135 lines
6.7 KiB
Markdown
# Deployment preflight tests
|
|
|
|
Run the deployment preflight on every new node after its qBittorrent and
|
|
Syncthing stacks are running, but before starting the Archive Control daemon
|
|
for normal operation or creating any routes/jobs. It does not contact the
|
|
control daemon, alter Syncthing/qBittorrent state, or print secret contents.
|
|
It does create and remove two unique, zero-byte probe files while proving that
|
|
the live client mount topology permits hard-link staging; no resource data or
|
|
configuration is modified.
|
|
|
|
The script is [`scripts/preflight-deployment.py`](../scripts/preflight-deployment.py).
|
|
It deliberately takes paths and container names as arguments rather than
|
|
assuming hostnames, compose project names, mount paths, credentials, or roles.
|
|
It can therefore be used unchanged on cache and archive nodes.
|
|
|
|
## Requirements
|
|
|
|
- Run it on the Docker host being checked.
|
|
- Use Python 3.8 or newer. The script reads the mounted configuration through
|
|
the client image, so it does not depend on the host Python TOML library.
|
|
- Have Docker CLI access to the daemon.
|
|
- Have the checkout containing the script available on that host, or copy only
|
|
this script into the node's compose directory.
|
|
- Bring up qBittorrent and Syncthing first. Do not start the normal Archive
|
|
Control daemon yet.
|
|
|
|
The disposable client container below uses the exact `archive-client` service
|
|
image, mounts, environment, and network specified by that node's compose file.
|
|
Consequently its qB/Syncthing API probes validate the same runtime environment
|
|
the daemon will use, not the host shell's network namespace.
|
|
|
|
## New-node procedure
|
|
|
|
The example uses generic names. Replace paths, compose project directories,
|
|
service names, and real container names for the node being deployed.
|
|
|
|
```sh
|
|
# 1. Bring up only the local dependencies.
|
|
cd /srv/compose/syncthing-node && docker compose up -d
|
|
cd /srv/compose/qbittorrent-node && docker compose up -d
|
|
|
|
# 2. Create a temporary client from the exact production image/config, without
|
|
# running the daemon or registering it with control.
|
|
cd /srv/compose/ArchiveControl-archive
|
|
docker compose run -d --no-deps --name archive-control-preflight \
|
|
--entrypoint sleep archive-client infinity
|
|
|
|
# 3. Discover the real dependency container names if needed.
|
|
docker ps --format '{{.Names}}'
|
|
|
|
# 4. Run the deployment checks. They include a disposable hard-link probe.
|
|
python3 /path/to/archive-clients/scripts/preflight-deployment.py \
|
|
--client-config /srv/compose/ArchiveControl-archive/client.toml \
|
|
--client-container archive-control-preflight \
|
|
--syncthing-container syncthing-node-syncthing-1 \
|
|
--qbittorrent-container qbittorrent-node-qbittorrent-1
|
|
|
|
# 5. Remove the temporary container, then start the real daemon only after a
|
|
# successful result.
|
|
docker rm -f archive-control-preflight
|
|
docker compose up -d archive-client
|
|
```
|
|
|
|
If the real client configuration is mounted somewhere other than the standard
|
|
`/etc/archive-control/client.toml`, pass that in-container location explicitly:
|
|
|
|
```sh
|
|
python3 scripts/preflight-deployment.py ... \
|
|
--container-config /custom/path/client.toml
|
|
```
|
|
|
|
## Required arguments
|
|
|
|
| Argument | Meaning |
|
|
| --- | --- |
|
|
| `--client-config` | Host path to the exact `client.toml` that will be mounted into the daemon. |
|
|
| `--client-container` | Running disposable or already-running client container to inspect and probe. |
|
|
| `--syncthing-container` | Running Syncthing container for this node. |
|
|
| `--qbittorrent-container` | Running qBittorrent container for this node. |
|
|
| `--container-config` | Optional `client.toml` path inside the client container; defaults to `/etc/archive-control/client.toml`. |
|
|
|
|
## What it checks
|
|
|
|
- The future automatic `routes/<route-id>` path resolves through Docker mounts
|
|
to the same host path as its client-side mapping.
|
|
- That future route path and qBittorrent content root use one client bind
|
|
mount, so hard-link staging remains possible rather than silently falling
|
|
back to a space-consuming copy.
|
|
- Every explicit `qbittorrent.local_path_overrides` entry maps the same host
|
|
path in the qBittorrent and client containers. This protects nested qB save
|
|
paths that use a dedicated bind mount.
|
|
- A real `link(2)` operation between a unique zero-byte file in the qB root
|
|
and one in the future automatic-route root. The probe verifies that both
|
|
names refer to the same inode and removes them unconditionally.
|
|
- Every existing `archive-control:*` Syncthing folder beneath `routes/` still
|
|
has its local directory. This catches a route-root bind-mount migration that
|
|
would otherwise hide an already configured folder and later fail a job with
|
|
`No such file or directory`.
|
|
- The token, qB password, and Syncthing API-key files are non-empty regular
|
|
files with no group/world permissions.
|
|
- The client image can read its configuration and reports usable permissions,
|
|
sparse-file support, and filesystem capabilities for both primary roots and
|
|
every configured qBittorrent local-path override.
|
|
- qBittorrent authentication/version compatibility and Syncthing
|
|
authentication/device identity are healthy from the client container.
|
|
|
|
In particular, if `syncthing.api_root` is the Syncthing configuration root,
|
|
bind its `routes` subdirectory from a dedicated directory beneath qB's data
|
|
root and add a `local_path_overrides` mapping for that exact API path. This
|
|
prevents automatic route folders being created on a small configuration
|
|
filesystem while the client expects to hardlink from the qB data mount.
|
|
|
|
qBittorrent resources may use any `save_path` below `qbittorrent.api_root`.
|
|
At job preflight the client maps that qB API path to its local mount and uses
|
|
it as the resource root; data is never moved to fit Archive Control. A resource
|
|
outside the configured qB API root, or whose resolved directory is unavailable
|
|
or not a real directory in the client container, fails that job before staging
|
|
or eviction. Use `qbittorrent.local_path_overrides` only for a nested qB API
|
|
prefix backed by a distinct client mount; this preflight verifies the Docker
|
|
bind topology for each such override.
|
|
|
|
When converting an existing node, stop its client and Syncthing containers,
|
|
move each existing `routes/<route-id>` directory from the old Syncthing config
|
|
tree into the new qB-backed route-root directory, then recreate Syncthing and
|
|
run this preflight. Do not discard existing route directories: they can contain
|
|
route handshake state or an in-progress transfer namespace.
|
|
|
|
## Failure handling
|
|
|
|
Treat a nonzero exit status as a deployment blocker. Correct the compose
|
|
mounts, `client.toml`, secret permissions, service credentials, or service
|
|
image/version that the message identifies, then rerun the same command. Do not
|
|
work around a mapping failure by creating route folders manually: the route
|
|
provisioner relies on those API/local path mappings being exact.
|