liab-deployments/liab_deployments/deploy/dumpthings.py

220 lines
5.4 KiB
Python

from pathlib import Path
from pyinfra.api import deploy
from pyinfra import (
host,
)
from pyinfra.operations import (
files,
server,
systemd,
)
from liab_deployments.operations import (
caddy,
user,
user_systemd,
)
# TODO without a static site, the --root-path must not be set to `api/`
service_unit_tmpl = """\
[Unit]
Description=DumpThings service
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
{service_params}
WorkingDirectory={user_home}
# we use inline script to be able to amend the PATH
ExecStart=bash -c "PATH={user_home}/.local/bin:$PATH; \\
dump-things-service \\
--port {host_port} \\
--config {config_filename} \\
--root-path /api \\
{service_args} \\
{user_home}/{data_dir} \\
"
[Install]
WantedBy=default.target
"""
caddy_config_tmpl = """\
{serve_address} {{
reverse_proxy localhost:{host_port}
}}
"""
caddy_config_w_staticsite_tmpl = """\
{serve_address} {{
handle_path /api/* {{
reverse_proxy localhost:{host_port}
}}
handle {{
root * {staticsite_root_dir}/
file_server
}}
}}
"""
# TODO make a parameter with this default
dumpthings_collections_config = """\
type: collections
version: 1
"""
@deploy("Deploy dumpthings")
def deploy_sites():
"""
Installing multiple dumpthings sites using different versions under
the same user account is not supported.
"""
if not hasattr(host.data, 'dumpthings'):
return
for spec in host.data.dumpthings.get('sites', []):
staticsite_root_dir = spec.get('staticsite_root_dir')
caddyfile_block_tmpl = spec.get('caddyfile_block_tmpl')
if caddyfile_block_tmpl is None:
caddyfile_block_tmpl = caddy_config_w_staticsite_tmpl \
if staticsite_root_dir else caddy_config_tmpl
kwargs = {
k: spec.get(k)
for k in (
'service_args',
'systemd_service_params',
'python_version',
'dumpthings_version',
)
}
_deploy_site(
spec['serve_address'],
spec['config_file_asset'],
spec['user'],
spec['host_port'],
spec['collections'],
data_dir=spec.get('data_dir', 'data'),
name=spec.get('name', 'dumpthings'),
caddyfile_block_tmpl=caddyfile_block_tmpl,
staticsite_root_dir=staticsite_root_dir,
**kwargs
)
def _deploy_site(
serve_address: str,
config_file_asset: str,
user_spec: tuple[str, int],
host_port: int,
collections: list[tuple],
*,
data_dir: str,
name: str,
caddyfile_block_tmpl: str,
service_args: str | None = None,
systemd_service_params: str,
dumpthings_version: str | None = None,
python_version: str | None = None,
staticsite_root_dir: str | None = None,
):
user_name, uid = user_spec
user_home = f'/home/{user_name}'
config_file = f'{user_home}/{name}_config.yaml'
user.systemd_service(
user_name,
uid,
user_home,
)
user.uv(
user_name,
)
pyarg = f"--python {python_version}" if python_version else ""
dtarg = f'dump-things-service{dumpthings_version or ""}'
server.shell(
name=f'Install dump-things-service {name!r}',
commands=[
f"$HOME/.local/bin/uv tool install {pyarg} {dtarg}"
],
_sudo_user=user_name,
)
for i in [data_dir] + [f'{data_dir}/{c[0]}' for c in collections]:
files.directory(
path=f'{user_home}/{i}',
present=True,
_sudo_user=user_name,
)
user_systemd.service_unit(
user_name,
user_home,
name,
service_unit_tmpl.format(
user_home=user_home,
host_port=host_port,
service_params=systemd_service_params or '',
service_args=service_args or '',
config_filename=config_file,
data_dir=data_dir,
),
)
files.block(
name='Dump-things API config',
path=config_file,
content=Path(config_file_asset).read_text(),
present=True,
try_prevent_shell_expansion=True,
_sudo_user=user_name,
)
files.block(
name='Dump-things collections config',
path=f'{user_home}/{data_dir}/.dumpthings.yaml',
content=dumpthings_collections_config,
present=True,
try_prevent_shell_expansion=True,
_sudo_user=user_name,
)
for c in collections:
if not c[1]:
continue
files.block(
name=f'Dump-things "{c[0]}" collection config',
path=f'{user_home}/{data_dir}/{c[0]}/.dumpthings.yaml',
content=Path(c[1]).read_text(),
present=True,
try_prevent_shell_expansion=True,
_sudo_user=user_name,
)
user_systemd.run_service(
user_name,
uid,
name,
)
server.wait(
name=f"Wait for dumpthings {serve_address!r} to start",
port=host_port,
)
caddyfile_block_params = {
'serve_address': serve_address,
'host_port': host_port,
}
if staticsite_root_dir:
caddyfile_block_params['staticsite_root_dir'] = staticsite_root_dir
caddy.caddyfile_block(
marker=f'DUMPTHINGS {serve_address}',
content=caddyfile_block_tmpl.format(**caddyfile_block_params),
)