liab-deployments/liab_deployments/deploy/photoview.py

182 lines
4.4 KiB
Python

#
# Deploy Photoview (https://photoview.github.io) media gallery
#
# An example inventory may look like this:
#
# group = [
# (
# 'edu.example.org',
# {
# 'photoview': {
# 'sites': [
# {
# 'serve_address': "photos.example.org",
# 'container_tag': 'docker.io/photoview/photoview:2',
# 'user': ('photos', 1002),
# 'host_port': "4001",
# 'media_mounts': {
# '/home/photos/collections': 'collections',
# },
# },
# ],
# },
#
# ...
#
from pyinfra.api import deploy
from pyinfra import (
host,
)
from pyinfra.operations import (
files,
server,
)
from liab_deployments.operations import (
caddy,
user,
user_systemd,
)
service_unit_tmpl = """\
[Unit]
Description=Podman-managed photoview service
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=no
TimeoutStopSec=300
ExecStartPre=/bin/rm \\
-f %t/%n.ctr-id
# we run `--user 0:0` because the upstream
# container has its own default user, but we
# want it to run rootless and have an easy time
# mapping to the host user
ExecStart=/usr/bin/podman container run \\
--cidfile=%t/%n.ctr-id \\
--cgroups=no-conmon \\
--rm \\
--sdnotify=conmon \\
-d \\
--replace \\
--pull missing \\
--name photoview \\
-e PHOTOVIEW_DATABASE_DRIVER=sqlite \\
-e PHOTOVIEW_SQLITE_PATH=/home/photoview/database/photoview.db \\
-p {host_port}:80 \\
-v /etc/localtime:/etc/localtime:ro \\
-v /etc/timezone:/etc/timezone:ro \\
-v {user_home}/{media_cache_dir}:/home/photoview/media-cache \\
-v {user_home}/{db_dir}:/home/photoview/database \\
{media_mounts} \\
--user 0:0 \\
{container_tag}
ExecStop=/usr/bin/podman stop \\
--ignore -t 10 \\
--cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm \\
-f \\
--ignore -t 10 \\
--cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all
[Install]
WantedBy=default.target
"""
caddyfile_block_tmpl = """\
{serve_address} {{
reverse_proxy localhost:{host_port}
}}
"""
@deploy("Deploy Photoview")
def deploy_sites():
if not hasattr(host.data, 'photoview'):
return
for spec in host.data.photoview.get('sites', []):
_deploy_site(
spec['serve_address'],
spec['container_tag'],
spec['user'],
spec['host_port'],
spec['media_mounts'],
name=spec.get('name', 'photoview'),
caddyfile_block_tmpl=spec.get(
'caddyfile_block_tmpl', caddyfile_block_tmpl
),
db_dir=spec.get('db_dir', 'db'),
media_cache_dir=spec.get('media_cache_dir', 'media_cache'),
)
def _deploy_site(
serve_address,
container_tag,
user_spec: tuple[str, int],
host_port,
media_mounts,
*,
name,
caddyfile_block_tmpl,
db_dir,
media_cache_dir,
):
user_name, uid = user_spec
user_home = f'/home/{user_name}'
user.systemd_service(
user_name,
uid,
user_home,
)
for i in [media_cache_dir, db_dir]:
files.directory(
path=f'{user_home}/{i}',
present=True,
_sudo=True,
_sudo_user=user_name,
)
user_systemd.service_unit(
user_name,
user_home,
name,
service_unit_tmpl.format(
user_home=user_home,
container_tag=container_tag,
host_port=host_port,
db_dir=db_dir,
media_cache_dir=media_cache_dir,
media_mounts=' '.join(
f'-v {src}:/home/photoview/photos/{dest}:ro'
for src, dest in media_mounts.items()
),
),
)
user_systemd.run_service(
user_name,
uid,
name,
)
server.wait(
name="Wait for photoview to start",
port=host_port,
)
caddy.caddyfile_block(
marker=f'PHOTOVIEW {serve_address}',
content=caddyfile_block_tmpl.format(
serve_address=serve_address,
host_port=host_port,
),
)