Init project
This commit is contained in:
parent
de5a8de641
commit
88bdb77e83
10 changed files with 261 additions and 1 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pyinfra-debug.log
|
46
README.md
46
README.md
|
@ -1,3 +1,47 @@
|
||||||
# linkding
|
# linkding
|
||||||
|
|
||||||
Pyinfra that deploy linkding un-dockerized. Mostly to be used in LXD/C.
|
Pyinfra that deploy linkding un-dockerized on a Debian 11 LXD container.
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
|
||||||
|
```
|
||||||
|
# lxc launch images:debian/11 <name>
|
||||||
|
# lxec exec <name> bash
|
||||||
|
# apt update && apt install python3-pip git
|
||||||
|
# pip install pyinfra
|
||||||
|
# git clone https://git.benpro.fr/pyinfra/linkding.git
|
||||||
|
# cd linkding
|
||||||
|
# ###!!!### Edit group_data/production.py
|
||||||
|
# pyinfra inventories/production.py deploy.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrade
|
||||||
|
|
||||||
|
Run `remove.py` then `deploy.py`.
|
||||||
|
|
||||||
|
```
|
||||||
|
# pyinfra inventories/production.py remove.py
|
||||||
|
# pyinfra inventories/production.py deploy.py
|
||||||
|
```
|
||||||
|
|
||||||
|
# Attach a pictures volume
|
||||||
|
|
||||||
|
If required, you can mount external volumes. Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
# lxc config device add <name> <name>_disk disk source=/dev/vdf path=/mnt/pictures readonly=true raw.mount.options=noload
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add it to `group_data/production.py`:
|
||||||
|
|
||||||
|
```
|
||||||
|
photoprism_originals_src='/mnt/pictures'
|
||||||
|
```
|
||||||
|
|
||||||
|
# Run photoprism command
|
||||||
|
|
||||||
|
A systemd-run wrapper is installed and you can use it via `/usr/local/bin/photoprism`.
|
||||||
|
|
||||||
|
```
|
||||||
|
# /usr/local/bin/photoprism config
|
||||||
|
```
|
||||||
|
|
124
deploy.py
Normal file
124
deploy.py
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
from pyinfra import host
|
||||||
|
from pyinfra.operations import apt, server, files, systemd, mysql
|
||||||
|
|
||||||
|
apt.update(
|
||||||
|
name='Update apt repositories',
|
||||||
|
)
|
||||||
|
|
||||||
|
apt.upgrade(
|
||||||
|
name='Upgrade apt packages',
|
||||||
|
)
|
||||||
|
|
||||||
|
apt.packages(
|
||||||
|
name='Install dependencies',
|
||||||
|
packages=host.data.app['deps'],
|
||||||
|
update=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
server.user(
|
||||||
|
name='Create UNIX user',
|
||||||
|
user=host.data.unix_account['user'],
|
||||||
|
home=host.data.unix_account['home'],
|
||||||
|
shell=host.data.unix_account['shell'],
|
||||||
|
ensure_home=True,
|
||||||
|
system=True,
|
||||||
|
comment=f"{host.data.unix_account['user']} system user",
|
||||||
|
present=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
for directory in host.data.data_directories:
|
||||||
|
files.directory(
|
||||||
|
name=f'Make required directory {directory}',
|
||||||
|
path=directory,
|
||||||
|
user=host.data.unix_account['user'],
|
||||||
|
group=host.data.unix_account['group'],
|
||||||
|
mode=755
|
||||||
|
)
|
||||||
|
|
||||||
|
files.download(
|
||||||
|
name='Download undocker',
|
||||||
|
src=host.data.undocker['url'],
|
||||||
|
dest=host.data.undocker['bin_path'],
|
||||||
|
user='root',
|
||||||
|
group='root',
|
||||||
|
mode='755',
|
||||||
|
sha256sum=host.data.undocker['sha256'],
|
||||||
|
)
|
||||||
|
|
||||||
|
files.directory(
|
||||||
|
name='Create undocker cache',
|
||||||
|
path=host.data.undocker['cache'],
|
||||||
|
user='root',
|
||||||
|
group='root',
|
||||||
|
mode=755
|
||||||
|
)
|
||||||
|
|
||||||
|
if not host.fact.file(f"{host.data.undocker['cache']}/{host.data.app['name']}.tar"):
|
||||||
|
server.shell(
|
||||||
|
name='Download Docker image',
|
||||||
|
chdir=host.data.undocker['cache'],
|
||||||
|
commands=[
|
||||||
|
f"skopeo copy {host.data.app['image']} docker-archive:{host.data.app['name']}.tar"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
files.directory(
|
||||||
|
name='Create undocker app destination',
|
||||||
|
path=host.data.undocker['app_dst'],
|
||||||
|
user='root',
|
||||||
|
group='root',
|
||||||
|
mode=755
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the bootstrap file is here, undockerized image should be fine, don't extract again
|
||||||
|
if not host.fact.file(f"{host.data.undocker['app_dst']}/etc/linkding/bootstrap.sh"):
|
||||||
|
server.shell(
|
||||||
|
name='Undocker the Docker image',
|
||||||
|
chdir=host.data.undocker['app_dst'],
|
||||||
|
commands=[
|
||||||
|
f"undocker {host.data.undocker['cache']}/{host.data.app['name']}.tar - | tar -xv"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Since the app is badly Dockerized we need to do a chown
|
||||||
|
files.directory(
|
||||||
|
name=f"Ensure {host.data.undocker['app_dst']}/etc/linkding has correct rights",
|
||||||
|
path=f"{host.data.undocker['app_dst']}/etc/linkding",
|
||||||
|
user=host.data.unix_account['user'],
|
||||||
|
group=host.data.unix_account['group'],
|
||||||
|
)
|
||||||
|
|
||||||
|
files.template(
|
||||||
|
name='Set env file',
|
||||||
|
src='templates/env.j2',
|
||||||
|
dest=host.data.systemd['EnvironmentFile'],
|
||||||
|
mode='600',
|
||||||
|
)
|
||||||
|
|
||||||
|
files.template(
|
||||||
|
name='Set systemd service file',
|
||||||
|
src=f'templates/systemd.service.j2',
|
||||||
|
dest=f"/etc/systemd/system/{host.data.app['name']}.service",
|
||||||
|
mode='644',
|
||||||
|
)
|
||||||
|
|
||||||
|
systemd.daemon_reload(
|
||||||
|
name='Reload systemd',
|
||||||
|
user_mode=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
systemd.service(
|
||||||
|
name='Enable systemd service',
|
||||||
|
service=f"{host.data.app['name']}.service",
|
||||||
|
running=True,
|
||||||
|
restarted=True,
|
||||||
|
enabled=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
files.template(
|
||||||
|
name='Set systemd-run wrapper',
|
||||||
|
src='templates/systemd-run.sh.j2',
|
||||||
|
dest=f"/usr/local/bin/{host.data.app['name']}",
|
||||||
|
mode='755',
|
||||||
|
)
|
||||||
|
|
37
group_data/all.py
Normal file
37
group_data/all.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
app = {
|
||||||
|
'name': 'linkding',
|
||||||
|
'deps': ['skopeo', 'curl'],
|
||||||
|
'image': 'docker://sissbruecker/linkding:latest',
|
||||||
|
}
|
||||||
|
|
||||||
|
undocker = {
|
||||||
|
'url': 'https://git.sr.ht/~motiejus/undocker/refs/download/v1.0.2/undocker-linux-amd64-v1.0.2',
|
||||||
|
'sha256': 'b937e69e774c530c080c1f6685763ca7db4dc58520a5a2054d111e1504f47688',
|
||||||
|
'bin_path': '/usr/local/bin/undocker',
|
||||||
|
'app_dst': f"/opt/{app['name']}",
|
||||||
|
'cache': '/var/cache/undocker',
|
||||||
|
}
|
||||||
|
|
||||||
|
unix_account = {
|
||||||
|
'user': app['name'],
|
||||||
|
'group': app['name'],
|
||||||
|
'home': f"/home/{app['name']}",
|
||||||
|
'shell': '/bin/false',
|
||||||
|
}
|
||||||
|
|
||||||
|
data_directories = [
|
||||||
|
f"{unix_account['home']}/data"
|
||||||
|
]
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
'Description': app['name'],
|
||||||
|
'User': unix_account['user'],
|
||||||
|
'Group': unix_account['group'],
|
||||||
|
'EnvironmentFile': f"/etc/{app['name']}.env",
|
||||||
|
'WorkingDirectory': f"/etc/{app['name']}",
|
||||||
|
'RootDirectory': f"{undocker['app_dst']}",
|
||||||
|
'ReadWritePaths': '+/etc/linkding',
|
||||||
|
'BindPaths': f"{data_directories[0]}:/etc/linkding/data",
|
||||||
|
'BindReadOnlyPaths': '/etc/resolv.conf /etc/hosts',
|
||||||
|
'ExecStart': '/etc/linkding/bootstrap.sh',
|
||||||
|
}
|
1
inventories/dev.py
Normal file
1
inventories/dev.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
dev_servers = ['@ssh/debian11.home.arpa']
|
1
inventories/production.py
Normal file
1
inventories/production.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
production_servers = ['@local']
|
16
remove.py
Normal file
16
remove.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from pyinfra import host
|
||||||
|
from pyinfra.operations import server, files
|
||||||
|
|
||||||
|
if host.fact.file(host.data.undocker_cache+'/photoprism.tar'):
|
||||||
|
server.shell(
|
||||||
|
name='Remove photoprism Docker image',
|
||||||
|
chdir=host.data.undocker_cache,
|
||||||
|
commands=['rm photoprism.tar'],
|
||||||
|
)
|
||||||
|
|
||||||
|
if host.fact.directory(host.data.undocker_dst):
|
||||||
|
server.shell(
|
||||||
|
name='Remove undocker destination',
|
||||||
|
commands=['rm -rf {}'.format(host.data.undocker_dst)],
|
||||||
|
)
|
||||||
|
|
3
templates/env.j2
Normal file
3
templates/env.j2
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# From Dockerfile
|
||||||
|
VIRTUAL_ENV=/opt/venv
|
||||||
|
PATH=/opt/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
13
templates/systemd-run.sh.j2
Normal file
13
templates/systemd-run.sh.j2
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
systemd-run \
|
||||||
|
--pty \
|
||||||
|
--property=User={{ host.data.systemd['User'] }} \
|
||||||
|
--property=Group={{ host.data.systemd['Group'] }} \
|
||||||
|
--property=EnvironmentFile={{ host.data.systemd['EnvironmentFile'] }} \
|
||||||
|
--property=WorkingDirectory={{ host.data.systemd['WorkingDirectory'] }} \
|
||||||
|
--property=RootDirectory={{ host.data.systemd['RootDirectory'] }} \
|
||||||
|
--property='BindPaths={{ host.data.systemd['BindPaths'] }}' \
|
||||||
|
--property='BindReadOnlyPaths={{ host.data.systemd['BindReadOnlyPaths'] }}' \
|
||||||
|
"$@"
|
20
templates/systemd.service.j2
Normal file
20
templates/systemd.service.j2
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[Unit]
|
||||||
|
Description={{ host.data.systemd['Description'] }}
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ host.data.systemd['User'] }}
|
||||||
|
Group={{ host.data.systemd['Group'] }}
|
||||||
|
EnvironmentFile={{ host.data.systemd['EnvironmentFile'] }}
|
||||||
|
WorkingDirectory={{ host.data.systemd['WorkingDirectory'] }}
|
||||||
|
RootDirectory={{ host.data.systemd['RootDirectory'] }}
|
||||||
|
ReadWritePaths={{ host.data.systemd['ReadWritePaths'] }}
|
||||||
|
BindPaths={{ host.data.systemd['BindPaths'] }}
|
||||||
|
BindReadOnlyPaths={{ host.data.systemd['BindReadOnlyPaths'] }}
|
||||||
|
ExecStart={{ host.data.systemd['ExecStart'] }}
|
||||||
|
KillSignal=SIGQUIT
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
Reference in a new issue