feat(deploy): templatize systemd services on install_dir via Jinja2
Distros reserve /opt for different things (some package managers own it
outright), and a DECNET install that wants to live at /srv/decnet or
/usr/local/decnet had to hand-edit 13 service files post-install.
Converts every deploy/decnet-*.service to a .j2 template keyed on
{{ install_dir }}, rendered by `decnet init` at install time. All other
paths (log_dir, state_dir, runtime_dir, user, group) stay standard —
only install_dir varies.
Changes:
- deploy/decnet-*.service → deploy/decnet-*.service.j2 (13 files).
- decnet init gains --install-dir (default /opt/decnet, preserves
existing behaviour byte-for-byte). Validates absolute-path at the
CLI boundary. Threads through useradd --home-dir and the dir-creation
list so the filesystem layout matches the rendered templates.
- _install_units renders via Jinja2 with StrictUndefined (typo → loud
error, not a silent broken unit). SHA over rendered output so
operators with a custom install_dir get idempotent re-runs.
- decnet.target, tmpfiles.d, polkit rule stay static — they don't
reference install paths.
- 4 new tests: custom install_dir renders into units, default remains
/opt/decnet, relative paths rejected, second run with same custom
dir is idempotent.
This commit is contained in:
@@ -11,9 +11,9 @@ User=decnet
|
||||
Group=decnet
|
||||
# docker.sock is group-readable by 'docker'; the agent needs it for compose.
|
||||
SupplementaryGroups=docker
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet agent --host 0.0.0.0 --port 8765 --agent-dir /etc/decnet/agent
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet agent --host 0.0.0.0 --port 8765 --agent-dir /etc/decnet/agent
|
||||
|
||||
# MACVLAN/IPVLAN management + scapy raw sockets. Granted via ambient caps so
|
||||
# the process starts unprivileged and keeps only these two bits.
|
||||
@@ -30,8 +30,8 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
# /opt/decnet holds release slots + state; the agent reads them and writes its PID.
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
# {{ install_dir }} holds release slots + state; the agent reads them and writes its PID.
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -11,9 +11,9 @@ User=decnet
|
||||
Group=decnet
|
||||
# docker.sock is group-readable by 'docker'; the API ingester tails container logs.
|
||||
SupplementaryGroups=docker
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet api
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet api
|
||||
|
||||
# MACVLAN/IPVLAN setup runs from the API lifespan when the embedded sniffer is on.
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
|
||||
@@ -29,7 +29,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -8,15 +8,15 @@ Wants=network-online.target
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
# /run/decnet is created automatically with the RuntimeDirectory= directive
|
||||
# below (mode 0755, owned by User/Group) and cleaned up on stop. The bus
|
||||
# socket is placed inside it with 0660 perms so only the decnet group can
|
||||
# connect.
|
||||
RuntimeDirectory=decnet
|
||||
RuntimeDirectoryMode=0755
|
||||
ExecStart=/opt/decnet/venv/bin/decnet bus \
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet bus \
|
||||
--socket /run/decnet/bus.sock \
|
||||
--group decnet
|
||||
|
||||
@@ -11,9 +11,9 @@ User=decnet
|
||||
Group=decnet
|
||||
# docker.sock is group-readable by 'docker'; the collector tails container logs.
|
||||
SupplementaryGroups=docker
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet collect
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet collect
|
||||
|
||||
# No privileged network operations.
|
||||
CapabilityBoundingSet=
|
||||
@@ -29,7 +29,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -10,12 +10,12 @@ Wants=network-online.target
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
# Replace <master-host> with the master's LAN address or hostname. The agent
|
||||
# cert bundle at /etc/decnet/agent is reused — the forwarder presents the same
|
||||
# worker identity when it connects to the master's listener.
|
||||
ExecStart=/opt/decnet/venv/bin/decnet forwarder \
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet forwarder \
|
||||
--log-file /var/log/decnet/decnet.log \
|
||||
--master-host ${DECNET_SWARM_MASTER_HOST} \
|
||||
--master-port 6514 \
|
||||
@@ -8,11 +8,11 @@ Wants=network-online.target
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
# Binds 0.0.0.0:6514 so workers across the LAN can connect. 6514 is not a
|
||||
# privileged port (≥1024), so no CAP_NET_BIND_SERVICE is required.
|
||||
ExecStart=/opt/decnet/venv/bin/decnet listener \
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet listener \
|
||||
--host 0.0.0.0 --port 6514 \
|
||||
--ca-dir /etc/decnet/ca \
|
||||
--log-path /var/log/decnet/master.log \
|
||||
@@ -11,9 +11,9 @@ User=decnet
|
||||
Group=decnet
|
||||
# Mutator recomposes decky services via docker compose.
|
||||
SupplementaryGroups=docker
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet mutate --watch
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet mutate --watch
|
||||
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
@@ -28,7 +28,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -8,9 +8,9 @@ Wants=network-online.target decnet-bus.service
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet probe
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet probe
|
||||
|
||||
# TCP connect probes only — no raw sockets required.
|
||||
CapabilityBoundingSet=
|
||||
@@ -26,7 +26,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -8,9 +8,9 @@ Wants=network-online.target decnet-bus.service
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet profiler
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet profiler
|
||||
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
@@ -25,7 +25,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -8,9 +8,9 @@ Wants=network-online.target decnet-bus.service
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet sniffer
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet sniffer
|
||||
|
||||
# scapy needs raw packet access on the MACVLAN host interface.
|
||||
CapabilityBoundingSet=CAP_NET_RAW
|
||||
@@ -26,7 +26,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -8,11 +8,11 @@ Wants=network-online.target
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
# Default bind is loopback — the controller is a master-local orchestrator
|
||||
# reached by the CLI and the web dashboard, not by workers.
|
||||
ExecStart=/opt/decnet/venv/bin/decnet swarmctl --host 127.0.0.1 --port 8770
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet swarmctl --host 127.0.0.1 --port 8770
|
||||
|
||||
# No special capabilities — the controller issues mTLS certs and talks to
|
||||
# workers over TCP on unprivileged ports.
|
||||
@@ -30,7 +30,7 @@ ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
# Reads/writes the CA bundle and the master DB.
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
ReadOnlyPaths=/etc/decnet
|
||||
|
||||
Restart=on-failure
|
||||
@@ -10,12 +10,12 @@ Wants=network-online.target
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet updater \
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet updater \
|
||||
--host 0.0.0.0 --port 8766 \
|
||||
--updater-dir /etc/decnet/updater \
|
||||
--install-dir /opt/decnet \
|
||||
--install-dir {{ install_dir }} \
|
||||
--agent-dir /etc/decnet/agent
|
||||
|
||||
# The updater SIGTERMs the agent and spawns a new one. Same User=decnet means
|
||||
@@ -37,7 +37,7 @@ ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
# Writes release slots, pip installs into venv, manages agent.pid.
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
@@ -8,9 +8,9 @@ Wants=network-online.target
|
||||
Type=simple
|
||||
User=decnet
|
||||
Group=decnet
|
||||
WorkingDirectory=/opt/decnet
|
||||
EnvironmentFile=-/opt/decnet/.env.local
|
||||
ExecStart=/opt/decnet/venv/bin/decnet web
|
||||
WorkingDirectory={{ install_dir }}
|
||||
EnvironmentFile=-{{ install_dir }}/.env.local
|
||||
ExecStart={{ install_dir }}/venv/bin/decnet web
|
||||
|
||||
# Uncomment if you bind the dashboard to a privileged port (80/443):
|
||||
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
@@ -28,7 +28,7 @@ ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
ReadWritePaths=/opt/decnet /var/log/decnet
|
||||
ReadWritePaths={{ install_dir }} /var/log/decnet
|
||||
ReadOnlyPaths=/etc/decnet
|
||||
|
||||
Restart=on-failure
|
||||
Reference in New Issue
Block a user