Compare commits
74 Commits
c6cc7d4c6c
...
feat-backu
| Author | SHA1 | Date | |
|---|---|---|---|
|
a31a07bd16
|
|||
|
54a951b96a
|
|||
|
e1379bc480
|
|||
|
037e0cab9b
|
|||
|
2655869814
|
|||
|
0e96b5030d
|
|||
|
a217c79e7d
|
|||
|
6a16ebf084
|
|||
|
2617aa2bd2
|
|||
|
b686e4da4d
|
|||
|
439c239ac8
|
|||
|
acf599f905
|
|||
|
eae4f5e27b
|
|||
|
4fbe9bd5de
|
|||
|
dcc4970b20
|
|||
|
2eac1362b5
|
|||
|
e3d8479397
|
|||
|
91c5eab236
|
|||
|
ca7f089fe6
|
|||
|
479e256b1e
|
|||
|
11e5b5752e
|
|||
|
392938d0fb
|
|||
|
2cc059104e
|
|||
|
d09a26b73a
|
|||
|
097676f569
|
|||
|
e878661cb3
|
|||
|
cb50c1c515
|
|||
|
33de71a087
|
|||
|
fbd5fa5faa
|
|||
|
faf7d58f78
|
|||
|
0a75378bbc
|
|||
|
bdd74bdf2e
|
|||
|
78bee84061
|
|||
|
7b81858af6
|
|||
|
08fda17561
|
|||
|
841bd38807
|
|||
|
fb1fd711c2
|
|||
|
ecf714eda7
|
|||
|
81f693938e
|
|||
|
10d67861a0
|
|||
|
3f5befb44d
|
|||
|
1b75ddaef2
|
|||
|
7d6ef77e64
|
|||
|
ae7c20a7aa
|
|||
|
67df03efca
|
|||
|
48bb8c9d33
|
|||
|
5b53cb30ac
|
|||
|
f2bc221663
|
|||
|
b41a50006b
|
|||
|
c2ea2cdb39
|
|||
|
7e67409393
|
|||
|
6882d61f8e
|
|||
|
47a63202b8
|
|||
|
af289f1e28
|
|||
|
b08f681c92
|
|||
|
8dfd061991
|
|||
|
306d4bf8d0
|
|||
|
dbd679aa8b
|
|||
|
47ed9c11c1
|
|||
|
f9ad08fd09
|
|||
|
4c7338f857
|
|||
|
a95da35389
|
|||
|
c74683cfe7
|
|||
|
9dff413867
|
|||
|
23a2bae7ec
|
|||
|
942bb7d999
|
|||
|
6ff7a7e3b4
|
|||
|
8ae28e64f4
|
|||
|
f7e8248cac
|
|||
|
2af9066dec
|
|||
|
e3b2e064c0
|
|||
|
380a54cb25
|
|||
|
d5078186e7
|
|||
|
57bb696e6e
|
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
exclude_paths:
|
exclude_paths:
|
||||||
- ".ansible/"
|
- ".ansible/"
|
||||||
|
- ".gitea/"
|
||||||
- "galaxy.roles/"
|
- "galaxy.roles/"
|
||||||
- "Taskfile.yml"
|
- "Taskfile.yml"
|
||||||
|
|||||||
4
.crushignore
Normal file
4
.crushignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
ansible-vault-password-file
|
||||||
|
|
||||||
|
*secrets.yml
|
||||||
|
*secrets.toml
|
||||||
@@ -6,14 +6,9 @@ insert_final_newline = true
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
[*.yml]
|
[*.{yml,yaml,yml.j2}]
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.yml.j2]
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[Vagrantfile]
|
[Vagrantfile]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
[Makefile]
|
|
||||||
indent_style = tab
|
|
||||||
|
|||||||
50
.gitea/workflows/lint.yml
Normal file
50
.gitea/workflows/lint.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
name: Linting
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
yamllint:
|
||||||
|
name: YAML Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install yamllint
|
||||||
|
run: pip install yamllint
|
||||||
|
|
||||||
|
- name: Run yamllint
|
||||||
|
run: yamllint . --format colored
|
||||||
|
|
||||||
|
ansible-lint:
|
||||||
|
name: Ansible Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pip install ansible ansible-lint
|
||||||
|
|
||||||
|
# Создаем пустой vault password file если он указан в конфиге, но отсутствует
|
||||||
|
- name: Fix vault issue
|
||||||
|
run: |
|
||||||
|
if grep -q "vault_password_file" ansible.cfg && [ ! -f ansible-vault-password-file ]; then
|
||||||
|
echo "Creating empty vault password file for CI..."
|
||||||
|
echo "foobar" > ansible-vault-password-file
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run ansible-lint
|
||||||
|
run: ansible-lint .
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@
|
|||||||
/ansible-vault-password-file
|
/ansible-vault-password-file
|
||||||
/temp
|
/temp
|
||||||
*.retry
|
*.retry
|
||||||
|
|
||||||
|
__pycache__
|
||||||
|
|||||||
25
.yamllint.yml
Normal file
25
.yamllint.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
extends: default
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- ".ansible/"
|
||||||
|
- "galaxy.roles/"
|
||||||
|
|
||||||
|
rules:
|
||||||
|
# Правила, требуемые ansible-lint
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
|
comments-indentation: false
|
||||||
|
braces:
|
||||||
|
max-spaces-inside: 1
|
||||||
|
octal-values:
|
||||||
|
forbid-implicit-octal: true
|
||||||
|
forbid-explicit-octal: true
|
||||||
|
|
||||||
|
# Дополнительные настройки (опционально)
|
||||||
|
line-length:
|
||||||
|
max: 120
|
||||||
|
allow-non-breakable-words: true
|
||||||
|
allow-non-breakable-inline-mappings: true
|
||||||
|
document-start: disable # Не требовать --- в начале файла
|
||||||
|
truthy:
|
||||||
|
level: warning
|
||||||
69
AGENTS.md
Normal file
69
AGENTS.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# AGENTS GUIDE
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Ansible-based server automation for personal services. Playbooks provision Dockerized apps (e.g., gitea, authelia, homepage, miniflux, wakapi, memos) via per-app users, Caddy proxy, and Yandex Docker Registry. Secrets are managed with Ansible Vault.
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
- Playbooks: `playbook-*.yml` (per service), `playbook-all-*.yml` for grouped actions.
|
||||||
|
- Inventory: `production.yml` (ungrouped host `server`).
|
||||||
|
- Variables: `vars/*.yml` (app configs, images), secrets in `vars/secrets.yml` (vault-encrypted).
|
||||||
|
- Roles: custom roles under `roles/` (e.g., `eget`, `owner`, `secrets`) plus galaxy roles fetched to `galaxy.roles/`.
|
||||||
|
- Files/templates: service docker-compose and backup templates under `files/`, shared templates under `templates/`.
|
||||||
|
- Scripts: helper Python scripts in `scripts/` (SMTP utilities) and `files/backups/backup-all.py`.
|
||||||
|
- CI: `.gitea/workflows/lint.yml` runs yamllint and ansible-lint.
|
||||||
|
- Hooks: `lefthook.yml` references local hooks in `/home/av/projects/private/git-hooks` (gitleaks, vault check).
|
||||||
|
- Formatting: `.editorconfig` enforces LF, trailing newline, 4-space indent; YAML/Jinja use 2-space indent.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
- Copy vault password sample: `cp ansible-vault-password-file.dist ansible-vault-password-file` (needed for ansible and CI).
|
||||||
|
- Install galaxy roles: `ansible-galaxy role install --role-file requirements.yml --force` (or `task install-roles`).
|
||||||
|
- Ensure `yq`, `task`, `ansible` installed per README requirements.
|
||||||
|
|
||||||
|
## Tasks (taskfile)
|
||||||
|
- `task install-roles` — install galaxy roles into `galaxy.roles/`.
|
||||||
|
- `task ssh` — SSH to target using inventory (`production.yml`).
|
||||||
|
- `task btop` — run `btop` on remote.
|
||||||
|
- `task encrypt|decrypt -- <files>` — ansible-vault helpers.
|
||||||
|
- Authelia helpers:
|
||||||
|
- `task authelia-cli -- <args>` — run authelia CLI in docker.
|
||||||
|
- `task authelia-validate-config` — render `files/authelia/configuration.template.yml` with secrets and validate via authelia docker image.
|
||||||
|
- `task authelia-gen-random-string LEN=64` — generate random string.
|
||||||
|
- `task authelia-gen-secret-and-hash LEN=72` — generate hashed secret.
|
||||||
|
- `task format-py-files` — run Black via docker (pyfound/black).
|
||||||
|
|
||||||
|
## Ansible Usage
|
||||||
|
- Inventory: `production.yml` with `server` host. `ansible.cfg` points to `./ansible-vault-password-file` and `./galaxy.roles` for roles path.
|
||||||
|
- Typical deploy example (from README): `ansible-playbook -i production.yml --diff playbook-gitea.yml`.
|
||||||
|
- Per-app playbooks: `playbook-<app>.yml`; grouped runs: `playbook-all-setup.yml`, `playbook-all-applications.yml`, `playbook-upgrade.yml`, etc.
|
||||||
|
- Secrets: encrypted `vars/secrets.yml`; additional `files/<app>/secrets.yml` used for templating (e.g., Authelia). Respect `.crushignore` ignoring vault files.
|
||||||
|
- Templates: many `docker-compose.template.yml` and `*.template.sh` files under `files/*` plus shared `templates/env.j2`. Use `vars/*.yml` to supply values.
|
||||||
|
- Custom roles:
|
||||||
|
- `roles/eget`: installs `eget` tool; see defaults/vars for version/source.
|
||||||
|
- `roles/owner`: manages user/group and env template.
|
||||||
|
- `roles/secrets`: manages vault-related items.
|
||||||
|
|
||||||
|
## Linting & CI
|
||||||
|
- Local lint configs: `.yamllint.yml`, `.ansible-lint.yml` (excludes `.ansible/`, `.gitea/`, `galaxy.roles/`, `Taskfile.yml`).
|
||||||
|
- CI (.gitea/workflows/lint.yml) installs `yamllint` and `ansible-lint` and runs `yamllint .` then `ansible-lint .`; creates dummy vault file if missing.
|
||||||
|
- Pre-commit via lefthook (local hooks path): runs `gitleaks git --staged` and secret-file vault check script.
|
||||||
|
|
||||||
|
## Coding/Templating Conventions
|
||||||
|
- Indentation: 2 spaces for YAML/Jinja (`.editorconfig`), 4 spaces default elsewhere.
|
||||||
|
- End-of-line: LF; ensure final newline.
|
||||||
|
- Template suffixes `.template.yml`, `.yml.j2`, `.template.sh` are rendered via Ansible `template` module.
|
||||||
|
- Avoid committing real secrets; `.crushignore` excludes `ansible-vault-password-file` and `*secrets.yml`.
|
||||||
|
- Service directories under `files/` hold docker-compose and backup templates; ensure per-app users and registry settings align with `vars/*.yml`.
|
||||||
|
|
||||||
|
## Testing/Validation
|
||||||
|
- YAML lint: `yamllint .` (CI default).
|
||||||
|
- Ansible lint: `ansible-lint .` (CI default).
|
||||||
|
- Authelia config validation: `task authelia-validate-config` (renders with secrets then validates via docker).
|
||||||
|
- Black formatting for Python helpers: `task format-py-files`.
|
||||||
|
- Python types validation with mypy: `mypy <file.py>`.
|
||||||
|
|
||||||
|
## Operational Notes
|
||||||
|
- Deployments rely on `production.yml` inventory and per-app playbooks; run with `--diff` for visibility.
|
||||||
|
- Yandex Docker Registry auth helper: `files/yandex-docker-registry-auth.sh`.
|
||||||
|
- Backups: templates and scripts under `files/backups/` per service; `backup-all.py` orchestrates.
|
||||||
|
- Home network/DNS reference in README (Yandex domains).
|
||||||
|
- Ensure `ansible-vault-password-file` present for vault operations and CI.
|
||||||
10
files/authelia/backup.template.sh
Normal file
10
files/authelia/backup.template.sh
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
echo "{{ app_name }}: backup data with gobackups"
|
||||||
|
|
||||||
|
(cd "{{ base_dir }}" && gobackup perform --config "{{ gobackup_config }}")
|
||||||
|
|
||||||
|
echo "{{ app_name }}: done."
|
||||||
@@ -1026,7 +1026,7 @@ storage:
|
|||||||
##
|
##
|
||||||
local:
|
local:
|
||||||
## Path to the SQLite3 Database.
|
## Path to the SQLite3 Database.
|
||||||
path: '/config/authelia_storage.sqlite3'
|
path: '/data/authelia_storage.sqlite3'
|
||||||
|
|
||||||
##
|
##
|
||||||
## MySQL / MariaDB (Storage Provider)
|
## MySQL / MariaDB (Storage Provider)
|
||||||
@@ -1292,7 +1292,8 @@ identity_providers:
|
|||||||
## configured has the RS256 algorithm. For RSA keys (RS or PS) the minimum is a 2048 bit key.
|
## configured has the RS256 algorithm. For RSA keys (RS or PS) the minimum is a 2048 bit key.
|
||||||
jwks:
|
jwks:
|
||||||
-
|
-
|
||||||
## Key ID embedded into the JWT header for key matching. Must be an alphanumeric string with 7 or less characters.
|
## Key ID embedded into the JWT header for key matching.
|
||||||
|
## Must be an alphanumeric string with 7 or less characters.
|
||||||
## This value is automatically generated if not provided. It's recommended to not configure this.
|
## This value is automatically generated if not provided. It's recommended to not configure this.
|
||||||
# key_id: 'example'
|
# key_id: 'example'
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ services:
|
|||||||
|
|
||||||
authelia_app:
|
authelia_app:
|
||||||
container_name: 'authelia_app'
|
container_name: 'authelia_app'
|
||||||
image: 'docker.io/authelia/authelia:4.39.13'
|
image: 'docker.io/authelia/authelia:4.39.14'
|
||||||
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
|
user: '{{ user_create_result.uid }}:{{ user_create_result.group }}'
|
||||||
restart: 'unless-stopped'
|
restart: 'unless-stopped'
|
||||||
networks:
|
networks:
|
||||||
@@ -10,9 +10,10 @@ services:
|
|||||||
- "monitoring_network"
|
- "monitoring_network"
|
||||||
volumes:
|
volumes:
|
||||||
- "{{ config_dir }}:/config"
|
- "{{ config_dir }}:/config"
|
||||||
|
- "{{ data_dir }}:/data"
|
||||||
|
|
||||||
authelia_redis:
|
authelia_redis:
|
||||||
image: valkey/valkey:9-alpine
|
image: valkey/valkey:9.0-alpine
|
||||||
container_name: authelia_redis
|
container_name: authelia_redis
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
16
files/authelia/gobackup.template.yml
Normal file
16
files/authelia/gobackup.template.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# https://gobackup.github.io/configuration
|
||||||
|
|
||||||
|
models:
|
||||||
|
|
||||||
|
authelia:
|
||||||
|
compress_with:
|
||||||
|
type: 'tgz'
|
||||||
|
storages:
|
||||||
|
local:
|
||||||
|
type: 'local'
|
||||||
|
path: '{{ backups_dir }}'
|
||||||
|
keep: 3
|
||||||
|
databases:
|
||||||
|
users:
|
||||||
|
type: sqlite
|
||||||
|
path: "{{ (data_dir, 'authelia_storage.sqlite3') | path_join }}"
|
||||||
488
files/backups/backup-all.py
Normal file
488
files/backups/backup-all.py
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Backup script for all applications
|
||||||
|
Automatically discovers and runs backup scripts for all users,
|
||||||
|
then creates restic backups and sends notifications.
|
||||||
|
"""
|
||||||
|
import itertools
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
import pwd
|
||||||
|
from abc import ABC
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
import requests
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
# Default config path
|
||||||
|
CONFIG_PATH = Path("/etc/backup/config.toml")
|
||||||
|
|
||||||
|
# File name to store directories and files to back up
|
||||||
|
BACKUP_TARGETS_FILE = "backup-targets"
|
||||||
|
|
||||||
|
# Default directory fo backups (relative to app dir)
|
||||||
|
# Used when backup-targets file not exists
|
||||||
|
BACKUP_DEFAULT_DIR = "backups"
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||||
|
handlers=[
|
||||||
|
logging.StreamHandler(sys.stdout),
|
||||||
|
logging.FileHandler("/var/log/backup-all.log"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
host_name: str
|
||||||
|
roots: List[Path]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Application:
|
||||||
|
path: Path
|
||||||
|
owner: str
|
||||||
|
|
||||||
|
|
||||||
|
class Storage(ABC):
|
||||||
|
def backup(self, backup_dirs: List[str]) -> bool:
|
||||||
|
"""Backup directories"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class ResticStorage(Storage):
|
||||||
|
TYPE_NAME = "restic"
|
||||||
|
|
||||||
|
def __init__(self, name: str, params: Dict[str, Any]):
|
||||||
|
self.name = name
|
||||||
|
self.restic_repository = str(params.get("restic_repository", ""))
|
||||||
|
self.restic_password = str(params.get("restic_password", ""))
|
||||||
|
self.aws_access_key_id = str(params.get("aws_access_key_id", ""))
|
||||||
|
self.aws_secret_access_key = str(params.get("aws_secret_access_key", ""))
|
||||||
|
self.aws_default_region = str(params.get("aws_default_region", ""))
|
||||||
|
|
||||||
|
if not all(
|
||||||
|
[
|
||||||
|
self.restic_repository,
|
||||||
|
self.restic_password,
|
||||||
|
self.aws_access_key_id,
|
||||||
|
self.aws_secret_access_key,
|
||||||
|
self.aws_default_region,
|
||||||
|
]
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"Missing storage configuration values for backend ResticStorage: '{self.name}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
def backup(self, backup_dirs: List[str]) -> bool:
|
||||||
|
if not backup_dirs:
|
||||||
|
logger.warning("No backup directories found")
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
return self.__backup_internal(backup_dirs)
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
logger.error("Restic backup process failed: %s", exc)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __backup_internal(self, backup_dirs: List[str]) -> bool:
|
||||||
|
logger.info("Starting restic backup")
|
||||||
|
logger.info("Destination: %s", self.restic_repository)
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env.update(
|
||||||
|
{
|
||||||
|
"RESTIC_REPOSITORY": self.restic_repository,
|
||||||
|
"RESTIC_PASSWORD": self.restic_password,
|
||||||
|
"AWS_ACCESS_KEY_ID": self.aws_access_key_id,
|
||||||
|
"AWS_SECRET_ACCESS_KEY": self.aws_secret_access_key,
|
||||||
|
"AWS_DEFAULT_REGION": self.aws_default_region,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
backup_cmd = ["restic", "backup", "--verbose"] + backup_dirs
|
||||||
|
result = subprocess.run(backup_cmd, env=env, capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.error("Restic backup failed: %s", result.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info("Restic backup completed successfully")
|
||||||
|
|
||||||
|
check_cmd = ["restic", "check"]
|
||||||
|
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.error("Restic check failed: %s", result.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info("Restic check completed successfully")
|
||||||
|
|
||||||
|
forget_cmd = [
|
||||||
|
"restic",
|
||||||
|
"forget",
|
||||||
|
"--compact",
|
||||||
|
"--prune",
|
||||||
|
"--keep-daily",
|
||||||
|
"90",
|
||||||
|
"--keep-monthly",
|
||||||
|
"36",
|
||||||
|
]
|
||||||
|
result = subprocess.run(forget_cmd, env=env, capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.error("Restic forget/prune failed: %s", result.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info("Restic forget/prune completed successfully")
|
||||||
|
|
||||||
|
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.error("Final restic check failed: %s", result.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info("Final restic check completed successfully")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class Notifier(ABC):
|
||||||
|
def send(self, html_message: str):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramNotifier(Notifier):
|
||||||
|
TYPE_NAME = "telegram"
|
||||||
|
|
||||||
|
def __init__(self, name: str, params: Dict[str, Any]):
|
||||||
|
self.name = name
|
||||||
|
self.telegram_bot_token = str(params.get("telegram_bot_token", ""))
|
||||||
|
self.telegram_chat_id = str(params.get("telegram_chat_id", ""))
|
||||||
|
if not all(
|
||||||
|
[
|
||||||
|
self.telegram_bot_token,
|
||||||
|
self.telegram_chat_id,
|
||||||
|
]
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"Missing notification configuration values for backend {name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def send(self, html_message: str):
|
||||||
|
url = f"https://api.telegram.org/bot{self.telegram_bot_token}/sendMessage"
|
||||||
|
data = {
|
||||||
|
"chat_id": self.telegram_chat_id,
|
||||||
|
"parse_mode": "HTML",
|
||||||
|
"text": html_message,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, data=data, timeout=30)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
logger.info("Telegram notification sent successfully")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"Failed to send Telegram notification: {response.status_code} - {response.text}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BackupManager:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
config: Config,
|
||||||
|
roots: List[Path],
|
||||||
|
storages: List[Storage],
|
||||||
|
notifiers: List[Notifier],
|
||||||
|
):
|
||||||
|
self.errors: List[str] = []
|
||||||
|
self.warnings: List[str] = []
|
||||||
|
self.successful_backups: List[str] = []
|
||||||
|
self.config = config
|
||||||
|
self.roots: List[Path] = roots
|
||||||
|
self.storages = storages
|
||||||
|
self.notifiers = notifiers
|
||||||
|
|
||||||
|
def find_applications(self) -> List[Application]:
|
||||||
|
"""Get all application directories and their owners."""
|
||||||
|
applications: List[Application] = []
|
||||||
|
source_dirs = itertools.chain(*(root.iterdir() for root in self.roots))
|
||||||
|
|
||||||
|
for app_dir in source_dirs:
|
||||||
|
if "lost+found" in str(app_dir):
|
||||||
|
continue
|
||||||
|
if app_dir.is_dir():
|
||||||
|
try:
|
||||||
|
stat_info = app_dir.stat()
|
||||||
|
owner = pwd.getpwuid(stat_info.st_uid).pw_name
|
||||||
|
applications.append(Application(path=app_dir, owner=owner))
|
||||||
|
except (KeyError, OSError) as e:
|
||||||
|
logger.warning(f"Could not get owner for {app_dir}: {e}")
|
||||||
|
|
||||||
|
return applications
|
||||||
|
|
||||||
|
def find_backup_script(self, app_dir: str) -> Optional[str]:
|
||||||
|
"""Find backup script in user's home directory"""
|
||||||
|
possible_scripts = [
|
||||||
|
os.path.join(app_dir, "backup.sh"),
|
||||||
|
os.path.join(app_dir, "backup"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for script_path in possible_scripts:
|
||||||
|
if os.path.exists(script_path):
|
||||||
|
# Check if file is executable
|
||||||
|
if os.access(script_path, os.X_OK):
|
||||||
|
return script_path
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"Backup script {script_path} exists but is not executable"
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def run_app_backup(self, script_path: str, app_dir: str, username: str) -> bool:
|
||||||
|
"""Run backup script as the specified user"""
|
||||||
|
try:
|
||||||
|
logger.info(f"Running backup script {script_path} (user {username})")
|
||||||
|
|
||||||
|
# Use su to run the script as the user
|
||||||
|
cmd = ["su", "--login", username, "--command", script_path]
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=app_dir,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=3600, # 1 hour timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
logger.info(f"Backup script for {username} completed successfully")
|
||||||
|
self.successful_backups.append(username)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
error_msg = f"Backup script {script_path} failed with return code {result.returncode}"
|
||||||
|
if result.stderr:
|
||||||
|
error_msg += f": {result.stderr}"
|
||||||
|
logger.error(error_msg)
|
||||||
|
self.errors.append(f"App {username}: {error_msg}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
error_msg = f"Backup script {script_path} timed out"
|
||||||
|
logger.error(error_msg)
|
||||||
|
self.errors.append(f"App {username}: {error_msg}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Failed to run backup script {script_path}: {str(e)}"
|
||||||
|
logger.error(error_msg)
|
||||||
|
self.errors.append(f"App {username}: {error_msg}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_backup_directories(self) -> List[str]:
|
||||||
|
"""Collect backup targets according to backup-targets rules"""
|
||||||
|
backup_dirs: List[str] = []
|
||||||
|
applications = self.find_applications()
|
||||||
|
|
||||||
|
def parse_targets_file(targets_file: Path) -> List[str]:
|
||||||
|
"""Parse backup-targets file, skipping comments and empty lines."""
|
||||||
|
targets: List[str] = []
|
||||||
|
try:
|
||||||
|
for raw_line in targets_file.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
targets.append(line)
|
||||||
|
except OSError as e:
|
||||||
|
warning_msg = f"Could not read backup targets file {targets_file}: {e}"
|
||||||
|
logger.warning(warning_msg)
|
||||||
|
self.warnings.append(warning_msg)
|
||||||
|
return targets
|
||||||
|
|
||||||
|
for app in applications:
|
||||||
|
app_dir = app.path
|
||||||
|
targets_file = app_dir / BACKUP_TARGETS_FILE
|
||||||
|
resolved_targets: List[Path] = []
|
||||||
|
|
||||||
|
if targets_file.exists():
|
||||||
|
# Read custom targets defined by the application.
|
||||||
|
for target_line in parse_targets_file(targets_file):
|
||||||
|
target_path = Path(target_line)
|
||||||
|
if not target_path.is_absolute():
|
||||||
|
target_path = (app_dir / target_path).resolve()
|
||||||
|
else:
|
||||||
|
target_path = target_path.resolve()
|
||||||
|
if target_path.exists():
|
||||||
|
resolved_targets.append(target_path)
|
||||||
|
else:
|
||||||
|
warning_msg = (
|
||||||
|
f"Backup target does not exist for {app_dir}: {target_path}"
|
||||||
|
)
|
||||||
|
logger.warning(warning_msg)
|
||||||
|
self.warnings.append(warning_msg)
|
||||||
|
else:
|
||||||
|
# Fallback to default backups directory when no list is provided.
|
||||||
|
default_target = (app_dir / BACKUP_DEFAULT_DIR).resolve()
|
||||||
|
if default_target.exists():
|
||||||
|
resolved_targets.append(default_target)
|
||||||
|
else:
|
||||||
|
warning_msg = f"Default backup path does not exist for {app_dir}: {default_target}"
|
||||||
|
logger.warning(warning_msg)
|
||||||
|
self.warnings.append(warning_msg)
|
||||||
|
|
||||||
|
for target in resolved_targets:
|
||||||
|
target_str = str(target)
|
||||||
|
if target_str not in backup_dirs:
|
||||||
|
backup_dirs.append(target_str)
|
||||||
|
|
||||||
|
return backup_dirs
|
||||||
|
|
||||||
|
def send_notification(self, success: bool) -> None:
|
||||||
|
"""Send notification to Notifiers"""
|
||||||
|
|
||||||
|
if success and not self.errors:
|
||||||
|
message = f"<b>{self.config.host_name}</b>: бекап успешно завершен!"
|
||||||
|
if self.successful_backups:
|
||||||
|
message += f"\n\nУспешные бекапы: {', '.join(self.successful_backups)}"
|
||||||
|
else:
|
||||||
|
message = f"<b>{self.config.host_name}</b>: бекап завершен с ошибками!"
|
||||||
|
|
||||||
|
if self.successful_backups:
|
||||||
|
message += (
|
||||||
|
f"\n\n✅ Успешные бекапы: {', '.join(self.successful_backups)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.warnings:
|
||||||
|
message += f"\n\n⚠️ Предупреждения:\n" + "\n".join(self.warnings)
|
||||||
|
|
||||||
|
if self.errors:
|
||||||
|
message += f"\n\n❌ Ошибки:\n" + "\n".join(self.errors)
|
||||||
|
|
||||||
|
for notificator in self.notifiers:
|
||||||
|
try:
|
||||||
|
notificator.send(message)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to send notification: {str(e)}")
|
||||||
|
|
||||||
|
def run_backup_process(self) -> bool:
|
||||||
|
"""Main backup process"""
|
||||||
|
logger.info("Starting backup process")
|
||||||
|
|
||||||
|
# Get all home directories
|
||||||
|
applications = self.find_applications()
|
||||||
|
logger.info(f"Found {len(applications)} application directories")
|
||||||
|
|
||||||
|
# Process each user's backup
|
||||||
|
for app in applications:
|
||||||
|
app_dir = str(app.path)
|
||||||
|
username = app.owner
|
||||||
|
logger.info(f"Processing backup for app: {app_dir} (user {username})")
|
||||||
|
|
||||||
|
# Find backup script
|
||||||
|
backup_script = self.find_backup_script(app_dir)
|
||||||
|
|
||||||
|
if backup_script is None:
|
||||||
|
warning_msg = (
|
||||||
|
f"No backup script found for app: {app_dir} (user {username})"
|
||||||
|
)
|
||||||
|
logger.warning(warning_msg)
|
||||||
|
self.warnings.append(warning_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.run_app_backup(backup_script, app_dir, username)
|
||||||
|
|
||||||
|
# Get backup directories
|
||||||
|
backup_dirs = self.get_backup_directories()
|
||||||
|
logger.info(f"Found backup directories: {backup_dirs}")
|
||||||
|
|
||||||
|
overall_success = True
|
||||||
|
|
||||||
|
for storage in self.storages:
|
||||||
|
backup_result = storage.backup(backup_dirs)
|
||||||
|
if not backup_result:
|
||||||
|
self.errors.append("Restic backup failed")
|
||||||
|
|
||||||
|
# Determine overall success
|
||||||
|
overall_success = overall_success and backup_result
|
||||||
|
|
||||||
|
# Send notification
|
||||||
|
self.send_notification(overall_success)
|
||||||
|
|
||||||
|
logger.info("Backup process completed")
|
||||||
|
|
||||||
|
if self.errors:
|
||||||
|
logger.error(f"Backup completed with {len(self.errors)} errors")
|
||||||
|
return False
|
||||||
|
elif self.warnings:
|
||||||
|
logger.warning(f"Backup completed with {len(self.warnings)} warnings")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.info("Backup completed successfully")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def initialize(config_path: Path) -> BackupManager:
|
||||||
|
try:
|
||||||
|
with config_path.open("rb") as config_file:
|
||||||
|
raw_config = tomllib.load(config_file)
|
||||||
|
except OSError as e:
|
||||||
|
logger.error(f"Failed to read config file {config_path}: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
host_name = str(raw_config.get("host_name", "unknown"))
|
||||||
|
|
||||||
|
roots_raw = raw_config.get("roots") or []
|
||||||
|
if not isinstance(roots_raw, list) or not roots_raw:
|
||||||
|
raise ValueError("roots must be a non-empty list of paths in config.toml")
|
||||||
|
roots = [Path(root) for root in roots_raw]
|
||||||
|
|
||||||
|
storage_raw = raw_config.get("storage") or {}
|
||||||
|
storages: List[Storage] = []
|
||||||
|
for name, params in storage_raw.items():
|
||||||
|
if not isinstance(params, dict):
|
||||||
|
raise ValueError(f"Storage config for {name} must be a table")
|
||||||
|
storage_type = params.get("type", "")
|
||||||
|
if storage_type == ResticStorage.TYPE_NAME:
|
||||||
|
storages.append(ResticStorage(name, params))
|
||||||
|
if not storages:
|
||||||
|
raise ValueError("At least one storage backend must be configured")
|
||||||
|
|
||||||
|
notifications_raw = raw_config.get("notifier") or {}
|
||||||
|
notifiers: List[Notifier] = []
|
||||||
|
for name, params in notifications_raw.items():
|
||||||
|
if not isinstance(params, dict):
|
||||||
|
raise ValueError(f"Notificator config for {name} must be a table")
|
||||||
|
notifier_type = params.get("type", "")
|
||||||
|
if notifier_type == TelegramNotifier.TYPE_NAME:
|
||||||
|
notifiers.append(TelegramNotifier(name, params))
|
||||||
|
if not notifiers:
|
||||||
|
raise ValueError("At least one notification backend must be configured")
|
||||||
|
|
||||||
|
config = Config(host_name=host_name, roots=roots)
|
||||||
|
|
||||||
|
return BackupManager(
|
||||||
|
config=config, roots=roots, storages=storages, notifiers=notifiers
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
backup_manager = initialize(CONFIG_PATH)
|
||||||
|
success = backup_manager.run_backup_process()
|
||||||
|
if not success:
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logger.info("Backup process interrupted by user")
|
||||||
|
sys.exit(130)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error in backup process: {str(e)}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,326 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Backup script for all applications
|
|
||||||
Automatically discovers and runs backup scripts for all users,
|
|
||||||
then creates restic backups and sends notifications.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import logging
|
|
||||||
import pwd
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import List, Tuple, Optional
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
|
||||||
handlers=[
|
|
||||||
logging.StreamHandler(sys.stdout),
|
|
||||||
logging.FileHandler("/var/log/backup-all.log"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Configuration from Ansible template variables
|
|
||||||
RESTIC_REPOSITORY = "{{ restic_repository }}"
|
|
||||||
RESTIC_PASSWORD = "{{ restic_password }}"
|
|
||||||
AWS_ACCESS_KEY_ID = "{{ restic_s3_access_key }}"
|
|
||||||
AWS_SECRET_ACCESS_KEY = "{{ restic_s3_access_secret }}"
|
|
||||||
AWS_DEFAULT_REGION = "{{ restic_s3_region }}"
|
|
||||||
TELEGRAM_BOT_TOKEN = "{{ notifications_tg_bot_token }}"
|
|
||||||
TELEGRAM_CHAT_ID = "{{ notifications_tg_chat_id }}"
|
|
||||||
NOTIFICATIONS_NAME = "{{ notifications_name }}"
|
|
||||||
|
|
||||||
|
|
||||||
class BackupManager:
|
|
||||||
def __init__(self):
|
|
||||||
self.errors = []
|
|
||||||
self.warnings = []
|
|
||||||
self.successful_backups = []
|
|
||||||
|
|
||||||
def get_home_directories(self) -> List[Tuple[str, str]]:
|
|
||||||
"""Get all home directories and their owners"""
|
|
||||||
home_dirs = []
|
|
||||||
home_path = Path("/home")
|
|
||||||
|
|
||||||
if not home_path.exists():
|
|
||||||
logger.error("/home directory does not exist")
|
|
||||||
return home_dirs
|
|
||||||
|
|
||||||
for user_dir in home_path.iterdir():
|
|
||||||
if user_dir.is_dir():
|
|
||||||
try:
|
|
||||||
# Get the owner of the directory
|
|
||||||
stat_info = user_dir.stat()
|
|
||||||
owner = pwd.getpwuid(stat_info.st_uid).pw_name
|
|
||||||
home_dirs.append((str(user_dir), owner))
|
|
||||||
except (KeyError, OSError) as e:
|
|
||||||
logger.warning(f"Could not get owner for {user_dir}: {e}")
|
|
||||||
|
|
||||||
return home_dirs
|
|
||||||
|
|
||||||
def find_backup_script(self, home_dir: str) -> Optional[str]:
|
|
||||||
"""Find backup script in user's home directory"""
|
|
||||||
possible_scripts = [
|
|
||||||
os.path.join(home_dir, "backup.sh"),
|
|
||||||
os.path.join(home_dir, "backup"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for script_path in possible_scripts:
|
|
||||||
if os.path.exists(script_path):
|
|
||||||
# Check if file is executable
|
|
||||||
if os.access(script_path, os.X_OK):
|
|
||||||
return script_path
|
|
||||||
else:
|
|
||||||
logger.warning(
|
|
||||||
f"Backup script {script_path} exists but is not executable"
|
|
||||||
)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def run_user_backup(self, script_path: str, username: str) -> bool:
|
|
||||||
"""Run backup script as the specified user"""
|
|
||||||
try:
|
|
||||||
logger.info(f"Running backup script {script_path} as user {username}")
|
|
||||||
|
|
||||||
# Use su to run the script as the user
|
|
||||||
cmd = ["su", "--login", username, "--command", script_path]
|
|
||||||
|
|
||||||
result = subprocess.run(
|
|
||||||
cmd, capture_output=True, text=True, timeout=3600 # 1 hour timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
logger.info(f"Backup script for {username} completed successfully")
|
|
||||||
self.successful_backups.append(username)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
error_msg = f"Backup script for {username} failed with return code {result.returncode}"
|
|
||||||
if result.stderr:
|
|
||||||
error_msg += f": {result.stderr}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"User {username}: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
error_msg = f"Backup script for {username} timed out"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"User {username}: {error_msg}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
error_msg = f"Failed to run backup script for {username}: {str(e)}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"User {username}: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_backup_directories(self) -> List[str]:
|
|
||||||
"""Get all backup directories that exist"""
|
|
||||||
backup_dirs = []
|
|
||||||
home_dirs = self.get_home_directories()
|
|
||||||
|
|
||||||
for home_dir, _ in home_dirs:
|
|
||||||
backup_path = os.path.join(home_dir, "backups")
|
|
||||||
if os.path.exists(backup_path) and os.path.isdir(backup_path):
|
|
||||||
backup_dirs.append(backup_path)
|
|
||||||
|
|
||||||
return backup_dirs
|
|
||||||
|
|
||||||
def run_restic_backup(self, backup_dirs: List[str]) -> bool:
|
|
||||||
"""Run restic backup for all backup directories"""
|
|
||||||
if not backup_dirs:
|
|
||||||
logger.warning("No backup directories found")
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
logger.info("Starting restic backup")
|
|
||||||
|
|
||||||
# Set environment variables for restic
|
|
||||||
env = os.environ.copy()
|
|
||||||
env.update(
|
|
||||||
{
|
|
||||||
"RESTIC_REPOSITORY": RESTIC_REPOSITORY,
|
|
||||||
"RESTIC_PASSWORD": RESTIC_PASSWORD,
|
|
||||||
"AWS_ACCESS_KEY_ID": AWS_ACCESS_KEY_ID,
|
|
||||||
"AWS_SECRET_ACCESS_KEY": AWS_SECRET_ACCESS_KEY,
|
|
||||||
"AWS_DEFAULT_REGION": AWS_DEFAULT_REGION,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run backup
|
|
||||||
backup_cmd = ["restic", "backup", "--verbose"] + backup_dirs
|
|
||||||
result = subprocess.run(backup_cmd, env=env, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode != 0:
|
|
||||||
error_msg = f"Restic backup failed: {result.stderr}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"Restic backup: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.info("Restic backup completed successfully")
|
|
||||||
|
|
||||||
# Run check
|
|
||||||
check_cmd = ["restic", "check"]
|
|
||||||
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode != 0:
|
|
||||||
error_msg = f"Restic check failed: {result.stderr}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"Restic check: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.info("Restic check completed successfully")
|
|
||||||
|
|
||||||
# Run forget and prune
|
|
||||||
forget_cmd = [
|
|
||||||
"restic",
|
|
||||||
"forget",
|
|
||||||
"--compact",
|
|
||||||
"--prune",
|
|
||||||
"--keep-daily",
|
|
||||||
"90",
|
|
||||||
"--keep-monthly",
|
|
||||||
"36",
|
|
||||||
]
|
|
||||||
result = subprocess.run(forget_cmd, env=env, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode != 0:
|
|
||||||
error_msg = f"Restic forget/prune failed: {result.stderr}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"Restic forget/prune: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.info("Restic forget/prune completed successfully")
|
|
||||||
|
|
||||||
# Final check
|
|
||||||
result = subprocess.run(check_cmd, env=env, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode != 0:
|
|
||||||
error_msg = f"Final restic check failed: {result.stderr}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"Final restic check: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.info("Final restic check completed successfully")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
error_msg = f"Restic backup process failed: {str(e)}"
|
|
||||||
logger.error(error_msg)
|
|
||||||
self.errors.append(f"Restic: {error_msg}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def send_telegram_notification(self, success: bool) -> None:
|
|
||||||
"""Send notification to Telegram"""
|
|
||||||
try:
|
|
||||||
if success and not self.errors:
|
|
||||||
message = f"<b>{NOTIFICATIONS_NAME}</b>: бекап успешно завершен!"
|
|
||||||
if self.successful_backups:
|
|
||||||
message += (
|
|
||||||
f"\n\nУспешные бекапы: {', '.join(self.successful_backups)}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
message = f"<b>{NOTIFICATIONS_NAME}</b>: бекап завершен с ошибками!"
|
|
||||||
|
|
||||||
if self.successful_backups:
|
|
||||||
message += (
|
|
||||||
f"\n\n✅ Успешные бекапы: {', '.join(self.successful_backups)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.warnings:
|
|
||||||
message += f"\n\n⚠️ Предупреждения:\n" + "\n".join(self.warnings)
|
|
||||||
|
|
||||||
if self.errors:
|
|
||||||
message += f"\n\n❌ Ошибки:\n" + "\n".join(self.errors)
|
|
||||||
|
|
||||||
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
|
|
||||||
data = {"chat_id": TELEGRAM_CHAT_ID, "parse_mode": "HTML", "text": message}
|
|
||||||
|
|
||||||
response = requests.post(url, data=data, timeout=30)
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
logger.info("Telegram notification sent successfully")
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
f"Failed to send Telegram notification: {response.status_code} - {response.text}"
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to send Telegram notification: {str(e)}")
|
|
||||||
|
|
||||||
def run_backup_process(self) -> bool:
|
|
||||||
"""Main backup process"""
|
|
||||||
logger.info("Starting backup process")
|
|
||||||
|
|
||||||
# Get all home directories
|
|
||||||
home_dirs = self.get_home_directories()
|
|
||||||
logger.info(f"Found {len(home_dirs)} home directories")
|
|
||||||
|
|
||||||
# Process each user's backup
|
|
||||||
for home_dir, username in home_dirs:
|
|
||||||
logger.info(f"Processing backup for user: {username} ({home_dir})")
|
|
||||||
|
|
||||||
# Find backup script
|
|
||||||
backup_script = self.find_backup_script(home_dir)
|
|
||||||
|
|
||||||
if backup_script is None:
|
|
||||||
warning_msg = (
|
|
||||||
f"No backup script found for user {username} in {home_dir}"
|
|
||||||
)
|
|
||||||
logger.warning(warning_msg)
|
|
||||||
self.warnings.append(warning_msg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Run backup script
|
|
||||||
self.run_user_backup(backup_script, username)
|
|
||||||
|
|
||||||
# Get backup directories
|
|
||||||
backup_dirs = self.get_backup_directories()
|
|
||||||
logger.info(f"Found backup directories: {backup_dirs}")
|
|
||||||
|
|
||||||
# Run restic backup
|
|
||||||
restic_success = self.run_restic_backup(backup_dirs)
|
|
||||||
|
|
||||||
# Determine overall success
|
|
||||||
overall_success = restic_success and len(self.errors) == 0
|
|
||||||
|
|
||||||
# Send notification
|
|
||||||
self.send_telegram_notification(overall_success)
|
|
||||||
|
|
||||||
logger.info("Backup process completed")
|
|
||||||
|
|
||||||
if self.errors:
|
|
||||||
logger.error(f"Backup completed with {len(self.errors)} errors")
|
|
||||||
return False
|
|
||||||
elif self.warnings:
|
|
||||||
logger.warning(f"Backup completed with {len(self.warnings)} warnings")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.info("Backup completed successfully")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main entry point"""
|
|
||||||
try:
|
|
||||||
backup_manager = BackupManager()
|
|
||||||
success = backup_manager.run_backup_process()
|
|
||||||
|
|
||||||
if success:
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("Backup process interrupted by user")
|
|
||||||
sys.exit(130)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unexpected error in backup process: {str(e)}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
18
files/backups/config.template.toml
Normal file
18
files/backups/config.template.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
host_name = "{{ notifications_name }}"
|
||||||
|
|
||||||
|
roots = [
|
||||||
|
"{{ application_dir }}"
|
||||||
|
]
|
||||||
|
|
||||||
|
[storage.yandex_cloud_s3]
|
||||||
|
type = "restic"
|
||||||
|
restic_repository = "{{ restic_repository }}"
|
||||||
|
restic_password = "{{ restic_password }}"
|
||||||
|
aws_access_key_id = "{{ restic_s3_access_key }}"
|
||||||
|
aws_secret_access_key = "{{ restic_s3_access_secret }}"
|
||||||
|
aws_default_region = "{{ restic_s3_region }}"
|
||||||
|
|
||||||
|
[notifier.server_notifications_channel]
|
||||||
|
type = "telegram"
|
||||||
|
telegram_bot_token = "{{ notifications_tg_bot_token }}"
|
||||||
|
telegram_chat_id = "{{ notifications_tg_chat_id }}"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
dozzle_app:
|
dozzle_app:
|
||||||
image: amir20/dozzle:v8.14.6
|
image: amir20/dozzle:v8.14.11
|
||||||
container_name: dozzle_app
|
container_name: dozzle_app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
gitea_app:
|
gitea_app:
|
||||||
image: gitea/gitea:1.25.0
|
image: gitea/gitea:1.25.3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
container_name: gitea_app
|
container_name: gitea_app
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
gramps_app: &gramps_app
|
gramps_app: &gramps_app
|
||||||
image: ghcr.io/gramps-project/grampsweb:25.10.1
|
image: ghcr.io/gramps-project/grampsweb:25.11.2
|
||||||
container_name: gramps_app
|
container_name: gramps_app
|
||||||
depends_on:
|
depends_on:
|
||||||
- gramps_redis
|
- gramps_redis
|
||||||
@@ -15,10 +15,10 @@ services:
|
|||||||
- "{{ (data_dir, 'gramps_db') | path_join }}:/root/.gramps/grampsdb" # persist Gramps database
|
- "{{ (data_dir, 'gramps_db') | path_join }}:/root/.gramps/grampsdb" # persist Gramps database
|
||||||
- "{{ (data_dir, 'gramps_users') | path_join }}:/app/users" # persist user database
|
- "{{ (data_dir, 'gramps_users') | path_join }}:/app/users" # persist user database
|
||||||
- "{{ (data_dir, 'gramps_index') | path_join }}:/app/indexdir" # persist search index
|
- "{{ (data_dir, 'gramps_index') | path_join }}:/app/indexdir" # persist search index
|
||||||
- "{{ (data_dir, 'gramps_thumb_cache') | path_join }}:/app/thumbnail_cache" # persist thumbnails
|
|
||||||
- "{{ (data_dir, 'gramps_cache') | path_join }}:/app/cache" # persist export and report caches
|
|
||||||
- "{{ (data_dir, 'gramps_secret') | path_join }}:/app/secret" # persist flask secret
|
- "{{ (data_dir, 'gramps_secret') | path_join }}:/app/secret" # persist flask secret
|
||||||
- "{{ (data_dir, 'gramps_media') | path_join }}:/app/media" # persist media files
|
- "{{ (cache_dir, 'gramps_thumb_cache') | path_join }}:/app/thumbnail_cache" # persist thumbnails
|
||||||
|
- "{{ (cache_dir, 'gramps_cache') | path_join }}:/app/cache" # persist export and report caches
|
||||||
|
- "{{ media_dir }}:/app/media" # persist media files
|
||||||
environment:
|
environment:
|
||||||
GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists
|
GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists
|
||||||
GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}"
|
GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}"
|
||||||
@@ -37,12 +37,8 @@ services:
|
|||||||
GRAMPSWEB_EMAIL_USE_TLS: "false"
|
GRAMPSWEB_EMAIL_USE_TLS: "false"
|
||||||
GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me"
|
GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me"
|
||||||
|
|
||||||
# media storage at s3
|
# media storage
|
||||||
GRAMPSWEB_MEDIA_BASE_DIR: "s3://av-gramps-media-storage"
|
GRAMPSWEB_MEDIA_BASE_DIR: "/app/media"
|
||||||
AWS_ENDPOINT_URL: "{{ gramps_s3_endpoint }}"
|
|
||||||
AWS_ACCESS_KEY_ID: "{{ gramps_s3_access_key_id }}"
|
|
||||||
AWS_SECRET_ACCESS_KEY: "{{ gramps_s3_secret_access_key }}"
|
|
||||||
AWS_DEFAULT_REGION: "{{ gramps_s3_region }}"
|
|
||||||
|
|
||||||
gramps_celery:
|
gramps_celery:
|
||||||
<<: *gramps_app # YAML merge key copying the entire grampsweb service config
|
<<: *gramps_app # YAML merge key copying the entire grampsweb service config
|
||||||
@@ -53,10 +49,10 @@ services:
|
|||||||
ports: []
|
ports: []
|
||||||
networks:
|
networks:
|
||||||
- "gramps_network"
|
- "gramps_network"
|
||||||
command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=2
|
command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=1
|
||||||
|
|
||||||
gramps_redis:
|
gramps_redis:
|
||||||
image: valkey/valkey:8.1.1-alpine
|
image: valkey/valkey:9.0-alpine
|
||||||
container_name: gramps_redis
|
container_name: gramps_redis
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -23,10 +23,3 @@ models:
|
|||||||
undo:
|
undo:
|
||||||
type: sqlite
|
type: sqlite
|
||||||
path: "{{ (data_dir, 'gramps_db/59a0f3d6-1c3d-4410-8c1d-1c9c6689659f/undo.db') | path_join }}"
|
path: "{{ (data_dir, 'gramps_db/59a0f3d6-1c3d-4410-8c1d-1c9c6689659f/undo.db') | path_join }}"
|
||||||
archive:
|
|
||||||
includes:
|
|
||||||
- "{{ data_dir }}"
|
|
||||||
excludes:
|
|
||||||
- "{{ (data_dir, 'gramps_cache') | path_join }}"
|
|
||||||
- "{{ (data_dir, 'gramps_thumb_cache') | path_join }}"
|
|
||||||
- "{{ (data_dir, 'gramps_tmp') | path_join }}"
|
|
||||||
65
files/gramps/gramps_rename.py
Executable file
65
files/gramps/gramps_rename.py
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3.12
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Rename Gramps document files by appending extensions from a list."
|
||||||
|
)
|
||||||
|
parser.add_argument("directory", type=Path, help="Directory containing hashed files")
|
||||||
|
parser.add_argument("names_file", type=Path, help="Text file with target names")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def read_names(path: Path) -> list[str]:
|
||||||
|
if not path.is_file():
|
||||||
|
raise FileNotFoundError(f"Names file not found: {path}")
|
||||||
|
|
||||||
|
names = []
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
name = line.strip()
|
||||||
|
if name:
|
||||||
|
names.append(name)
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def rename_files(directory: Path, names: list[str]) -> None:
|
||||||
|
if not directory.is_dir():
|
||||||
|
raise NotADirectoryError(f"Directory not found: {directory}")
|
||||||
|
|
||||||
|
for name in names:
|
||||||
|
hash_part, dot, _ = name.partition(".")
|
||||||
|
if not dot:
|
||||||
|
print(f"Skipping invalid entry (missing extension): {name}", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
|
||||||
|
source = directory / hash_part
|
||||||
|
target = directory / name
|
||||||
|
|
||||||
|
if target.exists():
|
||||||
|
print(f"Target already exists, skipping: {target}", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not source.exists():
|
||||||
|
print(f"Source not found, skipping: {source}", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
|
||||||
|
source.rename(target)
|
||||||
|
print(f"Renamed {source.name} -> {target.name}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
args = parse_args()
|
||||||
|
try:
|
||||||
|
names = read_names(args.names_file)
|
||||||
|
rename_files(args.directory, names)
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
print(str(exc), file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
homepage_app:
|
homepage_app:
|
||||||
# noinspection ComposeUnknownValues
|
# noinspection ComposeUnknownValues
|
||||||
image: "{{ registry_homepage_nginx_image }}"
|
image: "{{ registry_homepage_nginx_image }}"
|
||||||
@@ -10,4 +11,3 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
web_proxy_network:
|
web_proxy_network:
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
memos_app:
|
memos_app:
|
||||||
image: neosmemo/memos:0.25.2
|
image: neosmemo/memos:0.25.3
|
||||||
container_name: memos_app
|
container_name: memos_app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
|
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
|
||||||
@@ -14,6 +14,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- MEMOS_MODE=prod
|
- MEMOS_MODE=prod
|
||||||
- MEMOS_PORT=5230
|
- MEMOS_PORT=5230
|
||||||
|
- MEMOS_STORAGE_TYPE=local
|
||||||
|
- MEMOS_STORAGE_PATH=assets/{uuid}
|
||||||
|
- MEMOS_MAX_FILE_SIZE=52428800
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
web_proxy_network:
|
web_proxy_network:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
models:
|
models:
|
||||||
|
|
||||||
gramps:
|
memos:
|
||||||
compress_with:
|
compress_with:
|
||||||
type: 'tgz'
|
type: 'tgz'
|
||||||
storages:
|
storages:
|
||||||
@@ -14,8 +14,3 @@ models:
|
|||||||
users:
|
users:
|
||||||
type: sqlite
|
type: sqlite
|
||||||
path: "{{ (data_dir, 'memos_prod.db') | path_join }}"
|
path: "{{ (data_dir, 'memos_prod.db') | path_join }}"
|
||||||
archive:
|
|
||||||
includes:
|
|
||||||
- "{{ data_dir }}"
|
|
||||||
excludes:
|
|
||||||
- "{{ (data_dir, '.thumbnail_cache') | path_join }}"
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
netdata:
|
netdata:
|
||||||
image: netdata/netdata:v2.7.3
|
image: netdata/netdata:v2.8.4
|
||||||
container_name: netdata
|
container_name: netdata
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
cap_add:
|
cap_add:
|
||||||
@@ -17,12 +16,16 @@ services:
|
|||||||
- "{{ config_dir }}:/etc/netdata"
|
- "{{ config_dir }}:/etc/netdata"
|
||||||
- "{{ (data_dir, 'lib') | path_join }}:/var/lib/netdata"
|
- "{{ (data_dir, 'lib') | path_join }}:/var/lib/netdata"
|
||||||
- "{{ (data_dir, 'cache') | path_join }}:/var/cache/netdata"
|
- "{{ (data_dir, 'cache') | path_join }}:/var/cache/netdata"
|
||||||
|
|
||||||
# Netdata system volumes
|
# Netdata system volumes
|
||||||
- "/:/host/root:ro,rslave"
|
- "/:/host/root:ro,rslave"
|
||||||
|
|
||||||
- "/etc/group:/host/etc/group:ro"
|
- "/etc/group:/host/etc/group:ro"
|
||||||
|
- "/etc/hostname:/host/etc/hostname:ro"
|
||||||
- "/etc/localtime:/etc/localtime:ro"
|
- "/etc/localtime:/etc/localtime:ro"
|
||||||
- "/etc/os-release:/host/etc/os-release:ro"
|
- "/etc/os-release:/host/etc/os-release:ro"
|
||||||
- "/etc/passwd:/host/etc/passwd:ro"
|
- "/etc/passwd:/host/etc/passwd:ro"
|
||||||
|
|
||||||
- "/proc:/host/proc:ro"
|
- "/proc:/host/proc:ro"
|
||||||
- "/run/dbus:/run/dbus:ro"
|
- "/run/dbus:/run/dbus:ro"
|
||||||
- "/sys:/host/sys:ro"
|
- "/sys:/host/sys:ro"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
jobs:
|
jobs:
|
||||||
- name: fail2ban
|
- name: fail2ban
|
||||||
update_every: 15 # Collect Fail2Ban jails statistics every 15 seconds
|
update_every: 60 # Collect Fail2Ban jails statistics every N seconds
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
update_every: 15
|
update_every: 60
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
# cpu cores = 2
|
# cpu cores = 2
|
||||||
# libuv worker threads = 16
|
# libuv worker threads = 16
|
||||||
# profile = standalone
|
# profile = standalone
|
||||||
hostname = {{ host_name }}
|
# hostname = rivendell-v2
|
||||||
# glibc malloc arena max for plugins = 1
|
# glibc malloc arena max for plugins = 1
|
||||||
# glibc malloc arena max for netdata = 1
|
# glibc malloc arena max for netdata = 1
|
||||||
# crash reports = all
|
# crash reports = all
|
||||||
@@ -30,12 +30,15 @@
|
|||||||
# has unstable connection = no
|
# has unstable connection = no
|
||||||
|
|
||||||
[db]
|
[db]
|
||||||
|
#| >>> [db].update every <<<
|
||||||
|
#| datatype: duration (seconds), default value: 1s
|
||||||
|
update every = 10s
|
||||||
|
|
||||||
# enable replication = yes
|
# enable replication = yes
|
||||||
# replication period = 1d
|
# replication period = 1d
|
||||||
# replication step = 1h
|
# replication step = 1h
|
||||||
# replication threads = 1
|
# replication threads = 1
|
||||||
# replication prefetch = 10
|
# replication prefetch = 10
|
||||||
# update every = 1s
|
|
||||||
# db = dbengine
|
# db = dbengine
|
||||||
# memory deduplication (ksm) = auto
|
# memory deduplication (ksm) = auto
|
||||||
# cleanup orphan hosts after = 1h
|
# cleanup orphan hosts after = 1h
|
||||||
@@ -47,7 +50,7 @@
|
|||||||
# dbengine extent cache size = off
|
# dbengine extent cache size = off
|
||||||
# dbengine enable journal integrity check = no
|
# dbengine enable journal integrity check = no
|
||||||
# dbengine use all ram for caches = no
|
# dbengine use all ram for caches = no
|
||||||
# dbengine out of memory protection = 391.99MiB
|
# dbengine out of memory protection = 391.49MiB
|
||||||
# dbengine use direct io = yes
|
# dbengine use direct io = yes
|
||||||
# dbengine journal v2 unmount time = 2m
|
# dbengine journal v2 unmount time = 2m
|
||||||
# dbengine pages per extent = 109
|
# dbengine pages per extent = 109
|
||||||
@@ -96,9 +99,6 @@
|
|||||||
# PYTHONPATH =
|
# PYTHONPATH =
|
||||||
# TZ = :/etc/localtime
|
# TZ = :/etc/localtime
|
||||||
|
|
||||||
[host labels]
|
|
||||||
# name = value
|
|
||||||
|
|
||||||
[cloud]
|
[cloud]
|
||||||
# conversation log = no
|
# conversation log = no
|
||||||
# scope = full
|
# scope = full
|
||||||
@@ -107,15 +107,15 @@
|
|||||||
|
|
||||||
[ml]
|
[ml]
|
||||||
# enabled = auto
|
# enabled = auto
|
||||||
# maximum num samples to train = 21600
|
# training window = 6h
|
||||||
# minimum num samples to train = 900
|
# min training window = 15m
|
||||||
|
# max training vectors = 1440
|
||||||
|
# max samples to smooth = 3
|
||||||
# train every = 3h
|
# train every = 3h
|
||||||
# number of models per dimension = 18
|
# number of models per dimension = 18
|
||||||
# delete models older than = 7d
|
# delete models older than = 7d
|
||||||
# num samples to diff = 1
|
# num samples to diff = 1
|
||||||
# num samples to smooth = 3
|
|
||||||
# num samples to lag = 5
|
# num samples to lag = 5
|
||||||
# random sampling ratio = 0.20000
|
|
||||||
# maximum number of k-means iterations = 1000
|
# maximum number of k-means iterations = 1000
|
||||||
# dimension anomaly score threshold = 0.99000
|
# dimension anomaly score threshold = 0.99000
|
||||||
# host anomaly rate threshold = 1.00000
|
# host anomaly rate threshold = 1.00000
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
# gzip compression level = 3
|
# gzip compression level = 3
|
||||||
# ssl skip certificate verification = no
|
# ssl skip certificate verification = no
|
||||||
# web server threads = 6
|
# web server threads = 6
|
||||||
# web server max sockets = 262144
|
# web server max sockets = 131072
|
||||||
|
|
||||||
[registry]
|
[registry]
|
||||||
# enabled = no
|
# enabled = no
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
# registry expire idle persons = 1y
|
# registry expire idle persons = 1y
|
||||||
# registry domain =
|
# registry domain =
|
||||||
# registry to announce = https://registry.my-netdata.io
|
# registry to announce = https://registry.my-netdata.io
|
||||||
# registry hostname = 7171b7f9fc69
|
# registry hostname = rivendell-v2
|
||||||
# verify browser cookies support = yes
|
# verify browser cookies support = yes
|
||||||
# enable cookies SameSite and Secure = yes
|
# enable cookies SameSite and Secure = yes
|
||||||
# max URL length = 1024
|
# max URL length = 1024
|
||||||
@@ -202,9 +202,29 @@
|
|||||||
|
|
||||||
[pulse]
|
[pulse]
|
||||||
# extended = no
|
# extended = no
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
#| >>> [plugins].perf <<<
|
||||||
|
#| datatype: yes or no, default value: yes
|
||||||
|
perf = no
|
||||||
|
|
||||||
|
#| >>> [plugins].python.d <<<
|
||||||
|
#| datatype: yes or no, default value: yes
|
||||||
|
python.d = no
|
||||||
|
|
||||||
|
#| >>> [plugins].charts.d <<<
|
||||||
|
#| datatype: yes or no, default value: yes
|
||||||
|
charts.d = no
|
||||||
|
|
||||||
|
#| >>> [plugins].otel <<<
|
||||||
|
#| datatype: yes or no, default value: yes
|
||||||
|
otel = no
|
||||||
|
|
||||||
|
#| >>> [plugins].statsd <<<
|
||||||
|
#| datatype: yes or no, default value: yes
|
||||||
|
statsd = no
|
||||||
|
|
||||||
# idlejitter = yes
|
# idlejitter = yes
|
||||||
# netdata pulse = yes
|
# netdata pulse = yes
|
||||||
# profile = no
|
# profile = no
|
||||||
@@ -213,23 +233,20 @@
|
|||||||
# proc = yes
|
# proc = yes
|
||||||
# cgroups = yes
|
# cgroups = yes
|
||||||
# timex = yes
|
# timex = yes
|
||||||
# statsd = yes
|
|
||||||
# enable running new plugins = yes
|
# enable running new plugins = yes
|
||||||
# check for new plugins every = 1m
|
# check for new plugins every = 1m
|
||||||
# slabinfo = no
|
# slabinfo = no
|
||||||
# freeipmi = no
|
# freeipmi = no
|
||||||
# python.d = yes
|
|
||||||
# go.d = yes
|
|
||||||
# apps = yes
|
|
||||||
# systemd-journal = yes
|
|
||||||
# network-viewer = yes
|
|
||||||
# charts.d = yes
|
|
||||||
# debugfs = yes
|
# debugfs = yes
|
||||||
# perf = yes
|
|
||||||
# ioping = yes
|
# ioping = yes
|
||||||
|
# network-viewer = yes
|
||||||
|
# apps = yes
|
||||||
|
# go.d = yes
|
||||||
|
# systemd-units = yes
|
||||||
|
# systemd-journal = yes
|
||||||
|
|
||||||
[statsd]
|
[statsd]
|
||||||
# update every (flushInterval) = 1s
|
# update every (flushInterval) = 10s
|
||||||
# udp messages to process at once = 10
|
# udp messages to process at once = 10
|
||||||
# create private charts for metrics matching = *
|
# create private charts for metrics matching = *
|
||||||
# max private charts hard limit = 1000
|
# max private charts hard limit = 1000
|
||||||
@@ -247,10 +264,7 @@
|
|||||||
# gaps on histograms (deleteHistograms) = no
|
# gaps on histograms (deleteHistograms) = no
|
||||||
# gaps on timers (deleteTimers) = no
|
# gaps on timers (deleteTimers) = no
|
||||||
# gaps on dictionaries (deleteDictionaries) = no
|
# gaps on dictionaries (deleteDictionaries) = no
|
||||||
# statsd server max TCP sockets = 262144
|
# statsd server max TCP sockets = 131072
|
||||||
# listen backlog = 4096
|
|
||||||
# default port = 8125
|
|
||||||
# bind to = udp:localhost tcp:localhost
|
|
||||||
|
|
||||||
[plugin:idlejitter]
|
[plugin:idlejitter]
|
||||||
# loop time = 20ms
|
# loop time = 20ms
|
||||||
@@ -300,8 +314,14 @@
|
|||||||
# /sys/class/drm = yes
|
# /sys/class/drm = yes
|
||||||
|
|
||||||
[plugin:cgroups]
|
[plugin:cgroups]
|
||||||
# update every = 1s
|
#| >>> [plugin:cgroups].update every <<<
|
||||||
# check for new cgroups every = 10s
|
#| datatype: duration (seconds), default value: 10s
|
||||||
|
update every = 20s
|
||||||
|
|
||||||
|
#| >>> [plugin:cgroups].check for new cgroups every <<<
|
||||||
|
#| datatype: duration (seconds), default value: 10s
|
||||||
|
check for new cgroups every = 20s
|
||||||
|
|
||||||
# use unified cgroups = auto
|
# use unified cgroups = auto
|
||||||
# max cgroups to allow = 1000
|
# max cgroups to allow = 1000
|
||||||
# max cgroups depth to monitor = 0
|
# max cgroups depth to monitor = 0
|
||||||
@@ -314,8 +334,11 @@
|
|||||||
# cgroups to match as systemd services = !/system.slice/*/*.service /system.slice/*.service
|
# cgroups to match as systemd services = !/system.slice/*/*.service /system.slice/*.service
|
||||||
|
|
||||||
[plugin:proc:diskspace]
|
[plugin:proc:diskspace]
|
||||||
|
#| >>> [plugin:proc:diskspace].update every <<<
|
||||||
|
#| datatype: duration (seconds), default value: 10s
|
||||||
|
update every = 1m
|
||||||
|
|
||||||
# remove charts of unmounted disks = yes
|
# remove charts of unmounted disks = yes
|
||||||
# update every = 1s
|
|
||||||
# check for new mount points every = 15s
|
# check for new mount points every = 15s
|
||||||
# exclude space metrics on paths = /dev /dev/shm /proc/* /sys/* /var/run/user/* /run/lock /run/user/* /snap/* /var/lib/docker/* /var/lib/containers/storage/* /run/credentials/* /run/containerd/* /rpool /rpool/*
|
# exclude space metrics on paths = /dev /dev/shm /proc/* /sys/* /var/run/user/* /run/lock /run/user/* /snap/* /var/lib/docker/* /var/lib/containers/storage/* /run/credentials/* /run/containerd/* /rpool /rpool/*
|
||||||
# exclude space metrics on filesystems = *gvfs *gluster* *s3fs *ipfs *davfs2 *httpfs *sshfs *gdfs *moosefs fusectl autofs cgroup cgroup2 hugetlbfs devtmpfs fuse.lxcfs
|
# exclude space metrics on filesystems = *gvfs *gluster* *s3fs *ipfs *davfs2 *httpfs *sshfs *gdfs *moosefs fusectl autofs cgroup cgroup2 hugetlbfs devtmpfs fuse.lxcfs
|
||||||
@@ -326,40 +349,28 @@
|
|||||||
[plugin:tc]
|
[plugin:tc]
|
||||||
# script to run to get tc values = /usr/libexec/netdata/plugins.d/tc-qos-helper.sh
|
# script to run to get tc values = /usr/libexec/netdata/plugins.d/tc-qos-helper.sh
|
||||||
|
|
||||||
[plugin:python.d]
|
|
||||||
# update every = 1s
|
|
||||||
# command options =
|
|
||||||
|
|
||||||
[plugin:go.d]
|
[plugin:go.d]
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
# command options =
|
# command options =
|
||||||
|
|
||||||
[plugin:apps]
|
[plugin:apps]
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
# command options =
|
# command options =
|
||||||
|
|
||||||
[plugin:systemd-journal]
|
[plugin:systemd-journal]
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
# command options =
|
# command options =
|
||||||
|
|
||||||
[plugin:network-viewer]
|
[plugin:network-viewer]
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
# command options =
|
|
||||||
|
|
||||||
[plugin:charts.d]
|
|
||||||
# update every = 1s
|
|
||||||
# command options =
|
# command options =
|
||||||
|
|
||||||
[plugin:debugfs]
|
[plugin:debugfs]
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
# command options =
|
|
||||||
|
|
||||||
[plugin:perf]
|
|
||||||
# update every = 1s
|
|
||||||
# command options =
|
# command options =
|
||||||
|
|
||||||
[plugin:ioping]
|
[plugin:ioping]
|
||||||
# update every = 1s
|
# update every = 10s
|
||||||
# command options =
|
# command options =
|
||||||
|
|
||||||
[plugin:proc:/proc/net/dev]
|
[plugin:proc:/proc/net/dev]
|
||||||
@@ -635,7 +646,7 @@
|
|||||||
# preferred disk ids = *
|
# preferred disk ids = *
|
||||||
# exclude disks = loop* ram*
|
# exclude disks = loop* ram*
|
||||||
# filename to monitor = /host/proc/diskstats
|
# filename to monitor = /host/proc/diskstats
|
||||||
# performance metrics for disks with major 252 = yes
|
# performance metrics for disks with major 253 = yes
|
||||||
|
|
||||||
[plugin:proc:/proc/mdstat]
|
[plugin:proc:/proc/mdstat]
|
||||||
# faulty devices = yes
|
# faulty devices = yes
|
||||||
@@ -685,3 +696,7 @@
|
|||||||
|
|
||||||
[plugin:proc:/sys/class/drm]
|
[plugin:proc:/sys/class/drm]
|
||||||
# directory to monitor = /host/sys/class/drm
|
# directory to monitor = /host/sys/class/drm
|
||||||
|
|
||||||
|
[plugin:systemd-units]
|
||||||
|
# update every = 10s
|
||||||
|
# command options =
|
||||||
@@ -3,14 +3,12 @@ services:
|
|||||||
# See sample https://github.com/outline/outline/blob/main/.env.sample
|
# See sample https://github.com/outline/outline/blob/main/.env.sample
|
||||||
|
|
||||||
outline_app:
|
outline_app:
|
||||||
image: outlinewiki/outline:1.0.1
|
image: outlinewiki/outline:1.1.0
|
||||||
container_name: outline_app
|
container_name: outline_app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- outline_postgres
|
- outline_postgres
|
||||||
- outline_redis
|
- outline_redis
|
||||||
ports:
|
|
||||||
- "127.0.0.1:{{ outline_port }}:3000"
|
|
||||||
networks:
|
networks:
|
||||||
- "outline_network"
|
- "outline_network"
|
||||||
- "web_proxy_network"
|
- "web_proxy_network"
|
||||||
@@ -20,7 +18,7 @@ services:
|
|||||||
FORCE_HTTPS: 'true'
|
FORCE_HTTPS: 'true'
|
||||||
SECRET_KEY: '{{ outline_secret_key }}'
|
SECRET_KEY: '{{ outline_secret_key }}'
|
||||||
UTILS_SECRET: '{{ outline_utils_secret }}'
|
UTILS_SECRET: '{{ outline_utils_secret }}'
|
||||||
DATABASE_URL: 'postgres://{{ outline_postgres_user }}:{{ outline_postgres_password }}@outline_postgres:5432/{{ outline_postgres_database }}'
|
DATABASE_URL: 'postgres://{{ outline_postgres_user }}:{{ outline_postgres_password }}@outline_postgres:5432/{{ outline_postgres_database }}' # yamllint disable-line rule:line-length
|
||||||
PGSSLMODE: 'disable'
|
PGSSLMODE: 'disable'
|
||||||
REDIS_URL: 'redis://outline_redis:6379'
|
REDIS_URL: 'redis://outline_redis:6379'
|
||||||
|
|
||||||
@@ -54,7 +52,7 @@ services:
|
|||||||
SMTP_SECURE: 'false'
|
SMTP_SECURE: 'false'
|
||||||
|
|
||||||
outline_redis:
|
outline_redis:
|
||||||
image: valkey/valkey:8.1.1-alpine
|
image: valkey/valkey:9.0-alpine
|
||||||
container_name: outline_redis
|
container_name: outline_redis
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -64,8 +62,10 @@ services:
|
|||||||
outline_postgres:
|
outline_postgres:
|
||||||
image: postgres:16.3-bookworm
|
image: postgres:16.3-bookworm
|
||||||
container_name: outline_postgres
|
container_name: outline_postgres
|
||||||
|
user: "{{ user_create_result.uid }}:{{ user_create_result.group }}"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
|
- "/etc/passwd:/etc/passwd:ro"
|
||||||
- "{{ postgres_data_dir }}:/var/lib/postgresql/data"
|
- "{{ postgres_data_dir }}:/var/lib/postgresql/data"
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: '{{ outline_postgres_user }}'
|
POSTGRES_USER: '{{ outline_postgres_user }}'
|
||||||
@@ -74,6 +74,10 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- "outline_network"
|
- "outline_network"
|
||||||
- "monitoring_network"
|
- "monitoring_network"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "--username={{ outline_postgres_user }}", "--dbname={{ outline_postgres_database }}"]
|
||||||
|
interval: 10s
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
outline_network:
|
outline_network:
|
||||||
|
|||||||
11
lefthook.yml
11
lefthook.yml
@@ -1,6 +1,8 @@
|
|||||||
# Refer for explanation to following link:
|
# Refer for explanation to following link:
|
||||||
# https://lefthook.dev/configuration/
|
# https://lefthook.dev/configuration/
|
||||||
|
|
||||||
|
glob_matcher: doublestar
|
||||||
|
|
||||||
templates:
|
templates:
|
||||||
av-hooks-dir: "/home/av/projects/private/git-hooks"
|
av-hooks-dir: "/home/av/projects/private/git-hooks"
|
||||||
|
|
||||||
@@ -12,3 +14,12 @@ pre-commit:
|
|||||||
|
|
||||||
- name: "check secret files"
|
- name: "check secret files"
|
||||||
run: "python3 {av-hooks-dir}/pre-commit/check-secrets-encrypted-with-ansible-vault.py"
|
run: "python3 {av-hooks-dir}/pre-commit/check-secrets-encrypted-with-ansible-vault.py"
|
||||||
|
|
||||||
|
- name: "format python"
|
||||||
|
glob: "**/*.py"
|
||||||
|
run: "black --quiet {staged_files}"
|
||||||
|
stage_fixed: true
|
||||||
|
|
||||||
|
- name: "mypy"
|
||||||
|
glob: "**/*.py"
|
||||||
|
run: "mypy {staged_files}"
|
||||||
|
|||||||
48
playbook-all-applications.yml
Normal file
48
playbook-all-applications.yml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
- name: 'Configure netdata'
|
||||||
|
ansible.builtin.import_playbook: playbook-netdata.yml
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
- name: 'Configure dozzle'
|
||||||
|
ansible.builtin.import_playbook: playbook-dozzle.yml
|
||||||
|
|
||||||
|
- name: 'Configure gitea'
|
||||||
|
ansible.builtin.import_playbook: playbook-gitea.yml
|
||||||
|
|
||||||
|
- name: 'Configure gramps'
|
||||||
|
ansible.builtin.import_playbook: playbook-gramps.yml
|
||||||
|
|
||||||
|
- name: 'Configure memos'
|
||||||
|
ansible.builtin.import_playbook: playbook-memos.yml
|
||||||
|
|
||||||
|
- name: 'Configure miniflux'
|
||||||
|
ansible.builtin.import_playbook: playbook-miniflux.yml
|
||||||
|
|
||||||
|
- name: 'Configure outline'
|
||||||
|
ansible.builtin.import_playbook: playbook-outline.yml
|
||||||
|
|
||||||
|
- name: 'Configure rssbridge'
|
||||||
|
ansible.builtin.import_playbook: playbook-rssbridge.yml
|
||||||
|
|
||||||
|
- name: 'Configure wakapi'
|
||||||
|
ansible.builtin.import_playbook: playbook-wakapi.yml
|
||||||
|
|
||||||
|
- name: 'Configure wanderer'
|
||||||
|
ansible.builtin.import_playbook: playbook-wanderer.yml
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
- name: 'Configure homepage'
|
||||||
|
ansible.builtin.import_playbook: playbook-homepage.yml
|
||||||
|
|
||||||
|
- name: 'Configure transcriber'
|
||||||
|
ansible.builtin.import_playbook: playbook-transcriber.yml
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
- name: 'Configure authelia'
|
||||||
|
ansible.builtin.import_playbook: playbook-authelia.yml
|
||||||
|
|
||||||
|
- name: 'Configure caddy proxy'
|
||||||
|
ansible.builtin.import_playbook: playbook-caddyproxy.yml
|
||||||
12
playbook-all-setup.yml
Normal file
12
playbook-all-setup.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
- name: 'Configure system'
|
||||||
|
ansible.builtin.import_playbook: playbook-system.yml
|
||||||
|
|
||||||
|
- name: 'Configure docker'
|
||||||
|
ansible.builtin.import_playbook: playbook-docker.yml
|
||||||
|
|
||||||
|
- name: 'Configure eget applications'
|
||||||
|
ansible.builtin.import_playbook: playbook-eget.yml
|
||||||
|
|
||||||
|
- name: 'Configure backups'
|
||||||
|
ansible.builtin.import_playbook: playbook-backups.yml
|
||||||
@@ -3,15 +3,19 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
- files/authelia/secrets.yml
|
- files/authelia/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "authelia"
|
app_name: "authelia"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1011
|
||||||
|
app_owner_gid: 1012
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||||
|
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||||
|
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: "Create user and environment"
|
- name: "Create user and environment"
|
||||||
@@ -19,6 +23,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create internal application directories"
|
- name: "Create internal application directories"
|
||||||
@@ -29,7 +35,10 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
- "{{ data_dir }}"
|
||||||
- "{{ config_dir }}"
|
- "{{ config_dir }}"
|
||||||
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
- name: "Copy users file"
|
- name: "Copy users file"
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
@@ -39,7 +48,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
|
|
||||||
- name: "Copy configuration files (templates)"
|
- name: "Copy configuration file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "files/{{ app_name }}/configuration.template.yml"
|
src: "files/{{ app_name }}/configuration.template.yml"
|
||||||
dest: "{{ (config_dir, 'configuration.yml') | path_join }}"
|
dest: "{{ (config_dir, 'configuration.yml') | path_join }}"
|
||||||
@@ -47,6 +56,22 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: "Copy gobackup config"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "files/{{ app_name }}/gobackup.template.yml"
|
||||||
|
dest: "{{ gobackup_config }}"
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0640"
|
||||||
|
|
||||||
|
- name: "Copy backup script"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "files/{{ app_name }}/backup.template.sh"
|
||||||
|
dest: "{{ (base_dir, 'backup.sh') | path_join }}"
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0750"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
||||||
@@ -60,8 +85,12 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|
||||||
- name: "Restart application with docker compose"
|
- name: "Restart application with docker compose"
|
||||||
community.docker.docker_compose_v2:
|
community.docker.docker_compose_v2:
|
||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "restarted"
|
state: "restarted"
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -4,13 +4,39 @@
|
|||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
- vars/secrets.yml
|
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
|
backup_config_dir: "/etc/backup"
|
||||||
|
backup_config_file: "{{ (backup_config_dir, 'config.toml') | path_join }}"
|
||||||
|
|
||||||
restic_shell_script: "{{ (bin_prefix, 'restic-shell.sh') | path_join }}"
|
restic_shell_script: "{{ (bin_prefix, 'restic-shell.sh') | path_join }}"
|
||||||
backup_all_script: "{{ (bin_prefix, 'backup-all.py') | path_join }}"
|
backup_all_script: "{{ (bin_prefix, 'backup-all.py') | path_join }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: "Create backup config directory"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ backup_config_dir }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: "Create backup config file"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "files/backups/config.template.toml"
|
||||||
|
dest: "{{ backup_config_file }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0640"
|
||||||
|
|
||||||
|
- name: "Allow user to run the backup script without a password"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
state: present
|
||||||
|
line: "{{ primary_user }} ALL=(ALL) NOPASSWD: {{ backup_all_script }}"
|
||||||
|
validate: /usr/sbin/visudo -cf %s # ВАЖНО: проверка синтаксиса перед сохранением
|
||||||
|
create: no # Файл уже должен существовать
|
||||||
|
|
||||||
- name: "Copy restic shell script"
|
- name: "Copy restic shell script"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "files/backups/restic-shell.sh.j2"
|
src: "files/backups/restic-shell.sh.j2"
|
||||||
@@ -20,8 +46,8 @@
|
|||||||
mode: "0700"
|
mode: "0700"
|
||||||
|
|
||||||
- name: "Copy backup all script"
|
- name: "Copy backup all script"
|
||||||
ansible.builtin.template:
|
ansible.builtin.copy:
|
||||||
src: "files/backups/backup-all.template.py"
|
src: "files/backups/backup-all.py"
|
||||||
dest: "{{ backup_all_script }}"
|
dest: "{{ backup_all_script }}"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "caddyproxy"
|
app_name: "caddyproxy"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1010
|
||||||
|
app_owner_gid: 1011
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
|
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||||
@@ -23,8 +24,9 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
owner_extra_groups:
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
- "docker"
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create internal application directories"
|
- name: "Create internal application directories"
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0770"
|
mode: "0770"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
- "{{ config_dir }}"
|
- "{{ config_dir }}"
|
||||||
- "{{ caddy_file_dir }}"
|
- "{{ caddy_file_dir }}"
|
||||||
@@ -59,14 +62,20 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|
||||||
# - name: "Reload caddy"
|
# - name: "Reload caddy"
|
||||||
# community.docker.docker_compose_v2_exec:
|
# community.docker.docker_compose_v2_exec:
|
||||||
# project_src: '{{ base_dir }}'
|
# project_src: '{{ base_dir }}'
|
||||||
# service: "{{ service_name }}"
|
# service: "{{ service_name }}"
|
||||||
# command: caddy reload --config /etc/caddy/Caddyfile
|
# command: caddy reload --config /etc/caddy/Caddyfile
|
||||||
|
# tags:
|
||||||
|
# - run-app
|
||||||
|
|
||||||
- name: "Restart application with docker compose"
|
- name: "Restart application with docker compose"
|
||||||
community.docker.docker_compose_v2:
|
community.docker.docker_compose_v2:
|
||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "restarted"
|
state: "restarted"
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: "Install python docker lib from pip"
|
# - name: "Install python docker lib from pip"
|
||||||
ansible.builtin.pip:
|
# ansible.builtin.pip:
|
||||||
name: docker
|
# name: docker
|
||||||
|
|
||||||
- name: "Install docker"
|
- name: "Install docker"
|
||||||
ansible.builtin.import_role:
|
ansible.builtin.import_role:
|
||||||
@@ -32,3 +31,10 @@
|
|||||||
community.docker.docker_network:
|
community.docker.docker_network:
|
||||||
name: "monitoring_network"
|
name: "monitoring_network"
|
||||||
driver: "bridge"
|
driver: "bridge"
|
||||||
|
|
||||||
|
- name: "Schedule docker image prune"
|
||||||
|
ansible.builtin.cron:
|
||||||
|
name: "docker image prune"
|
||||||
|
minute: "0"
|
||||||
|
hour: "3"
|
||||||
|
job: "/usr/bin/docker image prune -af"
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "dozzle"
|
app_name: "dozzle"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1016
|
||||||
|
app_owner_gid: 1017
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: "Create user and environment"
|
- name: "Create user and environment"
|
||||||
@@ -17,11 +18,23 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
|
- name: "Create internal application directories"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0770"
|
||||||
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
||||||
dest: "{{ base_dir }}/docker-compose.yml"
|
dest: "{{ base_dir }}/docker-compose.yml"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -32,3 +45,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
# See: https://github.com/zyedidia/eget/releases
|
# See: https://github.com/zyedidia/eget/releases
|
||||||
@@ -22,25 +21,42 @@
|
|||||||
|
|
||||||
- name: "Install rclone"
|
- name: "Install rclone"
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "{{ eget_bin_path }} rclone/rclone --quiet --upgrade-only --to {{ eget_install_dir }} --asset zip --tag v1.71.2"
|
cmd: >
|
||||||
changed_when: false
|
{{ eget_bin_path }} rclone/rclone --quiet --upgrade-only --to {{ eget_install_dir }} --asset zip
|
||||||
|
--tag v1.72.0
|
||||||
- name: "Install btop"
|
|
||||||
ansible.builtin.command:
|
|
||||||
cmd: "{{ eget_bin_path }} aristocratos/btop --quiet --upgrade-only --to {{ eget_install_dir }} --tag v1.4.5"
|
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: "Install restic"
|
- name: "Install restic"
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "{{ eget_bin_path }} restic/restic --quiet --upgrade-only --to {{ eget_install_dir }} --tag v0.18.1"
|
cmd: >
|
||||||
|
{{ eget_bin_path }} restic/restic --quiet --upgrade-only --to {{ eget_install_dir }}
|
||||||
|
--tag v0.18.1
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: "Install btop"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >
|
||||||
|
{{ eget_bin_path }} aristocratos/btop --quiet --upgrade-only --to {{ eget_install_dir }}
|
||||||
|
--tag v1.4.5
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: "Install gobackup"
|
- name: "Install gobackup"
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "{{ eget_bin_path }} gobackup/gobackup --quiet --upgrade-only --to {{ eget_install_dir }} --tag v2.15.3"
|
cmd: >
|
||||||
|
{{ eget_bin_path }} gobackup/gobackup --quiet --upgrade-only --to {{ eget_install_dir }}
|
||||||
|
--tag v2.17.0
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: "Install task"
|
- name: "Install task"
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "{{ eget_bin_path }} go-task/task --quiet --upgrade-only --to {{ eget_install_dir }} --asset tar.gz --tag v3.45.4"
|
cmd: >
|
||||||
|
{{ eget_bin_path }} go-task/task --quiet --upgrade-only --to {{ eget_install_dir }} --asset tar.gz
|
||||||
|
--tag v3.45.5
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: 'Install dust'
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >
|
||||||
|
{{ bin_prefix }}/eget bootandy/dust --quiet --upgrade-only --to {{ bin_prefix }} --asset gnu
|
||||||
|
--tag v1.2.3
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "gitea"
|
app_name: "gitea"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1005
|
||||||
|
app_owner_gid: 1006
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||||
|
|
||||||
@@ -19,10 +20,9 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
owner_extra_groups:
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
- "docker"
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_ssh_keys:
|
owner_extra_groups: ["docker"]
|
||||||
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
|
|
||||||
|
|
||||||
- name: "Create internal application directories"
|
- name: "Create internal application directories"
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0770"
|
mode: "0770"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
- "{{ backups_dir }}"
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
@@ -56,3 +57,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,14 +3,17 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "gramps"
|
app_name: "gramps"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1009
|
||||||
|
app_owner_gid: 1010
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
|
media_dir: "{{ (base_dir, 'media') | path_join }}"
|
||||||
|
cache_dir: "{{ (base_dir, 'cache') | path_join }}"
|
||||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||||
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||||
|
|
||||||
@@ -20,10 +23,9 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
owner_extra_groups:
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
- "docker"
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_ssh_keys:
|
owner_extra_groups: ["docker"]
|
||||||
- "{{ lookup('file', 'files/av_id_rsa.pub') }}"
|
|
||||||
|
|
||||||
- name: "Create application internal directories"
|
- name: "Create application internal directories"
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@@ -33,12 +35,15 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
|
- "{{ media_dir }}"
|
||||||
|
- "{{ cache_dir }}"
|
||||||
- "{{ backups_dir }}"
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
- name: "Copy gobackup config"
|
- name: "Copy gobackup config"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/gobackup.yml.j2"
|
src: "./files/{{ app_name }}/gobackup.template.yml"
|
||||||
dest: "{{ gobackup_config }}"
|
dest: "{{ gobackup_config }}"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -46,15 +51,36 @@
|
|||||||
|
|
||||||
- name: "Copy backup script"
|
- name: "Copy backup script"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "files/{{ app_name }}/backup.sh.j2"
|
src: "files/{{ app_name }}/backup.template.sh"
|
||||||
dest: "{{ base_dir }}/backup.sh"
|
dest: "{{ base_dir }}/backup.sh"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
|
|
||||||
|
- name: "Create backup targets file"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ base_dir }}/backup-targets"
|
||||||
|
line: "{{ item }}"
|
||||||
|
create: true
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0750"
|
||||||
|
loop:
|
||||||
|
- "{{ data_dir }}"
|
||||||
|
- "{{ media_dir }}"
|
||||||
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
|
- name: "Copy rename script"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "files/{{ app_name }}/gramps_rename.py"
|
||||||
|
dest: "{{ base_dir }}/gramps_rename.py"
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0750"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
||||||
dest: "{{ base_dir }}/docker-compose.yml"
|
dest: "{{ base_dir }}/docker-compose.yml"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -65,3 +91,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
- name: "Upload local homepage images to registry"
|
- name: "Upload local homepage images to registry"
|
||||||
hosts: all
|
hosts: all
|
||||||
gather_facts: no
|
gather_facts: false
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
- vars/homepage.yml
|
- vars/homepage.yml
|
||||||
- vars/homepage.images.yml
|
- vars/homepage.images.yml
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
- vars/homepage.yml
|
- vars/homepage.yml
|
||||||
- vars/homepage.images.yml
|
- vars/homepage.images.yml
|
||||||
@@ -14,8 +13,20 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
|
- name: "Create application internal directories"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0750"
|
||||||
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
|
||||||
- name: "Login to yandex docker registry."
|
- name: "Login to yandex docker registry."
|
||||||
ansible.builtin.script:
|
ansible.builtin.script:
|
||||||
cmd: "files/yandex-docker-registry-auth.sh"
|
cmd: "files/yandex-docker-registry-auth.sh"
|
||||||
@@ -33,3 +44,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "memos"
|
app_name: "memos"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1019
|
||||||
|
app_owner_gid: 1020
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||||
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create application internal directories"
|
- name: "Create application internal directories"
|
||||||
@@ -30,6 +33,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
- "{{ backups_dir }}"
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
@@ -49,6 +53,18 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
|
|
||||||
|
- name: "Create backup targets file"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ base_dir }}/backup-targets"
|
||||||
|
line: "{{ item }}"
|
||||||
|
create: true
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0750"
|
||||||
|
loop:
|
||||||
|
- "{{ data_dir }}"
|
||||||
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
||||||
@@ -62,3 +78,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "miniflux"
|
app_name: "miniflux"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1013
|
||||||
|
app_owner_gid: 1014
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
secrets_dir: "{{ (base_dir, 'secrets') | path_join }}"
|
secrets_dir: "{{ (base_dir, 'secrets') | path_join }}"
|
||||||
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
|
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create internal directories"
|
- name: "Create internal directories"
|
||||||
@@ -31,6 +34,10 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0770"
|
mode: "0770"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
- "{{ data_dir }}"
|
||||||
|
- "{{ secrets_dir }}"
|
||||||
|
- "{{ postgres_data_dir }}"
|
||||||
- "{{ postgres_backups_dir }}"
|
- "{{ postgres_backups_dir }}"
|
||||||
|
|
||||||
- name: "Copy secrets"
|
- name: "Copy secrets"
|
||||||
@@ -50,7 +57,7 @@
|
|||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
src: "./files/{{ app_name }}/docker-compose.template.yml"
|
||||||
dest: "{{ base_dir }}/docker-compose.yml"
|
dest: "{{ base_dir }}/docker-compose.yml"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -58,7 +65,7 @@
|
|||||||
|
|
||||||
- name: "Copy backup script"
|
- name: "Copy backup script"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/backup.sh.j2"
|
src: "./files/{{ app_name }}/backup.template.sh"
|
||||||
dest: "{{ base_dir }}/backup.sh"
|
dest: "{{ base_dir }}/backup.sh"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -70,3 +77,5 @@
|
|||||||
state: "present"
|
state: "present"
|
||||||
recreate: "always"
|
recreate: "always"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "netdata"
|
app_name: "netdata"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1012
|
||||||
|
app_owner_gid: 1013
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||||
config_go_d_dir: "{{ (config_dir, 'go.d') | path_join }}"
|
config_go_d_dir: "{{ (config_dir, 'go.d') | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create internal application directories"
|
- name: "Create internal application directories"
|
||||||
@@ -30,13 +33,14 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0770"
|
mode: "0770"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
- "{{ data_dir }}"
|
||||||
- "{{ config_dir }}"
|
- "{{ config_dir }}"
|
||||||
- "{{ config_go_d_dir }}"
|
- "{{ config_go_d_dir }}"
|
||||||
- "{{ data_dir }}"
|
|
||||||
|
|
||||||
- name: "Copy netdata config file"
|
- name: "Copy netdata config file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "files/{{ app_name }}/netdata.conf.j2"
|
src: "files/{{ app_name }}/netdata.template.conf"
|
||||||
dest: "{{ config_dir }}/netdata.conf"
|
dest: "{{ config_dir }}/netdata.conf"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -93,8 +97,12 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|
||||||
- name: "Restart application with docker compose"
|
- name: "Restart application with docker compose"
|
||||||
community.docker.docker_compose_v2:
|
community.docker.docker_compose_v2:
|
||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "restarted"
|
state: "restarted"
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "outline"
|
app_name: "outline"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1007
|
||||||
|
app_owner_gid: 1008
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
|
postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}"
|
||||||
postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}"
|
postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}"
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create internal directories"
|
- name: "Create internal directories"
|
||||||
@@ -30,6 +33,9 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0770"
|
mode: "0770"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
- "{{ data_dir }}"
|
||||||
|
- "{{ postgres_data_dir }}"
|
||||||
- "{{ postgres_backups_dir }}"
|
- "{{ postgres_backups_dir }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
@@ -42,7 +48,7 @@
|
|||||||
|
|
||||||
- name: "Copy backup script"
|
- name: "Copy backup script"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/backup.sh.j2"
|
src: "./files/{{ app_name }}/backup.template.sh"
|
||||||
dest: "{{ base_dir }}/backup.sh"
|
dest: "{{ base_dir }}/backup.sh"
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
@@ -53,3 +59,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "rssbridge"
|
app_name: "rssbridge"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1014
|
||||||
|
app_owner_gid: 1015
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: "Create user and environment"
|
- name: "Create user and environment"
|
||||||
@@ -17,8 +18,20 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
|
- name: "Create internal application directories"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ app_user }}"
|
||||||
|
group: "{{ app_user }}"
|
||||||
|
mode: "0770"
|
||||||
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
src: "./files/{{ app_name }}/docker-compose.yml.j2"
|
||||||
@@ -32,3 +45,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
@@ -40,3 +39,20 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: 'Create directory for mount'
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: '/mnt/applications'
|
||||||
|
state: 'directory'
|
||||||
|
mode: '0755'
|
||||||
|
tags:
|
||||||
|
- mount-storage
|
||||||
|
|
||||||
|
- name: 'Mount external storages'
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: '/mnt/applications'
|
||||||
|
src: 'UUID=3942bffd-8328-4536-8e88-07926fb17d17'
|
||||||
|
fstype: ext4
|
||||||
|
state: mounted
|
||||||
|
tags:
|
||||||
|
- mount-storage
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
- name: "Upload local transcriber images to registry"
|
- name: "Upload local transcriber images to registry"
|
||||||
hosts: all
|
hosts: all
|
||||||
gather_facts: no
|
gather_facts: false
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
- vars/transcriber.yml
|
- vars/transcriber.yml
|
||||||
- vars/transcriber.images.yml
|
- vars/transcriber.images.yml
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
- vars/transcriber.yml
|
- vars/transcriber.yml
|
||||||
- vars/transcriber.images.yml
|
- vars/transcriber.images.yml
|
||||||
@@ -14,6 +13,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create application internal directories"
|
- name: "Create application internal directories"
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ config_dir }}"
|
- "{{ config_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
- "{{ backups_dir }}"
|
- "{{ backups_dir }}"
|
||||||
@@ -53,3 +55,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "wakapi"
|
app_name: "wakapi"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1015
|
||||||
|
app_owner_gid: 1016
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||||
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create application internal directories"
|
- name: "Create application internal directories"
|
||||||
@@ -30,6 +33,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
- "{{ backups_dir }}"
|
- "{{ backups_dir }}"
|
||||||
|
|
||||||
@@ -62,3 +66,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
hosts: all
|
hosts: all
|
||||||
|
|
||||||
vars_files:
|
vars_files:
|
||||||
- vars/ports.yml
|
|
||||||
- vars/secrets.yml
|
- vars/secrets.yml
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
app_name: "wanderer"
|
app_name: "wanderer"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1018
|
||||||
|
app_owner_gid: 1019
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
data_dir: "{{ (base_dir, 'data') | path_join }}"
|
||||||
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
backups_dir: "{{ (base_dir, 'backups') | path_join }}"
|
||||||
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
gobackup_config: "{{ (base_dir, 'gobackup.yml') | path_join }}"
|
||||||
@@ -23,6 +24,8 @@
|
|||||||
name: owner
|
name: owner
|
||||||
vars:
|
vars:
|
||||||
owner_name: "{{ app_user }}"
|
owner_name: "{{ app_user }}"
|
||||||
|
owner_uid: "{{ app_owner_uid }}"
|
||||||
|
owner_gid: "{{ app_owner_gid }}"
|
||||||
owner_extra_groups: ["docker"]
|
owner_extra_groups: ["docker"]
|
||||||
|
|
||||||
- name: "Create application internal directories"
|
- name: "Create application internal directories"
|
||||||
@@ -33,6 +36,7 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
loop:
|
loop:
|
||||||
|
- "{{ base_dir }}"
|
||||||
- "{{ data_dir }}"
|
- "{{ data_dir }}"
|
||||||
- "{{ (data_dir, 'pb_data') | path_join }}"
|
- "{{ (data_dir, 'pb_data') | path_join }}"
|
||||||
- "{{ (data_dir, 'uploads') | path_join }}"
|
- "{{ (data_dir, 'uploads') | path_join }}"
|
||||||
@@ -47,13 +51,29 @@
|
|||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
|
|
||||||
- name: "Copy backup script"
|
# - name: "Copy backup script"
|
||||||
ansible.builtin.template:
|
# ansible.builtin.template:
|
||||||
src: "files/{{ app_name }}/backup.template.sh"
|
# src: "files/{{ app_name }}/backup.template.sh"
|
||||||
|
# dest: "{{ base_dir }}/backup.sh"
|
||||||
|
# owner: "{{ app_user }}"
|
||||||
|
# group: "{{ app_user }}"
|
||||||
|
# mode: "0750"
|
||||||
|
|
||||||
|
- name: "Disable backup script"
|
||||||
|
ansible.builtin.file:
|
||||||
dest: "{{ base_dir }}/backup.sh"
|
dest: "{{ base_dir }}/backup.sh"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: "Create backup targets file"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ base_dir }}/backup-targets"
|
||||||
|
line: "{{ item }}"
|
||||||
|
create: true
|
||||||
owner: "{{ app_user }}"
|
owner: "{{ app_user }}"
|
||||||
group: "{{ app_user }}"
|
group: "{{ app_user }}"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
|
loop:
|
||||||
|
- "{{ data_dir }}"
|
||||||
|
|
||||||
- name: "Copy docker compose file"
|
- name: "Copy docker compose file"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -68,3 +88,5 @@
|
|||||||
project_src: "{{ base_dir }}"
|
project_src: "{{ base_dir }}"
|
||||||
state: "present"
|
state: "present"
|
||||||
remove_orphans: true
|
remove_orphans: true
|
||||||
|
tags:
|
||||||
|
- run-app
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
# defaults file for eget
|
# defaults file for eget
|
||||||
eget_version: "1.3.4"
|
eget_version: "1.3.4"
|
||||||
eget_download_url: "https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz"
|
eget_download_url: "https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz" # yamllint disable-line rule:line-length
|
||||||
eget_install_path: "/usr/bin/eget"
|
eget_install_path: "/usr/bin/eget"
|
||||||
|
|
||||||
eget_download_dest: '/tmp/{{ eget_download_url | split("/") | last }}'
|
eget_download_dest: '/tmp/{{ eget_download_url | split("/") | last }}'
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
owner_name: ""
|
owner_name: ""
|
||||||
|
owner_uid: 0
|
||||||
owner_group: "{{ owner_name }}"
|
owner_group: "{{ owner_name }}"
|
||||||
|
owner_gid: "{{ owner_uid }}"
|
||||||
owner_extra_groups: []
|
owner_extra_groups: []
|
||||||
owner_ssh_keys: []
|
owner_ssh_keys: []
|
||||||
owner_env: {}
|
owner_env: {}
|
||||||
|
|||||||
@@ -4,9 +4,15 @@
|
|||||||
msg: You must set owner name.
|
msg: You must set owner name.
|
||||||
when: not owner_name
|
when: not owner_name
|
||||||
|
|
||||||
|
- name: 'Check app requirements for user "{{ owner_name }}".'
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: You must set owner uid.
|
||||||
|
when: not owner_uid
|
||||||
|
|
||||||
- name: 'Create group "{{ owner_group }}".'
|
- name: 'Create group "{{ owner_group }}".'
|
||||||
ansible.builtin.group:
|
ansible.builtin.group:
|
||||||
name: "{{ owner_group }}"
|
name: "{{ owner_group }}"
|
||||||
|
gid: "{{ owner_gid }}"
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: 'Create user "{{ owner_name }}".'
|
- name: 'Create user "{{ owner_name }}".'
|
||||||
@@ -14,6 +20,7 @@
|
|||||||
name: "{{ owner_name }}"
|
name: "{{ owner_name }}"
|
||||||
group: "{{ owner_group }}"
|
group: "{{ owner_group }}"
|
||||||
groups: "{{ owner_extra_groups }}"
|
groups: "{{ owner_extra_groups }}"
|
||||||
|
uid: "{{ owner_uid }}"
|
||||||
shell: /bin/bash
|
shell: /bin/bash
|
||||||
register: user_create_result
|
register: user_create_result
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
app_name: "homepage"
|
app_name: "homepage"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1008
|
||||||
|
app_owner_gid: 1009
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
|
docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade"
|
||||||
|
|
||||||
# Registry images
|
# Registry images
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
base_port: 41080
|
|
||||||
homepage_port: "{{ base_port + 3 }}"
|
|
||||||
netdata_port: "{{ base_port + 4 }}"
|
|
||||||
gitea_port: "{{ base_port + 8 }}"
|
|
||||||
outline_port: "{{ base_port + 10 }}"
|
|
||||||
gramps_port: "{{ base_port + 12 }}"
|
|
||||||
317
vars/secrets.yml
317
vars/secrets.yml
@@ -1,157 +1,162 @@
|
|||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
34393132353631343361373266363131613633303038633363333036386436356139616630346163
|
66333736306664316131646233393639356436323832386638353237393761386631303639396432
|
||||||
3034666435313833306265633833326631353562336266640a396332316162313462396334326439
|
6266616434663638306637356435393564303633613332640a663366336135373061396239653065
|
||||||
39383731303565396132643736323562383832376630633338393362623439353366303830656662
|
32343339313734346261363461633735383538613033656138383835653661633334316533613738
|
||||||
6265383433376136660a326164316431336637376635306138366262333935646362303935353135
|
3730656661393466370a653863353234323739346630323534333334306432646636383664313865
|
||||||
65666330383933613838356361643932353630656137316131663764326137346137363435396534
|
39666133623032393638633239653665376336626638303334626164376663626666393439346661
|
||||||
31366531633339373838623036633931356633306535366133396230383738353136323863653232
|
32363364363066653933313135666566346138353163333639353062336331623938353131646334
|
||||||
62393461333434623561333365636464316533313763346361343764326238383464346630633439
|
32326466323030643364356530653862633332363338333633393730663536353363663165303838
|
||||||
30666333636532333133336637643532656434346162626463316634633732326561353461373530
|
66643331366231613733316139333233393636626162643861346437613062643133353035353566
|
||||||
35333231353635616664653230306664323663306536616663646638333335383432326565313737
|
38323062653135393364376265346361613163346337636634383932666137636133363864643264
|
||||||
65616230613738313866333937386230666461313731656234613234363733303235323430653530
|
39646634363037383662376166633335636365303734646465623833313766656235333835396263
|
||||||
65303664613138333966376364616135303866666330346264303133323331343337633332366665
|
63326237306337396465343065313263663466613336303934373337623364613839323931623934
|
||||||
39643063386331643763393037393633383331333536303763363135306339623932383538326566
|
35353665363339646365353139643032366164653133616562303930306264343633343439373830
|
||||||
66376339643530616666663838323063623463333038633330313434666562383365343165316131
|
66356635343363363361313633353134313837653163346537656465623832633561323833663763
|
||||||
61313632303965306166323562656230396435343336623432313638313662643866353934636434
|
32323537303931343934376165663466356466333533643237653038373866626439356334663539
|
||||||
36306632633436306361636533613733633935656666333332633033333431323861623065353534
|
36613336326633306331323763393230653236306337353139353135316566336661623464316237
|
||||||
62356330303564633163356130663865363465623236373932663662663536656235613434353939
|
37363930666539373837336466333265333866633733363032323862333239643764333766366134
|
||||||
31666537396635366563643139363662313337313037643530313066656538663861376464353933
|
33396330613463633261626334393262343031663131313737623233323535303965333864366631
|
||||||
36363138643466376264363337646462653562663633626430316632393865313738343662393432
|
65333535343062393434396361313433386463613630353765663363633431393831316233313162
|
||||||
31306164613431626166306437613164333234376164333765353034383530636137646438383438
|
39656333306265386437663863343638323262663734623830336562636366333534373735313932
|
||||||
62366637376364373565363935353663626363303035376562323665636432666437333136353130
|
61653739616635643661306165363364316262386434643033646430366235303334326236373262
|
||||||
66663130643330306336343138636462373838633264326632343433636163353061636231313135
|
34396261653934633337646338333364326237663739623965313039303134393238653634616431
|
||||||
39363437333535313466383937366636333133653438313037346431633166306331616165343337
|
39376539323238353033323333643337613935616335336562626361383164616432633331313536
|
||||||
66613036333431313066616138666666663266393030313832653332643138626532666531336437
|
63373633633966373237393131663064613332316536373163303163653638343264323934393365
|
||||||
37343635653665333861613263343439643432396330366534393336336361653037326264353437
|
62356636643434646465313734363936346435663237393064663138643135303237633965393965
|
||||||
36663836386663303439656266383630323561363332303837373030316435303730643564356630
|
31353564616136366237666139646561363364353738633862346262616637663362333137386133
|
||||||
65623064333265303666323966353865376565373137363134643964653630313365303334383732
|
63383662363866306366326232643462376532313632336662666666386562356436616236663430
|
||||||
63666161336539313136303637373336636237353361363061613439363561393561633431343433
|
37636661656237646431616331633833306266313434326461353561643835646236346539323337
|
||||||
31336137646330366565336538646462666532376265313332313664653430303737303435376665
|
30303065313165306233646135363766613931383433313165366662636137326162633363306431
|
||||||
32346364323038303435643836316439656636353530346336303266663733646665383039623264
|
31626564323566313036363666386338366462613164663735363333313830306238323365363737
|
||||||
32613935656332643435333630666162616338333762633131656436353130353532393939376363
|
65333137363631623139333964316464376662643661643038326162643630323938626332633130
|
||||||
38303530393665393333303064353663353238643436303032306237663031323035623230623163
|
62626334666131373731343432303461656537303062396638326134356166663936663364656139
|
||||||
38363965646437643033333335363137663037306436343764373266343335316637373638306461
|
32333864653231636235663165323936626135313838663866373132376437656236363235353533
|
||||||
65646436633034313236353737363734656561313161343036353237613536623432393334666539
|
39346532626666656233343433383434613238653833626436643566653462346330316565366564
|
||||||
61346266383935373466393563636635316566323765326430366530393836663333646331383962
|
33623831643265623966326638656462306161636366303266333734396433653861393363333332
|
||||||
36616663623465356436653033313534336461313562636530306137386532366265326336333336
|
61616463613931393835613463353039646164383435373330626134376339623861396266623430
|
||||||
37323563373039383266646632633635313239666630393561353231663436643434373635353163
|
30316365356337356531363263623362633332313536373333653964356534623861613232653932
|
||||||
37626165323733323135373033323633346535646538396331653231616262313034623235373836
|
35613064336131313632656663303631363664366163343362643365663932356138376235313466
|
||||||
65343639623837353534663938313934636230363862613532623434383632383662343736363935
|
30353333376461623435363931356665306333623736313562333836336563616137633863346635
|
||||||
38333363613430646230366539333834303665386265336565316539343664663534336165303362
|
37356137306361383134663535626130646135363661353438343961653766333538353330346236
|
||||||
37363234336563353165633630313534666461303639306634656366383764326335306565303336
|
64326639383864396336343062663965343964396162386266363639643962666431336237303864
|
||||||
32356165363637313065653937303834363534643137363030383232383465306530626334666362
|
66663861346139363335663362333032613637336266323439366566373136643133383361323061
|
||||||
35626563323636323231643031393063613839353333386231623337633435303565643565363539
|
37633134323933613665366665383962373935646137616139353661336665613661623834303465
|
||||||
34303364613230366162333435623330313963636538626535303033333461363239653333633634
|
30323264333137636261393535376438363134663734313662383533313130623365386335323662
|
||||||
61633334636364633861643130633061623564656239376632666561646430303831666137656236
|
63366139643238613632326165313835363964336237383936333737646239363365623030666364
|
||||||
33646134353039656361623739623835363137663530626535336235666238626536376335643937
|
64656432653164643565376666373262333839353139666561623731343234326637316636333765
|
||||||
35633736346137323663366138393162633466633238306639383236396234343839303630616130
|
63306331343538386433376566326239376232363434343838653864393562383063663566333263
|
||||||
65623362393635633965633635303139306261373262363434303436356561373534626162626330
|
35326333326462316134383139303534343263646530363266663933353834353138303435646339
|
||||||
65613266396362373230323631323030633830366531643863363337323763333461363139626538
|
33643361363062373735663430346636333431363736373463326439353437356530373935633962
|
||||||
64316135333631373031333464623663653630613736393437303035356163323537633865616633
|
63333031376163656565663536366230333731613833396266383465333461386161373337323863
|
||||||
65616136323162326537343330643337373030393163306232303161316632636163333231613138
|
36313032613534346230636566383930656330656133376431376462656536386263343831393862
|
||||||
61316266623263366266313831343961356339313430633865303663373664373066663037646239
|
32363365623061653837303736636664663361663862656562393661623330386435646336373531
|
||||||
62626564623632343531333238656461653664623134643665323139386530383037346332653333
|
35363037356638653831386261646235613337363066343632653632306631633138633235356139
|
||||||
38623536663934656636376334613733373939396633346436333866633832663631343466653339
|
39663936333262643061646330316538373862353030626632396336393030643239316634343730
|
||||||
66666536663634633933636363313733336530333263643735626362323631663661613331313931
|
35363564373865376533333466666439646633313932656665383930623531633038316363636332
|
||||||
34313539326439623363613939616362633837346439653636353961316132396636663263333234
|
37363466643835353132333532646163316636303662646234613038333334626365623964653235
|
||||||
37333338373730643538383064623161396164393833383739396666643033313135643132613239
|
36393739353164306666313537633538383934363330373235353262616165623132613330303735
|
||||||
35633739663931626362383664643434616264663336323938363165323331656431323265306361
|
65323362666263653937376562633833653264613439303236373466646362316462386632373038
|
||||||
64363431616630613566613632623061306135643765656332356164383439313265306233393962
|
35353462633730666430623638626439626364616335643964623933663233376433353233633235
|
||||||
34623366313563663164313261316162373430613062373138376439326232376235636366663036
|
34616233636335323365646538326639383033643832323139633064616635353331376230663738
|
||||||
37376365326261336361396666373663383239383735623534343239363232636666366135343738
|
35316665616230633335366233363332353432633937653335316662306166383337633262633634
|
||||||
63323161346266346431343339346364623439346235323764663534393432326564633362376262
|
35303766653733376362623436383663663437393461643266376530613533383038373035356234
|
||||||
39363030636261356361303365316133613232363666353761653532613433326262613065393166
|
32343962623930336566363164616361386134376362383138653963366339326431653832393664
|
||||||
35343737343039323061613833623536663031323636333762306331383664303730643666663436
|
66633566343666326339323536343738396532623735343034646130386332323466346365383135
|
||||||
35383938656436613036646261373135623065383236333063343931663738383862343365653966
|
33373732363833316138343938633262373066613930343162663163323331373466366631626237
|
||||||
38616362393034396434666337396262373437623030623832376131633036656433663835636531
|
34623062353139353734653366313363323634346564663861613233366537663732363134643638
|
||||||
35623633666339656633616135366465376362306362393965663133633733346265333930643339
|
65623964396665303765343430623636376637666364363835346462613763306135326463353631
|
||||||
65326533616461633339323135376539363866383537336531343863643563616264313864383164
|
33333834333464383437336531616662643461666138383435636530373761646564626438313433
|
||||||
32356632663061333139383938366531356261386663393663623661323139386239313937393534
|
64643037626637343433366631343266373436343864396663353039333231646462333932353436
|
||||||
65323935363765396434306466653733376338363664376433666538326133626635646634333434
|
33643837616639666363353237313137353133346636383231623634326335386537363030613537
|
||||||
32313534366364323435336361626136363036653336656632653364323836613165633362353462
|
33306333363638643533373237633833663333393566656466313832383636663031663433636663
|
||||||
30383037366631643037306631663361376166353733353832363530373061383631616238656434
|
37313034353734343966613934663530643537366562623137373331623632653466333839376331
|
||||||
34636333376238363738336337613038396664393037333939353837643963633635633030383066
|
61623866633937666365633763613138346365393934383163623730373134373531616138363733
|
||||||
61303933346630333164623963363263313233366535623630636665343935333065633530383039
|
65616230303337396561666462653866333438353463316235303331643834653165363033626537
|
||||||
34383361646138653862363431336132346466633563386564393761393438323038363166336536
|
37386636653337363666393163393031333461383331613965303262616530363133373732313362
|
||||||
30663763356331626334626665316130373162326433663238613833303034653439316337653765
|
33343033373963656333363064303035666663646536373764323833653037316231316430333634
|
||||||
31353136633230323662653534386361636334336533326439353538333236363535323535643538
|
65383236663035326636336535636462323438363165633437333337613439326433626466396365
|
||||||
34626637333364653662376430613731366337623434373165303330316662376338353837343937
|
38393362633266663962646565336539333239346133323434646632643537613435643434623631
|
||||||
62353037323239633036613864633034353064636463373938396566363166613062343733626465
|
63636536393936343165393966663438343261333966363639323462663566613437393838323036
|
||||||
61386239613239396163376533663862616361623432323662386161663832386136373032353466
|
33393163323034336533333632646230343138336333353236376136383664646466626666356234
|
||||||
30346431396436656339353065393135653664333463626632373338373638643439663961633365
|
61653466363933333331613539636431393934393235376433326665643263663638306463393837
|
||||||
61636537353731333035626332383663393863373435303064653338643262383564646539666633
|
34396339383536636461366230383938386339303334393038343239363361666565336237326465
|
||||||
62373030303938316236663661316461626534303862623234646465383330353661323639383664
|
63623231663861303436353533663661656431646165646662383065386362636633333631643335
|
||||||
33306133623265373938356131643366316538643733373139333664613939656133393630306538
|
38373464373432626538616234623638303734626237393566326463633765316365653837303433
|
||||||
62323134653163336335363262653663363735323866666237336336393138336235623437326233
|
34376438323439633237313733343836343733373930643138636333366166353666373966323231
|
||||||
35313736323832313737303965636266343564613139393838376133396532656662353735313336
|
63613466306365386137336538613837613264633735393937343166303736396162303230623430
|
||||||
37633238353362323534626438393838643833616330393464353837326238366332613339656239
|
33363233663761333063363134393935333530343133303138373934666236393732396330643438
|
||||||
34303664316263326432363930646662306635303234663931323363656432623035363561336138
|
61343231666363323834346132376135303339313766383365363837313163393636333563376436
|
||||||
31383637666431333838646139623162363164633964653936336137616230646430323466373736
|
64333832393361626663376161343033383763373537396264646239303736333263303739326436
|
||||||
63323661653361306331646363666561623930653035623764366235636161386666663931613632
|
31393665363734323364643032393031313135623563383639316163616535323938343765356264
|
||||||
33306262386337613939366632373166666437376364633038633565346439616662346665613738
|
35616433396236396662393930646431643063323163343366313030383766323733333865616235
|
||||||
38623466646131373762376264386461663134373661353563353661333635313839303232666531
|
65623737663763373363613030326665613333636366393464383139626462346333323162333537
|
||||||
63663136383737396662616165633639663334623137626537373332636430653765343066306530
|
32386635636532346539623534346364623532656435653638643962353836653330316638633661
|
||||||
64383632356165316337623037616566306463353036653634303538393733366535653862636234
|
38363330656161393766383237663737663835646565363966373563663832313263633238316165
|
||||||
35336235393261303961626364313330383965353337616430313739316266323462653838356230
|
64643536336232396436636261303866376132366261373132333631646166393764636130326335
|
||||||
35393732333265323833623233666164663163346632373966646638643265633037313635396464
|
39613037633239626666396262386533343764623233626335373139663937613433356134633330
|
||||||
38333836323362303638386536663661313138666235653261343036663565363933376132653836
|
32656138383563343734616536303265306661386362343939626332623763393136323138653937
|
||||||
62646461663534653035663432363365316131623137316665666664316136353966643161383938
|
39646537366461353364616538663361663465626634356338323166373837323339343061323261
|
||||||
62303461353166623239346235643065376336313565656336656465366233393230613033643265
|
33393765653034643166313639323962306265383336663265623832393038386334643661336663
|
||||||
35363161303861393039616237316538336166613938343764613030303361663934633030616362
|
66643330306239383736636330393266346537326436663432653031303765393930633761343363
|
||||||
37623239626134373361333237623431653735393866343463313037393038396432653235653239
|
61353163323830656538373366323230353830633534326133613861356132623938663565393130
|
||||||
65326530383462353363336430366336353735303335316465376662393232336539613037346135
|
63333764613761663162633035353036663736656138623234313833326337316438323835313465
|
||||||
36323461353939626465656439313865306165353936393137343662366139336465616561633137
|
31653136356638623965363266636461646465313065666137313931623063303337393963353437
|
||||||
39623764376435386536326662626464306231373161376431653463636164613238303438363035
|
39633166326662303834303261353965363235626535643564613064306636366461666263383833
|
||||||
37323239373634373162353864343639613861343832616161336337373765343636663030346139
|
66363966636538373435323365636630383561336533356631646563646536383333393864613164
|
||||||
35376462316331346662653939353465383661393839306531396237663364346438336164343465
|
38393638623263666239623835613933393564323132373630623734363334663735633438663137
|
||||||
37653163373231326637333332313766656536396539613839316134363531666166356335373261
|
35613563356161316632323562626639633931643762643232636136653731323337333236326363
|
||||||
32333437303563323161623665343239306639333532376264363964383961633236323138323438
|
36633338386230626665373666643831626132323866653430393531336466386463663865393962
|
||||||
34636265353664346264373663393765343638386236373863353734623738646437363134656666
|
37363633343632653963306531393537633762313565303631643064363531363839643363356139
|
||||||
31303432373363633365383932366661653163366464343164336165613637336536653937376364
|
33666162306331393237333966643735643731633331373839356637636462633836633762653039
|
||||||
62643332373337326665313761316365323263316130643564313836626336333364343135393465
|
32633630303437633130613366386633346637623266663361656365633737346432343263646536
|
||||||
36306431633765343935626631333764336634343636613864623663646661333432306535333339
|
35376433386464383533323231353830666363363437623033643238363438303565616137316461
|
||||||
36376564613066306266653031396437363566373465663234623535636335626434326566393238
|
64636233643365663666656431613966396561366236383363303135613433626466366437396465
|
||||||
39366330393661303730393831633665356536336338343834373733346334343264643733393031
|
37303562633336636635363133626238643430663132626331316437626532356237383730626665
|
||||||
38373161373939636134653663396437306534663265316333346338663132616131633139336262
|
38373633323531346366313631376231353966626637636262333936343066336639396262646436
|
||||||
63616331313965613062333064326638356266373935393733633664303862373134303664383362
|
31616165626466623066336138653966323334326236663439643561663863323130653631643231
|
||||||
32613434386537643030383064383865303431316339386233336564313432343534363834303138
|
31396335333066326166383337353033376335376664653162396466346638653531346239353936
|
||||||
37393536343032343432343766633333366662316535303336346361356330373262366537636335
|
33663862323135623432333036336162353061363537363463366435643461356562323935653962
|
||||||
37366561373231636630316232656132393365336438386566333363623638363235386634656139
|
62333130616466353032316462356233393037326438303064656235323966306535346132663232
|
||||||
35666263383735626233383235396363393436633738303639633838326263396538303334646532
|
63393238646534316433643638646536313934666361323061306561396132306431633932313031
|
||||||
64343538396435663166613865376433323461646539636462356466633134333233366161376639
|
31356430313463613331363064363265646361353430646563396238326330323038666264653935
|
||||||
30303837333331383638663530343630636261323466616331313263333962353366616531353337
|
63306635323439326665306135643730386239356661303232353737383532353363303163313437
|
||||||
36343230343965333364336664313131396433626138363038336365353730393534386633346263
|
38623064383035373239623966336563626130636335383833383366343130393939316535333466
|
||||||
65663731313566643731366561353965303039303830376139623030613237333037366563393663
|
65353062396632373330663365373761656161653837396666376531376663313466393230646461
|
||||||
62643730653431666161386663643536666333396166393338326239633861663465323236356462
|
62306465363166663962316634663763626666306631363731633834366433363630383833393361
|
||||||
65663033353834306332323364306462393035626339353461373134626363376261303564653638
|
36393932326138643432666437373463343061313135613163343034373464356666333134366664
|
||||||
64633330646134666336323965633065326561386233383338343837336461333236633935316238
|
66343461393733343130636266623661323066623332633930326361626436366237323664336637
|
||||||
64643433383766343461353934616265316335386661323365393032613461376562633239303235
|
61316135613330663835353139613039613264383838313337373432623133363735333732376566
|
||||||
61323730363963663039306530653631626266343132613239353435303030666562383263623561
|
38633763336438663737653736326466646237313130623934396531306430363461653962336132
|
||||||
32383066363230316332306365303735336465316336356634346530366330643132636266356139
|
64313437323766613632376439336234393165646338396334373037323237633737393866303231
|
||||||
63316461393664613364643163613536353062313832643262376330393464343738653363333765
|
33646338333532613432393234316235653466633639306465363062656335353034666665623631
|
||||||
32386663373732623234333032353964643033643538393132306133393365326438393564356364
|
66386437616462633336636566393537323831313566326637386466616331396464653438396562
|
||||||
62626439366132393531353733613936643363393736653461666339353036333333306466363430
|
33343132626463393364386133323163393336313065316433613133663961633033613232343337
|
||||||
66346135333162363033383339373631393836376635633136323134623861663066356638396136
|
37313334386533366465353461633930643662326235366139306335656163313864636161623239
|
||||||
33623131666163633035666665313236646235626363363633343263376333333133326564366637
|
38336238663630313836363739623334633130616433393536303431333735643565326265613561
|
||||||
35383663386133326432333537653032633230353435303935333161346532663731666339373431
|
30613762396162633964336165666137626333643735323330646266666563313935623230643262
|
||||||
37656364643531663866653962306638333663366338373537323035323832656365343239613739
|
30383635663035653437333339303730346366333765353739663231643433353764363966353435
|
||||||
63366166393237613534636563333137356365333936303633656134346166396530656133356562
|
63663466343864363033646663376261363562636630643038613365333936653165633733623134
|
||||||
65326230643236633531613139363134353731363730633165636636653339363266656265623330
|
36626135313330303463336535663235323536613661353332653139336165613261316638666563
|
||||||
64633063336330396130316264656165616537616162353130316138386335656635633237313462
|
63346131306536353630363236613864393935333431333864333464353664366134313861633463
|
||||||
61633231373236303534313861613639663732633638633030333465376438623234386634616263
|
34336364663030376264663961396235643438653730396661643032623762623965623737326430
|
||||||
34643939316135303737303837336533663739316633323238613838363531636463356338343235
|
34333236316335373863663232356136333431666233353138353139313466343762356632396561
|
||||||
31346164346535643733653936303337393230616137633161646364353034346632663830613332
|
37356162303636343833316432353831373532326436363538333466333636306666626465666333
|
||||||
38366138633334633131663662613862633162626366616435636563383839303766363131643763
|
36653065363538653266643831326234356534303133343564646564323762653238303161336131
|
||||||
66336530303835613432343065353031386636353761303730663865656336623333333464386334
|
35396235343837333331396535646564656433343766323765333465356134323536636639623962
|
||||||
35373365613766396237616462386432623635643537666565353734623663663136653766643031
|
65633233376136316334653335623666313666376661326362663338633862643963353536616563
|
||||||
61346162303534346262646236663935656561353137373132643664616334623334376437396533
|
66323939643332663665306536356566383164356561303935313432373264353738643131653831
|
||||||
31393035323366353961333538643233393832616534343530333137643064366635366236386466
|
31346231633037616139373330316231363938386536303432386638323139326132663539663738
|
||||||
65626235356438343038373331313131626365386133613464653561376566313132666236666461
|
64636231663538333932363332663137323764346336633336363965616535373030663363363861
|
||||||
61336639633163373662653939613830613863363230303834373231623231333165363362303436
|
65363632653831353761306231663562623732353433656637353966303033636666613739306230
|
||||||
34653366663838343035646562393031613062643332663739643334643436316336643261333133
|
64363465623031386431663565623966643836383532626134366363656265646563343635383538
|
||||||
34623764323363626362643662313233613238653133626631353338643336663664613030386266
|
32346166363832626664613335383731616135376635336266326531663731373633303131373965
|
||||||
34633632316431353039666438633366393038633462326262396432316438316664333130343634
|
66663666333739373033616363313132643938656335353164343764383933636265656665663433
|
||||||
33383632653535636565356334333563353138313134326330613865376561663731363138376130
|
65386235636531653236333465316331353938323331623733623731303462306566333365383134
|
||||||
37353461346364343563616464623837326665656532633062366439313063336639633635643837
|
31356337343732613764633731306335366136346633393265666331636465323566656337323838
|
||||||
34323738373631623937653533366330333635323436346630633466666665323234613938636330
|
37393031316630623466393138636666303065623430656336386430363962336539616262633030
|
||||||
65333936633766343930
|
62616365623765366236303539353166306630393362386430363736313438346266366336323839
|
||||||
|
34376462376633366463636339646337623965633663363862393261373535636230613532386464
|
||||||
|
34393564383130306138366564366666646132363732653036353135656132656339353932383530
|
||||||
|
61636234393165326631303731633062333735306635303566373838393164306538303664313266
|
||||||
|
33346337613836636337316638323631396235643363333836323135303738366235326465376630
|
||||||
|
3363
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
app_name: "transcriber"
|
app_name: "transcriber"
|
||||||
app_user: "{{ app_name }}"
|
app_user: "{{ app_name }}"
|
||||||
base_dir: "/home/{{ app_user }}"
|
app_owner_uid: 1017
|
||||||
|
app_owner_gid: 1018
|
||||||
|
base_dir: "{{ (application_dir, app_name) | path_join }}"
|
||||||
|
|
||||||
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
config_dir: "{{ (base_dir, 'config') | path_join }}"
|
||||||
config_file: "{{ (config_dir, 'config.toml') | path_join }}"
|
config_file: "{{ (config_dir, 'config.toml') | path_join }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user