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
|
||||
|
||||
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