mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-11-02 11:18:09 +00:00
Compare commits
194 Commits
a6a2be4373
...
feature/ke
| Author | SHA1 | Date | |
|---|---|---|---|
| 6fcf6a1ab6 | |||
| 4d9890406e | |||
| 59b652958f | |||
| a327adf8db | |||
| 7a38cb90fb | |||
| 9d6cf03f5b | |||
| 9439ac7f76 | |||
| 23353ac878 | |||
| 8beda2d45d | |||
| 5773409bd7 | |||
| b3ea962338 | |||
| b9fbf92461 | |||
| 6824e444b0 | |||
| 5cdcc18a99 | |||
| e7702948b8 | |||
| 09a4c243d7 | |||
| 1d5a50abf2 | |||
| 0d99c7f297 | |||
| 0a17e54d8c | |||
| bf94338845 | |||
| 5d42b78b3d | |||
| 26a1992d84 | |||
| 2439beb95a | |||
| 251f7b227d | |||
| 3fbb9c38a8 | |||
| 29e8b3a590 | |||
| 27b89d8fb6 | |||
| 55f2d15e93 | |||
| aa19a97ed6 | |||
| c06d1c4d17 | |||
| 66f294537d | |||
| a9097a3ec3 | |||
| fc59c64273 | |||
| dbbb3510f3 | |||
| eb3bf543a4 | |||
| 4f5602c791 | |||
| 75d476267e | |||
| c3e5db7f2e | |||
| dfd2d243b7 | |||
| 78ad2ea4b6 | |||
| c362e160fc | |||
| a044028e03 | |||
| 7405883b48 | |||
| 85db0a40db | |||
| 8af39c32ec | |||
| 31e86ac0fc | |||
| 4d223f1784 | |||
| 926def3d01 | |||
| 083b7d2914 | |||
| 73a38e0b2b | |||
| e3c0880e98 | |||
| a817d964e4 | |||
| 7572134e9d | |||
| 97af4990aa | |||
| b6d0535173 | |||
| 27d33435f8 | |||
| 3cc4014edf | |||
| 63da669c33 | |||
| fb04a4c7a0 | |||
| 2968ac7f0a | |||
| 1daa53017e | |||
| 9082443753 | |||
| bcee1fecdf | |||
| 0602148caa | |||
| cbfb991e79 | |||
| fa7b1400bd | |||
| c7cae93597 | |||
| 6ea0d09f14 | |||
| 5e4cda0ac9 | |||
| 1d29617f85 | |||
| 7c5ad8e6a1 | |||
| a26538d1b3 | |||
| f55b0ca797 | |||
| 6f3522dc28 | |||
| 5186eb5714 | |||
| 73bcdcaf45 | |||
| 9e402c863f | |||
| 84865d61b8 | |||
| 423850d3e6 | |||
| 598f4e854a | |||
| 1f99a6b84b | |||
| 189aaaa9ec | |||
| ca52dcda43 | |||
| 4f59e8e48b | |||
| a993c153dd | |||
| 8d6ebb4693 | |||
| 567babfdfc | |||
| 18e5f001d0 | |||
| 7d9cb5820f | |||
| c181c7f6cd | |||
| 929cddec0e | |||
| 9ba0efc1a1 | |||
| 9bf77e1e35 | |||
| 426ba32c11 | |||
| ff7b7aeb2d | |||
| c523d8d8d4 | |||
| 12d05ef013 | |||
| 3cbf37d774 | |||
| fc99c72f86 | |||
| 3211dd7cea | |||
| c07a9835fc | |||
| f4cf55b3c8 | |||
| 1b91ddeac2 | |||
| b638d00d73 | |||
| 75c36a1d71 | |||
| 7a119c3175 | |||
| 3e6193ffce | |||
| 9d8e06015f | |||
| 5daf3387bf | |||
| 6da7f28370 | |||
| 208848579d | |||
| d8c73e9fc3 | |||
| 10b20cc3c4 | |||
| 790c184e66 | |||
| 93d165fa4c | |||
| 1f3abb95af | |||
| 7ca3a73f21 | |||
| 08720a43c1 | |||
| 1baed62078 | |||
| 963e1aea21 | |||
| a819a05737 | |||
| 4cb58bec0f | |||
| 002f45d1df | |||
| cbc4dad1d1 | |||
| 70d395ed15 | |||
| e20a709f04 | |||
| d129f71cef | |||
| 4cb428274a | |||
| 97e2d440b2 | |||
| 588cd1959f | |||
| 5d1210d651 | |||
| aeab7e7358 | |||
| fa6bb67a66 | |||
| 3dc2fbd47c | |||
| 4b56ab3d18 | |||
| 8e934677ff | |||
| 0a927f49a2 | |||
| e6803e5614 | |||
| 6cf6c74802 | |||
| 734b8764f2 | |||
| 3edb66f444 | |||
| 181b2d0542 | |||
| 78ebf4d075 | |||
| d523629cdd | |||
| 08ac8b6a9d | |||
| 79db2419a6 | |||
| c424afa935 | |||
| 974a83fe6e | |||
| 0168167769 | |||
| 1c7152ceb2 | |||
| 2a98b265bc | |||
| 14d1362dc8 | |||
| a4a8061998 | |||
| 96ded68ef4 | |||
| 2d8967d559 | |||
| 5e616d3962 | |||
| 0f85d27a4d | |||
| c6677ca61b | |||
| 83ce88a048 | |||
| 7d150fa021 | |||
| 2806aab89e | |||
| 61772d5916 | |||
| a10ba78a5a | |||
| 6854acf204 | |||
| 54d4eeb1ab | |||
| 52fb7accac | |||
| d4c62dbf72 | |||
| 9ef4f91ec4 | |||
| 5bc635109a | |||
| efb5488cfc | |||
| 1dceabfd46 | |||
| c64ac0b4dc | |||
| e94aac1d78 | |||
| c274c1a5d4 | |||
| 62493ac5a9 | |||
| cc2b9d476f | |||
| d9c527e2e2 | |||
| eafdacc378 | |||
| c93ec6d43a | |||
| 0839b8e37f | |||
| def6dc96d8 | |||
| 364f4799bc | |||
| 6eb4ba45f7 | |||
| 0566c426c9 | |||
| 9ce73b9c71 | |||
| 83936edf73 | |||
| 40ecbc5466 | |||
| b18b3b104c | |||
| 2f992983f4 | |||
| d7d8578b13 | |||
| f106d5ec36 | |||
| 53b3a3a7b1 | |||
| f576b42579 | |||
| b0f10aa0d0 |
6
.github/workflows/test-cli.yml
vendored
6
.github/workflows/test-cli.yml
vendored
@@ -21,12 +21,12 @@ jobs:
|
||||
|
||||
- name: Clean build artifacts
|
||||
run: |
|
||||
docker run --rm infinito:latest make clean
|
||||
docker run --rm infinito:latest infinito make clean
|
||||
|
||||
- name: Generate project outputs
|
||||
run: |
|
||||
docker run --rm infinito:latest make build
|
||||
docker run --rm infinito:latest infinito make build
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
docker run --rm infinito:latest make test
|
||||
docker run --rm infinito:latest infinito make test
|
||||
|
||||
@@ -59,11 +59,4 @@ RUN INFINITO_PATH=$(pkgmgr path infinito) && \
|
||||
ln -sf "$INFINITO_PATH"/main.py /usr/local/bin/infinito && \
|
||||
chmod +x /usr/local/bin/infinito
|
||||
|
||||
# 10) Run integration tests
|
||||
# This needed to be deactivated becaus it doesn't work with gitthub workflow
|
||||
#RUN INFINITO_PATH=$(pkgmgr path infinito) && \
|
||||
# cd "$INFINITO_PATH" && \
|
||||
# make test
|
||||
|
||||
ENTRYPOINT ["infinito"]
|
||||
CMD ["--help"]
|
||||
CMD sh -c "infinito --help && exec tail -f /dev/null"
|
||||
|
||||
2
Makefile
2
Makefile
@@ -73,7 +73,7 @@ messy-test:
|
||||
@echo "🧪 Running Python tests…"
|
||||
PYTHONPATH=. python -m unittest discover -s tests
|
||||
@echo "📑 Checking Ansible syntax…"
|
||||
ansible-playbook playbook.yml --syntax-check
|
||||
ansible-playbook -i localhost, -c local $(foreach f,$(wildcard group_vars/all/*.yml),-e @$(f)) playbook.yml --syntax-check
|
||||
|
||||
install: build
|
||||
@echo "⚙️ Install complete."
|
||||
|
||||
@@ -17,6 +17,7 @@ def run_ansible_playbook(
|
||||
password_file=None,
|
||||
verbose=0,
|
||||
skip_build=False,
|
||||
skip_tests=False,
|
||||
logs=False
|
||||
):
|
||||
start_time = datetime.datetime.now()
|
||||
@@ -56,9 +57,8 @@ def run_ansible_playbook(
|
||||
except subprocess.CalledProcessError:
|
||||
print("\n❌ Inventory validation failed. Deployment aborted.\n", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Tests are controlled via MODE_TEST
|
||||
if modes.get("MODE_TEST", False):
|
||||
|
||||
if not skip_tests:
|
||||
print("\n🧪 Running tests (make messy-test)...\n")
|
||||
subprocess.run(["make", "messy-test"], check=True)
|
||||
|
||||
@@ -255,6 +255,12 @@ def main():
|
||||
action="store_true",
|
||||
help="Skip running 'make build' before deployment.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--skip-tests",
|
||||
action="store_true",
|
||||
help="Skip running 'make messy-tests' before deployment.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--id",
|
||||
@@ -301,6 +307,7 @@ def main():
|
||||
password_file=args.password_file,
|
||||
verbose=args.verbose,
|
||||
skip_build=args.skip_build,
|
||||
skip_tests=args.skip_tests,
|
||||
logs=args.logs,
|
||||
)
|
||||
|
||||
|
||||
60
docker-compose.yml
Normal file
60
docker-compose.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
infinito:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
network: host
|
||||
pull_policy: never
|
||||
container_name: infinito_nexus
|
||||
image: infinito_nexus
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- data:/var/lib/docker/volumes/
|
||||
- backups:/Backups/
|
||||
- letsencrypt:/etc/letsencrypt/
|
||||
ports:
|
||||
# --- Mail services (classic + secure) ---
|
||||
- "${BIND_IP:-127.0.0.1}:25:25" # SMTP
|
||||
- "${BIND_IP:-127.0.0.1}:110:110" # POP3
|
||||
- "${BIND_IP:-127.0.0.1}:143:143" # IMAP
|
||||
- "${BIND_IP:-127.0.0.1}:465:465" # SMTPS
|
||||
- "${BIND_IP:-127.0.0.1}:587:587" # Submission (SMTP)
|
||||
- "${BIND_IP:-127.0.0.1}:993:993" # IMAPS (bound to public IP)
|
||||
- "${BIND_IP:-127.0.0.1}:995:995" # POP3S
|
||||
- "${BIND_IP:-127.0.0.1}:4190:4190" # Sieve (ManageSieve)
|
||||
|
||||
# --- Web / API services ---
|
||||
- "${BIND_IP:-127.0.0.1}:80:80" # HTTP
|
||||
- "${BIND_IP:-127.0.0.1}:443:443" # HTTPS
|
||||
- "${BIND_IP:-127.0.0.1}:8448:8448" # Matrix federation port
|
||||
|
||||
# --- TURN / STUN (UDP + TCP) ---
|
||||
- "${BIND_IP:-127.0.0.1}:3478-3480:3478-3480/udp" # TURN/STUN UDP
|
||||
- "${BIND_IP:-127.0.0.1}:3478-3480:3478-3480" # TURN/STUN TCP
|
||||
|
||||
# --- Streaming / RTMP ---
|
||||
- "${BIND_IP:-127.0.0.1}:1935:1935" # Peertube
|
||||
|
||||
# --- Custom / application ports ---
|
||||
- "${BIND_IP:-127.0.0.1}:2201:2201" # Gitea
|
||||
- "${BIND_IP:-127.0.0.1}:2202:2202" # Gitlab
|
||||
- "${BIND_IP:-127.0.0.1}:2203:22" # SSH
|
||||
- "${BIND_IP:-127.0.0.1}:33552:33552"
|
||||
|
||||
# --- Consecutive ranges ---
|
||||
- "${BIND_IP:-127.0.0.1}:48081-48083:48081-48083"
|
||||
- "${BIND_IP:-127.0.0.1}:48087:48087"
|
||||
volumes:
|
||||
data:
|
||||
backups:
|
||||
letsencrypt:
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: ${SUBNET:-172.30.0.0/24}
|
||||
gateway: ${GATEWAY:-172.30.0.1}
|
||||
3
env.sample
Normal file
3
env.sample
Normal file
@@ -0,0 +1,3 @@
|
||||
BIND_IP=127.0.0.1
|
||||
SUBNET=172.30.0.0/24
|
||||
GATEWAY=172.30.0.1
|
||||
79
filter_plugins/active_docker.py
Normal file
79
filter_plugins/active_docker.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Ansible filter to count active docker services for current host.
|
||||
|
||||
Active means:
|
||||
- application key is in group_names
|
||||
- application key matches prefix regex (default: ^(web-|svc-).* )
|
||||
- under applications[app]['docker']['services'] each service is counted if:
|
||||
- 'enabled' is True, OR
|
||||
- 'enabled' is missing/undefined (treated as active)
|
||||
|
||||
Returns an integer. If ensure_min_one=True, returns at least 1.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Any, Dict, Mapping, Iterable
|
||||
|
||||
|
||||
def _is_mapping(x: Any) -> bool:
|
||||
# be liberal: Mapping covers dict-like; fallback to dict check
|
||||
try:
|
||||
return isinstance(x, Mapping)
|
||||
except Exception:
|
||||
return isinstance(x, dict)
|
||||
|
||||
|
||||
def active_docker_container_count(applications: Mapping[str, Any],
|
||||
group_names: Iterable[str],
|
||||
prefix_regex: str = r'^(web-|svc-).*',
|
||||
ensure_min_one: bool = False) -> int:
|
||||
if not _is_mapping(applications):
|
||||
return 1 if ensure_min_one else 0
|
||||
|
||||
group_set = set(group_names or [])
|
||||
try:
|
||||
pattern = re.compile(prefix_regex)
|
||||
except re.error:
|
||||
pattern = re.compile(r'^(web-|svc-).*') # fallback
|
||||
|
||||
count = 0
|
||||
|
||||
for app_key, app_val in applications.items():
|
||||
# host selection + name prefix
|
||||
if app_key not in group_set:
|
||||
continue
|
||||
if not pattern.match(str(app_key)):
|
||||
continue
|
||||
|
||||
docker = app_val.get('docker') if _is_mapping(app_val) else None
|
||||
services = docker.get('services') if _is_mapping(docker) else None
|
||||
if not _is_mapping(services):
|
||||
# sometimes roles define a single service name string; ignore
|
||||
continue
|
||||
|
||||
for _svc_name, svc_cfg in services.items():
|
||||
if not _is_mapping(svc_cfg):
|
||||
# allow shorthand like: service: {} or image string -> counts as enabled
|
||||
count += 1
|
||||
continue
|
||||
enabled = svc_cfg.get('enabled', True)
|
||||
if isinstance(enabled, bool):
|
||||
if enabled:
|
||||
count += 1
|
||||
else:
|
||||
# non-bool enabled -> treat "truthy" as enabled
|
||||
if bool(enabled):
|
||||
count += 1
|
||||
|
||||
if ensure_min_one and count < 1:
|
||||
return 1
|
||||
return count
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
# usage: {{ applications | active_docker_container_count(group_names) }}
|
||||
'active_docker_container_count': active_docker_container_count,
|
||||
}
|
||||
@@ -158,26 +158,31 @@ class FilterModule(object):
|
||||
for directive in directives:
|
||||
tokens = ["'self'"]
|
||||
|
||||
# 1) Load flags (includes defaults from get_csp_flags)
|
||||
# Load flags (includes defaults from get_csp_flags)
|
||||
flags = self.get_csp_flags(applications, application_id, directive)
|
||||
tokens += flags
|
||||
|
||||
# 2) Allow fetching from internal CDN by default for selected directives
|
||||
# Allow fetching from internal CDN by default for selected directives
|
||||
if directive in ['script-src-elem', 'connect-src', 'style-src-elem']:
|
||||
tokens.append(get_url(domains, 'web-svc-cdn', web_protocol))
|
||||
|
||||
# 3) Matomo integration if feature is enabled
|
||||
# Matomo integration if feature is enabled
|
||||
if directive in ['script-src-elem', 'connect-src']:
|
||||
if self.is_feature_enabled(applications, matomo_feature_name, application_id):
|
||||
tokens.append(get_url(domains, 'web-app-matomo', web_protocol))
|
||||
|
||||
# 4) ReCaptcha integration (scripts + frames) if feature is enabled
|
||||
# Simpleicons integration if feature is enabled
|
||||
if directive in ['connect-src']:
|
||||
if self.is_feature_enabled(applications, 'simpleicons', application_id):
|
||||
tokens.append(get_url(domains, 'web-svc-simpleicons', web_protocol))
|
||||
|
||||
# ReCaptcha integration (scripts + frames) if feature is enabled
|
||||
if self.is_feature_enabled(applications, 'recaptcha', application_id):
|
||||
if directive in ['script-src-elem', 'frame-src']:
|
||||
tokens.append('https://www.gstatic.com')
|
||||
tokens.append('https://www.google.com')
|
||||
|
||||
# 5) Frame ancestors handling (desktop + logout support)
|
||||
# Frame ancestors handling (desktop + logout support)
|
||||
if directive == 'frame-ancestors':
|
||||
if self.is_feature_enabled(applications, 'desktop', application_id):
|
||||
# Allow being embedded by the desktop app domain (and potentially its parent)
|
||||
@@ -189,10 +194,10 @@ class FilterModule(object):
|
||||
tokens.append(get_url(domains, 'web-svc-logout', web_protocol))
|
||||
tokens.append(get_url(domains, 'web-app-keycloak', web_protocol))
|
||||
|
||||
# 6) Custom whitelist entries
|
||||
# Custom whitelist entries
|
||||
tokens += self.get_csp_whitelist(applications, application_id, directive)
|
||||
|
||||
# 7) Add inline content hashes ONLY if final tokens do NOT include 'unsafe-inline'
|
||||
# Add inline content hashes ONLY if final tokens do NOT include 'unsafe-inline'
|
||||
# (Check tokens, not flags, to include defaults and later modifications.)
|
||||
if "'unsafe-inline'" not in tokens:
|
||||
for snippet in self.get_csp_inline_content(applications, application_id, directive):
|
||||
@@ -201,7 +206,7 @@ class FilterModule(object):
|
||||
# Append directive
|
||||
parts.append(f"{directive} {' '.join(tokens)};")
|
||||
|
||||
# 8) Static img-src directive (kept permissive for data/blob and any host)
|
||||
# Static img-src directive (kept permissive for data/blob and any host)
|
||||
parts.append("img-src * data: blob:;")
|
||||
|
||||
return ' '.join(parts)
|
||||
|
||||
@@ -7,7 +7,7 @@ class FilterModule(object):
|
||||
def filters(self):
|
||||
return {'domain_mappings': self.domain_mappings}
|
||||
|
||||
def domain_mappings(self, apps, PRIMARY_DOMAIN):
|
||||
def domain_mappings(self, apps, primary_domain, auto_build_alias):
|
||||
"""
|
||||
Build a flat list of redirect mappings for all apps:
|
||||
- source: each alias domain
|
||||
@@ -43,7 +43,7 @@ class FilterModule(object):
|
||||
domains_cfg = cfg.get('server',{}).get('domains',{})
|
||||
entry = domains_cfg.get('canonical')
|
||||
if entry is None:
|
||||
canonical_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
|
||||
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
|
||||
elif isinstance(entry, dict):
|
||||
canonical_map[app_id] = list(entry.values())
|
||||
elif isinstance(entry, list):
|
||||
@@ -61,11 +61,11 @@ class FilterModule(object):
|
||||
alias_map[app_id] = []
|
||||
continue
|
||||
if isinstance(domains_cfg, dict) and not domains_cfg:
|
||||
alias_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
|
||||
alias_map[app_id] = [default_domain(app_id, primary_domain)]
|
||||
continue
|
||||
|
||||
aliases = parse_entry(domains_cfg, 'aliases', app_id) or []
|
||||
default = default_domain(app_id, PRIMARY_DOMAIN)
|
||||
default = default_domain(app_id, primary_domain)
|
||||
has_aliases = 'aliases' in domains_cfg
|
||||
has_canonical = 'canonical' in domains_cfg
|
||||
|
||||
@@ -74,7 +74,7 @@ class FilterModule(object):
|
||||
aliases.append(default)
|
||||
elif has_canonical:
|
||||
canon = canonical_map.get(app_id, [])
|
||||
if default not in canon and default not in aliases:
|
||||
if default not in canon and default not in aliases and auto_build_alias:
|
||||
aliases.append(default)
|
||||
|
||||
alias_map[app_id] = aliases
|
||||
@@ -84,7 +84,7 @@ class FilterModule(object):
|
||||
mappings = []
|
||||
for app_id, sources in alias_map.items():
|
||||
canon_list = canonical_map.get(app_id, [])
|
||||
target = canon_list[0] if canon_list else default_domain(app_id, PRIMARY_DOMAIN)
|
||||
target = canon_list[0] if canon_list else default_domain(app_id, primary_domain)
|
||||
for src in sources:
|
||||
if src == target:
|
||||
# skip self-redirects
|
||||
|
||||
@@ -4,7 +4,7 @@ class FilterModule(object):
|
||||
def filters(self):
|
||||
return {'generate_all_domains': self.generate_all_domains}
|
||||
|
||||
def generate_all_domains(self, domains_dict, include_www=True):
|
||||
def generate_all_domains(self, domains_dict, include_www:bool=True):
|
||||
"""
|
||||
Transform a dict of domains (values: str, list, dict) into a flat list,
|
||||
optionally add 'www.' prefixes, dedupe and sort alphabetically.
|
||||
|
||||
@@ -20,9 +20,10 @@ def get_docker_paths(application_id: str, path_docker_compose_instances: str) ->
|
||||
'config': f"{base}config/",
|
||||
},
|
||||
'files': {
|
||||
'env': f"{base}.env/env",
|
||||
'docker_compose': f"{base}docker-compose.yml",
|
||||
'dockerfile': f"{base}Dockerfile",
|
||||
'env': f"{base}.env/env",
|
||||
'docker_compose': f"{base}docker-compose.yml",
|
||||
'docker_compose_override': f"{base}docker-compose.override.yml",
|
||||
'dockerfile': f"{base}Dockerfile",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
77
filter_plugins/jvm_filters.py
Normal file
77
filter_plugins/jvm_filters.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys, os, re
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from module_utils.config_utils import get_app_conf
|
||||
from module_utils.entity_name_utils import get_entity_name
|
||||
|
||||
_UNIT_RE = re.compile(r'^\s*(\d+(?:\.\d+)?)\s*([kKmMgGtT]?[bB]?)?\s*$')
|
||||
_FACTORS = {
|
||||
'': 1, 'b': 1,
|
||||
'k': 1024, 'kb': 1024,
|
||||
'm': 1024**2, 'mb': 1024**2,
|
||||
'g': 1024**3, 'gb': 1024**3,
|
||||
't': 1024**4, 'tb': 1024**4,
|
||||
}
|
||||
|
||||
def _to_bytes(v: str) -> int:
|
||||
if v is None:
|
||||
raise AnsibleFilterError("jvm_filters: size value is None")
|
||||
s = str(v).strip()
|
||||
m = _UNIT_RE.match(s)
|
||||
if not m:
|
||||
raise AnsibleFilterError(f"jvm_filters: invalid size '{v}'")
|
||||
num, unit = m.group(1), (m.group(2) or '').lower()
|
||||
try:
|
||||
val = float(num)
|
||||
except ValueError as e:
|
||||
raise AnsibleFilterError(f"jvm_filters: invalid numeric size '{v}'") from e
|
||||
factor = _FACTORS.get(unit)
|
||||
if factor is None:
|
||||
raise AnsibleFilterError(f"jvm_filters: unknown unit in '{v}'")
|
||||
return int(val * factor)
|
||||
|
||||
def _to_mb(v: str) -> int:
|
||||
return max(0, _to_bytes(v) // (1024 * 1024))
|
||||
|
||||
def _svc(app_id: str) -> str:
|
||||
return get_entity_name(app_id)
|
||||
|
||||
def _mem_limit_mb(apps: dict, app_id: str) -> int:
|
||||
svc = _svc(app_id)
|
||||
raw = get_app_conf(apps, app_id, f"docker.services.{svc}.mem_limit")
|
||||
mb = _to_mb(raw)
|
||||
if mb <= 0:
|
||||
raise AnsibleFilterError(f"jvm_filters: mem_limit for '{svc}' must be > 0 MB (got '{raw}')")
|
||||
return mb
|
||||
|
||||
def _mem_res_mb(apps: dict, app_id: str) -> int:
|
||||
svc = _svc(app_id)
|
||||
raw = get_app_conf(apps, app_id, f"docker.services.{svc}.mem_reservation")
|
||||
mb = _to_mb(raw)
|
||||
if mb <= 0:
|
||||
raise AnsibleFilterError(f"jvm_filters: mem_reservation for '{svc}' must be > 0 MB (got '{raw}')")
|
||||
return mb
|
||||
|
||||
def jvm_max_mb(apps: dict, app_id: str) -> int:
|
||||
"""Xmx = min( floor(0.7*limit), limit-1024, 12288 ) with floor at 1024 MB."""
|
||||
limit_mb = _mem_limit_mb(apps, app_id)
|
||||
c1 = (limit_mb * 7) // 10
|
||||
c2 = max(0, limit_mb - 1024)
|
||||
c3 = 12288
|
||||
return max(1024, min(c1, c2, c3))
|
||||
|
||||
def jvm_min_mb(apps: dict, app_id: str) -> int:
|
||||
"""Xms = min( floor(Xmx/2), mem_reservation, Xmx ) with floor at 512 MB."""
|
||||
xmx = jvm_max_mb(apps, app_id)
|
||||
res = _mem_res_mb(apps, app_id)
|
||||
return max(512, min(xmx // 2, res, xmx))
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
"jvm_max_mb": jvm_max_mb,
|
||||
"jvm_min_mb": jvm_min_mb,
|
||||
}
|
||||
40
filter_plugins/resource_filter.py
Normal file
40
filter_plugins/resource_filter.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# filter_plugins/resource_filter.py
|
||||
from __future__ import annotations
|
||||
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from module_utils.config_utils import get_app_conf, AppConfigKeyError, ConfigEntryNotSetError # noqa: F401
|
||||
from module_utils.entity_name_utils import get_entity_name
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
|
||||
|
||||
def resource_filter(
|
||||
applications: dict,
|
||||
application_id: str,
|
||||
key: str,
|
||||
service_name: str,
|
||||
hard_default,
|
||||
):
|
||||
"""
|
||||
Lookup order:
|
||||
1) docker.services.<service_name or get_entity_name(application_id)>.<key>
|
||||
2) hard_default (mandatory)
|
||||
|
||||
- service_name may be "" → will resolve to get_entity_name(application_id).
|
||||
- hard_default is mandatory (no implicit None).
|
||||
- required=False always.
|
||||
"""
|
||||
try:
|
||||
primary_service = service_name if service_name != "" else get_entity_name(application_id)
|
||||
return get_app_conf(applications, application_id, f"docker.services.{primary_service}.{key}", False, hard_default)
|
||||
except (AppConfigKeyError, ConfigEntryNotSetError) as e:
|
||||
raise AnsibleFilterError(str(e))
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
"resource_filter": resource_filter,
|
||||
}
|
||||
@@ -32,8 +32,10 @@ WEBSOCKET_PROTOCOL: "{{ 'wss' if WEB_PROTOCOL == 'https' else 'ws' }}"
|
||||
# WWW-Redirect to None WWW-Domains enabled
|
||||
WWW_REDIRECT_ENABLED: "{{ ('web-opt-rdr-www' in group_names) | bool }}"
|
||||
|
||||
AUTO_BUILD_ALIASES: False # If enabled it creates an alias domain for each web application by the entity name, recommended to set to false to safge domain space
|
||||
|
||||
# Domain
|
||||
PRIMARY_DOMAIN: "localhost" # Primary Domain of the server
|
||||
PRIMARY_DOMAIN: "localhost" # Primary Domain of the server
|
||||
|
||||
DNS_PROVIDER: cloudflare # The DNS Provider\Registrar for the domain
|
||||
|
||||
@@ -58,7 +60,7 @@ DOCKER_WHITELISTET_ANON_VOLUMES: []
|
||||
|
||||
# Asyn Confitguration
|
||||
ASYNC_ENABLED: "{{ not MODE_DEBUG | bool }}" # Activate async, deactivated for debugging
|
||||
ASYNC_TIME: "{{ 300 if ASYNC_ENABLED | bool else omit }}" # Run for mnax 5min
|
||||
ASYNC_TIME: "{{ 300 if ASYNC_ENABLED | bool else omit }}" # Run for max 5min
|
||||
ASYNC_POLL: "{{ 0 if ASYNC_ENABLED | bool else 10 }}" # Don't wait for task
|
||||
|
||||
# default value if not set via CLI (-e) or in playbook vars
|
||||
@@ -84,4 +86,4 @@ _applications_nextcloud_oidc_flavor: >-
|
||||
RBAC:
|
||||
GROUP:
|
||||
NAME: "/roles" # Name of the group which holds the RBAC roles
|
||||
CLAIM: "groups" # Name of the claim containing the RBAC groups
|
||||
CLAIM: "groups" # Name of the claim containing the RBAC groups
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Mode
|
||||
|
||||
# The following modes can be combined with each other
|
||||
MODE_TEST: false # Executes test routines instead of productive routines
|
||||
MODE_DUMMY: false # Executes dummy/test routines instead of productive routines
|
||||
MODE_UPDATE: true # Executes updates
|
||||
MODE_DEBUG: false # This enables debugging in ansible and in the apps, You SHOULD NOT enable this on production servers
|
||||
MODE_RESET: false # Cleans up all Infinito.Nexus files. It's necessary to run to whole playbook and not particial roles when using this function.
|
||||
MODE_CLEANUP: "{{ MODE_DEBUG | bool }}" # Cleanup unused files and configurations
|
||||
MODE_ASSERT: "{{ MODE_DEBUG | bool }}" # Executes validation tasks during the run.
|
||||
MODE_BACKUP: true # Executes the Backup before the deployment
|
||||
|
||||
@@ -29,4 +29,31 @@ NGINX:
|
||||
IMAGE: "/tmp/cache_nginx_image/" # Directory which nginx uses to cache images
|
||||
USER: "http" # Default nginx user in ArchLinux
|
||||
|
||||
# Effective CPUs (float) across proxy and the current app
|
||||
WEBSERVER_CPUS_EFFECTIVE: >-
|
||||
{{
|
||||
[
|
||||
(applications | resource_filter('svc-prx-openresty', 'cpus', service_name | default(''), RESOURCE_CPUS)) | float,
|
||||
(applications | resource_filter(application_id, 'cpus', service_name | default(''), RESOURCE_CPUS)) | float
|
||||
] | min
|
||||
}}
|
||||
|
||||
# Nginx requires an integer for worker_processes:
|
||||
# - if cpus < 1 → 1
|
||||
# - else → floor to int
|
||||
WEBSERVER_WORKER_PROCESSES: >-
|
||||
{{
|
||||
1 if (WEBSERVER_CPUS_EFFECTIVE | float) < 1
|
||||
else (WEBSERVER_CPUS_EFFECTIVE | float | int)
|
||||
}}
|
||||
|
||||
# worker_connections from pids_limit (use the smaller one), with correct key/defaults
|
||||
WEBSERVER_WORKER_CONNECTIONS: >-
|
||||
{{
|
||||
[
|
||||
(applications | resource_filter('svc-prx-openresty', 'pids_limit', service_name | default(''), RESOURCE_PIDS_LIMIT)) | int,
|
||||
(applications | resource_filter(application_id, 'pids_limit', service_name | default(''), RESOURCE_PIDS_LIMIT)) | int
|
||||
] | min
|
||||
}}
|
||||
|
||||
# @todo It propably makes sense to distinguish between target and source mount path, so that the config files can be stored in the openresty volumes folder
|
||||
@@ -6,12 +6,12 @@ SYS_TIMER_ALL_ENABLED: "{{ MODE_DEBUG }}" # Runtime Var
|
||||
|
||||
## Server Tact Variables
|
||||
|
||||
HOURS_SERVER_AWAKE: "0..23" # Ours in which the server is "awake" (100% working). Rest of the time is reserved for maintanance
|
||||
HOURS_SERVER_AWAKE: "6..23" # Ours in which the server is "awake" (100% working). Rest of the time is reserved for maintanance
|
||||
RANDOMIZED_DELAY_SEC: "5min" # Random delay for systemd timers to avoid peak loads.
|
||||
|
||||
## Timeouts for all services
|
||||
SYS_TIMEOUT_DOCKER_RPR_HARD: "10min"
|
||||
SYS_TIMEOUT_DOCKER_RPR_SOFT: "{{ SYS_TIMEOUT_DOCKER_RPR_HARD }}"
|
||||
SYS_TIMEOUT_DOCKER_RPR_SOFT: "{{ SYS_TIMEOUT_DOCKER_RPR_HARD }}"
|
||||
SYS_TIMEOUT_CLEANUP_SERVICES: "15min"
|
||||
SYS_TIMEOUT_DOCKER_UPDATE: "20min"
|
||||
SYS_TIMEOUT_STORAGE_OPTIMIZER: "{{ SYS_TIMEOUT_DOCKER_UPDATE }}"
|
||||
|
||||
@@ -104,6 +104,14 @@ defaults_networks:
|
||||
subnet: 192.168.103.224/28
|
||||
web-app-xwiki:
|
||||
subnet: 192.168.103.240/28
|
||||
web-app-openwebui:
|
||||
subnet: 192.168.104.0/28
|
||||
web-app-flowise:
|
||||
subnet: 192.168.104.16/28
|
||||
web-app-minio:
|
||||
subnet: 192.168.104.32/28
|
||||
web-svc-coturn:
|
||||
subnet: 192.168.104.48/28
|
||||
|
||||
# /24 Networks / 254 Usable Clients
|
||||
web-app-bigbluebutton:
|
||||
@@ -116,3 +124,5 @@ defaults_networks:
|
||||
subnet: 192.168.201.0/24
|
||||
svc-db-openldap:
|
||||
subnet: 192.168.202.0/24
|
||||
svc-ai-ollama:
|
||||
subnet: 192.168.203.0/24 # Big network to bridge applications into ai
|
||||
|
||||
@@ -75,21 +75,34 @@ ports:
|
||||
web-app-bluesky_view: 8051
|
||||
web-app-magento: 8052
|
||||
web-app-bridgy-fed: 8053
|
||||
web-app-xwiki: 8054
|
||||
web-app-xwiki: 8054
|
||||
web-app-openwebui: 8055
|
||||
web-app-flowise: 8056
|
||||
web-app-minio_api: 8057
|
||||
web-app-minio_console: 8058
|
||||
web-app-bigbluebutton: 48087 # This port is predefined by bbb. @todo Try to change this to a 8XXX port
|
||||
public:
|
||||
# The following ports should be changed to 22 on the subdomain via stream mapping
|
||||
ssh:
|
||||
web-app-gitea: 2201
|
||||
web-app-gitlab: 2202
|
||||
web-app-gitea: 2201
|
||||
web-app-gitlab: 2202
|
||||
ldaps:
|
||||
svc-db-openldap: 636
|
||||
stun:
|
||||
web-app-bigbluebutton: 3478 # Not sure if it's right placed here or if it should be moved to localhost section
|
||||
# Occupied by BBB: 3479
|
||||
web-app-nextcloud: 3480
|
||||
turn:
|
||||
web-app-bigbluebutton: 5349 # Not sure if it's right placed here or if it should be moved to localhost section
|
||||
web-app-nextcloud: 5350 # Not used yet
|
||||
svc-db-openldap: 636
|
||||
stun_turn:
|
||||
web-app-bigbluebutton: 3478 # Not sure if it's right placed here or if it should be moved to localhost section
|
||||
# Occupied by BBB: 3479
|
||||
web-app-nextcloud: 3480
|
||||
web-svc-coturn: 3481
|
||||
stun_turn_tls:
|
||||
web-app-bigbluebutton: 5349 # Not sure if it's right placed here or if it should be moved to localhost section
|
||||
web-app-nextcloud: 5350 # Not used yet
|
||||
web-svc-coturn: 5351
|
||||
federation:
|
||||
web-app-matrix_synapse: 8448
|
||||
relay_port_ranges:
|
||||
web-svc-coturn_start: 20000
|
||||
web-svc-coturn_end: 39999
|
||||
web-app-bigbluebutton_start: 40000
|
||||
web-app-bigbluebutton_end: 49999
|
||||
web-app-nextcloud_start: 50000
|
||||
web-app-nextcloud_end: 59999
|
||||
|
||||
3
group_vars/all/17_ai.yml
Normal file
3
group_vars/all/17_ai.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
# URL of Local Ollama Container
|
||||
OLLAMA_BASE_LOCAL_URL: "http://{{ applications | get_app_conf('svc-ai-ollama', 'docker.services.ollama.name') }}:{{ applications | get_app_conf('svc-ai-ollama', 'docker.services.ollama.port') }}"
|
||||
OLLAMA_LOCAL_ENABLED: "{{ applications | get_app_conf(application_id, 'features.local_ai') }}"
|
||||
47
group_vars/all/18_resource.yml
Normal file
47
group_vars/all/18_resource.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
# Host resources
|
||||
RESOURCE_HOST_CPUS: "{{ ansible_processor_vcpus | int }}"
|
||||
RESOURCE_HOST_MEM: "{{ (ansible_memtotal_mb | int) // 1024 }}"
|
||||
|
||||
# Reserve for OS
|
||||
RESOURCE_HOST_RESERVE_CPU: 2
|
||||
RESOURCE_HOST_RESERVE_MEM: 4
|
||||
|
||||
# Available for apps
|
||||
RESOURCE_AVAIL_CPUS: "{{ (RESOURCE_HOST_CPUS | int) - (RESOURCE_HOST_RESERVE_CPU | int) }}"
|
||||
RESOURCE_AVAIL_MEM: "{{ (RESOURCE_HOST_MEM | int) - (RESOURCE_HOST_RESERVE_MEM | int) }}"
|
||||
|
||||
# Count active docker services (only roles starting with web- or svc-; service counts if enabled==true OR enabled is undefined)
|
||||
RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT: >-
|
||||
{{
|
||||
applications
|
||||
| active_docker_container_count(group_names, '^(web-|svc-).*', ensure_min_one=True)
|
||||
}}
|
||||
|
||||
# Per-container fair share (numbers!), later we append 'g' only for the string fields in compose
|
||||
RESOURCE_CPUS_NUM: >-
|
||||
{{
|
||||
[
|
||||
(
|
||||
((RESOURCE_AVAIL_CPUS | float) / (RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT | float))
|
||||
| round(2)
|
||||
),
|
||||
0.5
|
||||
] | max
|
||||
}}
|
||||
|
||||
RESOURCE_MEM_RESERVATION_NUM: >-
|
||||
{{
|
||||
(((RESOURCE_AVAIL_MEM | float) / (RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT | float)) * 0.7)
|
||||
| round(1)
|
||||
}}
|
||||
RESOURCE_MEM_LIMIT_NUM: >-
|
||||
{{
|
||||
(((RESOURCE_AVAIL_MEM | float) / (RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT | float)) * 1.0)
|
||||
| round(1)
|
||||
}}
|
||||
|
||||
# Final strings with units for compose defaults (keep numbers above for math elsewhere if needed)
|
||||
RESOURCE_CPUS: "{{ RESOURCE_CPUS_NUM }}"
|
||||
RESOURCE_MEM_RESERVATION: "{{ RESOURCE_MEM_RESERVATION_NUM }}g"
|
||||
RESOURCE_MEM_LIMIT: "{{ RESOURCE_MEM_LIMIT_NUM }}g"
|
||||
RESOURCE_PIDS_LIMIT: 512
|
||||
@@ -142,7 +142,8 @@ class InventoryManager:
|
||||
"""
|
||||
if algorithm == "random_hex":
|
||||
return secrets.token_hex(64)
|
||||
|
||||
if algorithm == "random_hex_32":
|
||||
return secrets.token_hex(32)
|
||||
if algorithm == "sha256":
|
||||
return hashlib.sha256(secrets.token_bytes(32)).hexdigest()
|
||||
if algorithm == "sha1":
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
- name: Execute {{ SOFTWARE_NAME }} Play
|
||||
- name: "Execute {{ SOFTWARE_NAME }} Play"
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: "Load 'constructor' tasks"
|
||||
include_tasks: "tasks/stages/01_constructor.yml"
|
||||
- name: "Load '{{host_type}}' tasks"
|
||||
include_tasks: "tasks/stages/02_{{host_type}}.yml"
|
||||
- name: "Load '{{ host_type }}' tasks"
|
||||
include_tasks: "tasks/stages/02_{{ host_type }}.yml"
|
||||
- name: "Load 'destructor' tasks"
|
||||
include_tasks: "tasks/stages/03_destructor.yml"
|
||||
become: true
|
||||
@@ -148,6 +148,11 @@ roles:
|
||||
description: "Network setup (DNS, Let's Encrypt HTTP, WireGuard, etc.)"
|
||||
icon: "fas fa-globe"
|
||||
invokable: true
|
||||
ai:
|
||||
title: "AI Services"
|
||||
description: "Core AI building blocks—model serving, OpenAI-compatible gateways, vector databases, orchestration, and chat UIs."
|
||||
icon: "fas fa-brain"
|
||||
invokable: true
|
||||
user:
|
||||
title: "Users & Access"
|
||||
description: "User accounts & access control"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
- name: Link homefolders to cloud
|
||||
ansible.builtin.file:
|
||||
src: "{{nextcloud_cloud_directory}}{{item}}"
|
||||
dest: "{{nextcloud_user_home_directory}}{{item}}"
|
||||
src: "{{nextcloud_cloud_directory}}{{ item }}"
|
||||
dest: "{{nextcloud_user_home_directory}}{{ item }}"
|
||||
owner: "{{ users[desktop_username].username }}"
|
||||
group: "{{ users[desktop_username].username }}"
|
||||
state: link
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
- name: Setup locale.gen
|
||||
template:
|
||||
template:
|
||||
src: locale.gen.j2
|
||||
dest: /etc/locale.gen
|
||||
|
||||
- name: Setup locale.conf
|
||||
template:
|
||||
template:
|
||||
src: locale.conf.j2
|
||||
dest: /etc/locale.conf
|
||||
|
||||
|
||||
4
roles/dev-yay/defaults/main.yml
Normal file
4
roles/dev-yay/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
AUR_HELPER: yay
|
||||
AUR_BUILDER_USER: aur_builder
|
||||
AUR_BUILDER_GROUP: wheel
|
||||
AUR_BUILDER_SUDOERS_PATH: /etc/sudoers.d/11-install-aur_builder
|
||||
@@ -6,42 +6,53 @@
|
||||
- dev-git
|
||||
- dev-base-devel
|
||||
|
||||
- name: install yay
|
||||
- name: Install yay build prerequisites
|
||||
community.general.pacman:
|
||||
name:
|
||||
- base-devel
|
||||
- patch
|
||||
state: present
|
||||
|
||||
- name: Create the `aur_builder` user
|
||||
- name: Create the AUR builder user
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: aur_builder
|
||||
name: "{{ AUR_BUILDER_USER }}"
|
||||
create_home: yes
|
||||
group: wheel
|
||||
group: "{{ AUR_BUILDER_GROUP }}"
|
||||
|
||||
- name: Allow the `aur_builder` user to run `sudo pacman` without a password
|
||||
- name: Allow AUR builder to run pacman without password
|
||||
become: true
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/sudoers.d/11-install-aur_builder
|
||||
line: 'aur_builder ALL=(ALL) NOPASSWD: /usr/bin/pacman'
|
||||
path: "{{ AUR_BUILDER_SUDOERS_PATH }}"
|
||||
line: '{{ AUR_BUILDER_USER }} ALL=(ALL) NOPASSWD: /usr/bin/pacman'
|
||||
create: yes
|
||||
validate: 'visudo -cf %s'
|
||||
|
||||
- name: Clone yay from AUR
|
||||
become: true
|
||||
become_user: aur_builder
|
||||
become_user: "{{ AUR_BUILDER_USER }}"
|
||||
git:
|
||||
repo: https://aur.archlinux.org/yay.git
|
||||
dest: /home/aur_builder/yay
|
||||
dest: "/home/{{ AUR_BUILDER_USER }}/yay"
|
||||
clone: yes
|
||||
update: yes
|
||||
|
||||
- name: Build and install yay
|
||||
become: true
|
||||
become_user: aur_builder
|
||||
become_user: "{{ AUR_BUILDER_USER }}"
|
||||
shell: |
|
||||
cd /home/aur_builder/yay
|
||||
cd /home/{{ AUR_BUILDER_USER }}/yay
|
||||
makepkg -si --noconfirm
|
||||
args:
|
||||
creates: /usr/bin/yay
|
||||
|
||||
- name: upgrade the system using yay, only act on AUR packages.
|
||||
become: true
|
||||
become_user: "{{ AUR_BUILDER_USER }}"
|
||||
kewlfft.aur.aur:
|
||||
upgrade: yes
|
||||
use: "{{ AUR_HELPER }}"
|
||||
aur_only: yes
|
||||
when: MODE_UPDATE | bool
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
- block:
|
||||
- include_tasks: 01_core.yml
|
||||
- set_fact:
|
||||
run_once_dev_yay: true
|
||||
when: run_once_dev_yay is not defined
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
docker_compose_skipp_file_creation: false # If set to true the file creation will be skipped
|
||||
docker_pull_git_repository: false # Activates docker repository download and routine
|
||||
docker_compose_flush_handlers: false # Set to true in the vars/main.yml of the including role to autoflush after docker compose routine
|
||||
docker_compose_file_creation_enabled: true # If set to true the file creation will be skipped
|
||||
docker_pull_git_repository: false # Activates docker repository download and routine
|
||||
docker_compose_flush_handlers: false # Set to true in the vars/main.yml of the including role to autoflush after docker compose routine
|
||||
@@ -9,7 +9,6 @@
|
||||
listen:
|
||||
- docker compose up
|
||||
- docker compose restart
|
||||
- docker compose just up
|
||||
when: MODE_ASSERT | bool
|
||||
|
||||
- name: docker compose pull
|
||||
@@ -41,9 +40,8 @@
|
||||
listen:
|
||||
- docker compose up
|
||||
- docker compose restart
|
||||
- docker compose just up
|
||||
|
||||
- name: Build docker compose
|
||||
- name: Build docker compose
|
||||
shell: |
|
||||
set -euo pipefail
|
||||
docker compose build || {
|
||||
@@ -77,7 +75,6 @@
|
||||
DOCKER_CLIENT_TIMEOUT: 600
|
||||
listen:
|
||||
- docker compose up
|
||||
- docker compose just up # @todo replace later just up by up when code is refactored, build atm is also listening to up
|
||||
|
||||
- name: docker compose restart
|
||||
command:
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
- name: Set default docker_repository_path
|
||||
set_fact:
|
||||
docker_repository_path: "{{docker_compose.directories.services}}repository/"
|
||||
docker_repository_path: "{{ [ docker_compose.directories.services, 'repository/' ] | path_join }}"
|
||||
|
||||
- name: pull docker repository
|
||||
git:
|
||||
repo: "{{ docker_repository_address }}"
|
||||
dest: "{{ docker_repository_path }}"
|
||||
version: "{{ docker_repository_branch | default('main') }}"
|
||||
depth: 1
|
||||
update: yes
|
||||
recursive: yes
|
||||
repo: "{{ docker_repository_address }}"
|
||||
dest: "{{ docker_repository_path }}"
|
||||
version: "{{ docker_repository_branch | default('main') }}"
|
||||
single_branch: yes
|
||||
depth: 1
|
||||
update: yes
|
||||
recursive: yes
|
||||
force: yes
|
||||
accept_hostkey: yes
|
||||
notify:
|
||||
- docker compose build
|
||||
- docker compose up
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
- "{{ application_id | abs_role_path_by_application_id }}/templates/Dockerfile.j2"
|
||||
- "{{ application_id | abs_role_path_by_application_id }}/files/Dockerfile"
|
||||
notify:
|
||||
- docker compose up
|
||||
- docker compose build
|
||||
- docker compose up
|
||||
register: create_dockerfile_result
|
||||
failed_when:
|
||||
- create_dockerfile_result is failed
|
||||
@@ -28,6 +28,21 @@
|
||||
- env_template is failed
|
||||
- "'Could not find or access' not in env_template.msg"
|
||||
|
||||
- name: "Create (optional) '{{ docker_compose.files.docker_compose_override }}'"
|
||||
template:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ docker_compose.files.docker_compose_override }}"
|
||||
mode: '770'
|
||||
force: yes
|
||||
notify: docker compose up
|
||||
register: docker_compose_override_template
|
||||
loop:
|
||||
- "{{ application_id | abs_role_path_by_application_id }}/templates/docker-compose.override.yml.j2"
|
||||
- "{{ application_id | abs_role_path_by_application_id }}/files/docker-compose.override.yml"
|
||||
failed_when:
|
||||
- docker_compose_override_template is failed
|
||||
- "'Could not find or access' not in docker_compose_override_template.msg"
|
||||
|
||||
- name: "Create (obligatoric) '{{ docker_compose.files.docker_compose }}'"
|
||||
template:
|
||||
src: "docker-compose.yml.j2"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
include_tasks: "04_files.yml"
|
||||
- name: "Ensure that {{ docker_compose.directories.instance }} is up"
|
||||
include_tasks: "05_ensure_up.yml"
|
||||
when: not docker_compose_skipp_file_creation | bool
|
||||
when: docker_compose_file_creation_enabled | bool
|
||||
|
||||
- name: "flush docker compose for '{{ application_id }}'"
|
||||
meta: flush_handlers
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{# This template needs to be included in docker-compose.yml #}
|
||||
networks:
|
||||
{# Central RDMS-Database Network #}
|
||||
{% if
|
||||
(applications | get_app_conf(application_id, 'features.central_database', False) and database_type is defined) or
|
||||
application_id in ['svc-db-mariadb','svc-db-postgres']
|
||||
@@ -7,6 +8,7 @@ networks:
|
||||
{{ applications | get_app_conf('svc-db-' ~ database_type, 'docker.network') }}:
|
||||
external: true
|
||||
{% endif %}
|
||||
{# Central LDAP Network #}
|
||||
{% if
|
||||
applications | get_app_conf(application_id, 'features.ldap', False) and
|
||||
applications | get_app_conf('svc-db-openldap', 'network.docker', False)
|
||||
@@ -14,7 +16,13 @@ networks:
|
||||
{{ applications | get_app_conf('svc-db-openldap', 'docker.network') }}:
|
||||
external: true
|
||||
{% endif %}
|
||||
{% if not application_id.startswith('svc-db-') %}
|
||||
{# Central AI Network #}
|
||||
{% if applications | get_app_conf(application_id, 'features.local_ai', False) %}
|
||||
{{ applications | get_app_conf('svc-ai-ollama', 'docker.network') }}:
|
||||
external: true
|
||||
{% endif %}
|
||||
{# Default Network #}
|
||||
{% if not application_id.startswith('svc-db-') and not application_id.startswith('svc-ai-') %}
|
||||
default:
|
||||
{% if
|
||||
application_id in networks.local and
|
||||
@@ -25,7 +33,7 @@ networks:
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: {{networks.local[application_id].subnet}}
|
||||
- subnet: {{ networks.local[application_id].subnet }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ "\n" }}
|
||||
@@ -1,4 +1,6 @@
|
||||
users:
|
||||
blackhole:
|
||||
description: "Everything what will be send to this user will disapear"
|
||||
username: "blackhole"
|
||||
username: "blackhole"
|
||||
roles:
|
||||
- mail-bot
|
||||
@@ -1,2 +1,4 @@
|
||||
# @See https://chatgpt.com/share/67a23d18-fb54-800f-983c-d6d00752b0b4
|
||||
docker_compose: "{{ application_id | get_docker_paths(PATH_DOCKER_COMPOSE_INSTANCES) }}"
|
||||
docker_compose: "{{ application_id | get_docker_paths(PATH_DOCKER_COMPOSE_INSTANCES) }}"
|
||||
docker_compose_command_base: "docker compose --env-file {{ docker_compose.files.env }}"
|
||||
docker_compose_command_exec: "{{ docker_compose_command_base }} exec"
|
||||
@@ -1,11 +1,13 @@
|
||||
{# Base for docker services #}
|
||||
|
||||
restart: {{ DOCKER_RESTART_POLICY }}
|
||||
restart: {{ docker_restart_policy | default(DOCKER_RESTART_POLICY) }}
|
||||
{% if application_id | has_env %}
|
||||
env_file:
|
||||
- "{{ docker_compose.files.env }}"
|
||||
{% endif %}
|
||||
logging:
|
||||
driver: journald
|
||||
|
||||
{% filter indent(4) %}
|
||||
{% include 'roles/docker-container/templates/resource.yml.j2' %}
|
||||
{% endfilter %}
|
||||
{{ "\n" }}
|
||||
6
roles/docker-container/templates/build.yml.j2
Normal file
6
roles/docker-container/templates/build.yml.j2
Normal file
@@ -0,0 +1,6 @@
|
||||
{# integrate it into service sections to be build by Dockerfile #}
|
||||
pull_policy: never
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
{# pass Arguments here #}
|
||||
@@ -1,15 +1,25 @@
|
||||
{# This template needs to be included in docker-compose.yml containers #}
|
||||
networks:
|
||||
{# Central RDMS-Database Network #}
|
||||
{% if
|
||||
(applications | get_app_conf(application_id, 'features.central_database', False) and database_type is defined) or
|
||||
application_id in ['svc-db-mariadb','svc-db-postgres']
|
||||
%}
|
||||
{{ applications | get_app_conf('svc-db-' ~ database_type, 'docker.network') }}:
|
||||
{% if application_id in ['svc-db-mariadb','svc-db-postgres'] %}
|
||||
aliases:
|
||||
- {{ database_type }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{# Central LDAP Network #}
|
||||
{% if applications | get_app_conf(application_id, 'features.ldap', False) and applications | get_app_conf('svc-db-openldap', 'network.docker') %}
|
||||
{{ applications | get_app_conf('svc-db-openldap', 'docker.network') }}:
|
||||
{% endif %}
|
||||
{% if application_id != 'svc-db-openldap' %}
|
||||
{# Central AI Network #}
|
||||
{% if applications | get_app_conf(application_id, 'features.local_ai', False) %}
|
||||
{{ applications | get_app_conf('svc-ai-ollama', 'docker.network') }}:
|
||||
{% endif %}
|
||||
{% if not application_id.startswith('svc-db-') and not application_id.startswith('svc-ai-') %}
|
||||
default:
|
||||
{% endif %}
|
||||
{{ "\n" }}
|
||||
4
roles/docker-container/templates/resource.yml.j2
Normal file
4
roles/docker-container/templates/resource.yml.j2
Normal file
@@ -0,0 +1,4 @@
|
||||
cpus: {{ applications | resource_filter(application_id, 'cpus', service_name | default(''), RESOURCE_CPUS) }}
|
||||
mem_reservation: {{ applications | resource_filter(application_id, 'mem_reservation', service_name | default(''), RESOURCE_MEM_RESERVATION) }}
|
||||
mem_limit: {{ applications | resource_filter(application_id, 'mem_limit', service_name | default(''), RESOURCE_MEM_LIMIT) }}
|
||||
pids_limit: {{ applications | resource_filter(application_id, 'pids_limit', service_name | default(''), RESOURCE_PIDS_LIMIT) }}
|
||||
@@ -4,7 +4,7 @@
|
||||
run_once_pkgmgr_install: true
|
||||
when: run_once_pkgmgr_install is not defined
|
||||
|
||||
- name: update {{ package_name }}
|
||||
- name: "update {{ package_name }}"
|
||||
ansible.builtin.shell: |
|
||||
source ~/.venvs/pkgmgr/bin/activate
|
||||
pkgmgr update {{ package_name }} --dependencies --clone-mode https
|
||||
|
||||
23
roles/svc-ai-ollama/README.md
Normal file
23
roles/svc-ai-ollama/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
# Ollama
|
||||
|
||||
## Description
|
||||
|
||||
**Ollama** is a local model server that runs open LLMs on your hardware and exposes a simple HTTP API. It’s the backbone for privacy-first AI: prompts and data stay on your machines.
|
||||
|
||||
## Overview
|
||||
|
||||
After the first model pull, Ollama serves models to clients like Open WebUI (for chat) and Flowise (for workflows). Models are cached locally for quick reuse and can run fully offline when required.
|
||||
|
||||
## Features
|
||||
|
||||
* Run popular open models (chat, code, embeddings) locally
|
||||
* Simple, predictable HTTP API for developers
|
||||
* Local caching to avoid repeated downloads
|
||||
* Works seamlessly with Open WebUI and Flowise
|
||||
* Offline-capable for air-gapped deployments
|
||||
|
||||
## Further Resources
|
||||
|
||||
* Ollama — [https://ollama.com](https://ollama.com)
|
||||
* Ollama Model Library — [https://ollama.com/library](https://ollama.com/library)
|
||||
22
roles/svc-ai-ollama/config/main.yml
Normal file
22
roles/svc-ai-ollama/config/main.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
features:
|
||||
local_ai: true # Needs to be set so that network is loaded
|
||||
docker:
|
||||
services:
|
||||
ollama:
|
||||
backup:
|
||||
no_stop_required: true
|
||||
image: ollama/ollama
|
||||
version: latest
|
||||
name: ollama
|
||||
port: 11434
|
||||
cpus: "4.0"
|
||||
mem_reservation: "6g"
|
||||
mem_limit: "8g"
|
||||
pids_limit: 2048
|
||||
volumes:
|
||||
models: "ollama_models"
|
||||
network: "ollama"
|
||||
preload_models:
|
||||
- "llama3:latest"
|
||||
- "mistral:latest"
|
||||
- "nomic-embed-text:latest"
|
||||
25
roles/svc-ai-ollama/meta/main.yml
Normal file
25
roles/svc-ai-ollama/meta/main.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "Kevin Veen-Birkenbach"
|
||||
description: "Installs Ollama — a local model server for running open LLMs with a simple HTTP API."
|
||||
license: "Infinito.Nexus NonCommercial License"
|
||||
license_url: "https://s.infinito.nexus/license"
|
||||
company: |
|
||||
Kevin Veen-Birkenbach
|
||||
Consulting & Coaching Solutions
|
||||
https://www.veen.world
|
||||
galaxy_tags:
|
||||
- ai
|
||||
- llm
|
||||
- inference
|
||||
- offline
|
||||
- privacy
|
||||
- self-hosted
|
||||
- ollama
|
||||
repository: "https://s.infinito.nexus/code"
|
||||
issue_tracker_url: "https://s.infinito.nexus/issues"
|
||||
documentation: "https://s.infinito.nexus/code/"
|
||||
logo:
|
||||
class: "fa-solid fa-microchip"
|
||||
run_after: []
|
||||
dependencies: []
|
||||
38
roles/svc-ai-ollama/tasks/01_core.yml
Normal file
38
roles/svc-ai-ollama/tasks/01_core.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
- name: create docker network for Ollama, so that other applications can access it
|
||||
community.docker.docker_network:
|
||||
name: "{{ OLLAMA_NETWORK }}"
|
||||
state: present
|
||||
ipam_config:
|
||||
- subnet: "{{ networks.local[application_id].subnet }}"
|
||||
|
||||
- name: Include dependency 'sys-svc-docker'
|
||||
include_role:
|
||||
name: sys-svc-docker
|
||||
when: run_once_sys_svc_docker is not defined
|
||||
|
||||
- name: "include docker-compose role"
|
||||
include_role:
|
||||
name: docker-compose
|
||||
vars:
|
||||
docker_compose_flush_handlers: true
|
||||
|
||||
- name: Pre-pull Ollama models
|
||||
vars:
|
||||
_cmd: "docker exec -i {{ OLLAMA_CONTAINER }} ollama pull {{ model }}"
|
||||
shell: "{{ _cmd }}"
|
||||
register: pull_result
|
||||
loop: "{{ OLLAMA_PRELOAD_MODELS }}"
|
||||
loop_control:
|
||||
loop_var: model
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
changed_when: >
|
||||
(not (ASYNC_ENABLED | bool)) and (
|
||||
'downloaded' in (pull_result.stdout | default('')) or
|
||||
'pulling manifest' in (pull_result.stdout | default(''))
|
||||
)
|
||||
failed_when: >
|
||||
(pull_result.rc | default(0)) != 0 and
|
||||
('up to date' not in (pull_result.stdout | default('')))
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
5
roles/svc-ai-ollama/tasks/main.yml
Normal file
5
roles/svc-ai-ollama/tasks/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- block:
|
||||
- include_tasks: 01_core.yml
|
||||
vars:
|
||||
flush_handlers: true
|
||||
when: run_once_svc_ai_ollama is not defined
|
||||
17
roles/svc-ai-ollama/templates/docker-compose.yml.j2
Normal file
17
roles/svc-ai-ollama/templates/docker-compose.yml.j2
Normal file
@@ -0,0 +1,17 @@
|
||||
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
||||
|
||||
ollama:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
image: {{ OLLAMA_IMAGE }}:{{ OLLAMA_VERSION }}
|
||||
container_name: {{ OLLAMA_CONTAINER }}
|
||||
expose:
|
||||
- "{{ OLLAMA_PORT }}"
|
||||
volumes:
|
||||
- ollama_models:/root/.ollama
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
|
||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||
|
||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||
ollama_models:
|
||||
name: {{ OLLAMA_VOLUME }}
|
||||
16
roles/svc-ai-ollama/vars/main.yml
Normal file
16
roles/svc-ai-ollama/vars/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
# General
|
||||
application_id: "svc-ai-ollama"
|
||||
|
||||
# Docker
|
||||
docker_compose_flush_handlers: true
|
||||
|
||||
# Ollama
|
||||
# https://ollama.com/
|
||||
OLLAMA_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.ollama.version') }}"
|
||||
OLLAMA_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.ollama.image') }}"
|
||||
OLLAMA_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.ollama.name') }}"
|
||||
OLLAMA_PORT: "{{ applications | get_app_conf(application_id, 'docker.services.ollama.port') }}"
|
||||
OLLAMA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.models') }}"
|
||||
OLLAMA_NETWORK: "{{ applications | get_app_conf(application_id, 'docker.network') }}"
|
||||
OLLAMA_PRELOAD_MODELS: "{{ applications | get_app_conf(application_id, 'preload_models') }}"
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
docker:
|
||||
services:
|
||||
mariadb:
|
||||
version: "latest"
|
||||
image: "mariadb"
|
||||
name: "mariadb"
|
||||
version: "latest"
|
||||
image: "mariadb"
|
||||
name: "mariadb"
|
||||
backup:
|
||||
database_routine: true
|
||||
# Performance Variables aren't used yet, but will be in the future as soon as an docker file is implemented
|
||||
cpus: "2.0"
|
||||
mem_reservation: "2g"
|
||||
mem_limit: "4g"
|
||||
pids_limit: 1024
|
||||
network: "mariadb"
|
||||
volumes:
|
||||
data: "mariadb_data"
|
||||
@@ -5,7 +5,7 @@ network:
|
||||
docker:
|
||||
services:
|
||||
openldap:
|
||||
image: "bitnami/openldap"
|
||||
image: "bitnamilegacy/openldap"
|
||||
name: "openldap"
|
||||
version: "latest"
|
||||
network: "openldap"
|
||||
|
||||
40
roles/svc-db-openldap/handlers/main.yml
Normal file
40
roles/svc-db-openldap/handlers/main.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
- name: Load memberof module from file in OpenLDAP container
|
||||
shell: >
|
||||
docker exec -i {{ OPENLDAP_CONTAINER }} ldapmodify -Y EXTERNAL -H ldapi:/// -f "{{ [OPENLDAP_LDIF_PATH_DOCKER, 'configuration/01_member_of_configuration.ldif' ] | path_join }}"
|
||||
listen:
|
||||
- "Import configuration LDIF files"
|
||||
# @todo Remove the following ignore errors when setting up a new server
|
||||
# Just here because debugging would take to much time
|
||||
ignore_errors: true
|
||||
|
||||
- name: Refint Module Activation for OpenLDAP
|
||||
shell: >
|
||||
docker exec -i {{ OPENLDAP_CONTAINER }} ldapadd -Y EXTERNAL -H ldapi:/// -f "{{ [ OPENLDAP_LDIF_PATH_DOCKER, 'configuration/02_member_of_configuration.ldif' ] | path_join }}"
|
||||
listen:
|
||||
- "Import configuration LDIF files"
|
||||
register: ldapadd_result
|
||||
failed_when: ldapadd_result.rc not in [0, 68]
|
||||
# @todo Remove the following ignore errors when setting up a new server
|
||||
# Just here because debugging would take to much time
|
||||
ignore_errors: true
|
||||
|
||||
- name: Refint Overlay Configuration for OpenLDAP
|
||||
shell: >
|
||||
docker exec -i {{ OPENLDAP_CONTAINER }} ldapmodify -Y EXTERNAL -H ldapi:/// -f "{{ [ OPENLDAP_LDIF_PATH_DOCKER, 'configuration/03_member_of_configuration.ldif' ] | path_join }}"
|
||||
listen:
|
||||
- "Import configuration LDIF files"
|
||||
register: ldapadd_result
|
||||
failed_when: ldapadd_result.rc not in [0, 68]
|
||||
# @todo Remove the following ignore errors when setting up a new server
|
||||
# Just here because debugging would take to much time
|
||||
ignore_errors: true
|
||||
|
||||
- name: "Import users, groups, etc. to LDAP"
|
||||
shell: >
|
||||
docker exec -i {{ OPENLDAP_CONTAINER }} ldapadd -x -D "{{ LDAP.DN.ADMINISTRATOR.DATA }}" -w "{{ LDAP.BIND_CREDENTIAL }}" -c -f "{{ [ OPENLDAP_LDIF_PATH_DOCKER, 'groups', (item | basename | regex_replace('\.j2$', '')) ] | path_join }}"
|
||||
register: ldapadd_result
|
||||
changed_when: "'adding new entry' in ldapadd_result.stdout"
|
||||
failed_when: ldapadd_result.rc not in [0, 20, 68, 65]
|
||||
listen:
|
||||
- "Import groups LDIF files"
|
||||
loop: "{{ query('fileglob', role_path ~ '/templates/ldif/groups/*.j2') | sort }}"
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
- name: "Query available LDAP databases"
|
||||
shell: |
|
||||
docker exec {{ openldap_name }} \
|
||||
docker exec {{ OPENLDAP_CONTAINER }} \
|
||||
ldapsearch -Y EXTERNAL -H ldapi:/// -LLL -b cn=config "(olcDatabase=*)" dn
|
||||
register: ldap_databases
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
- name: "Generate hash for Database Admin password"
|
||||
shell: |
|
||||
docker exec {{ openldap_name }} \
|
||||
docker exec {{ OPENLDAP_CONTAINER }} \
|
||||
slappasswd -s "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
register: database_admin_pw_hash
|
||||
|
||||
- name: "Reset Database Admin password in LDAP (olcRootPW)"
|
||||
shell: |
|
||||
docker exec -i {{ openldap_name }} ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF
|
||||
docker exec -i {{ OPENLDAP_CONTAINER }} ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF
|
||||
dn: {{ data_backend_dn }}
|
||||
changetype: modify
|
||||
replace: olcRootPW
|
||||
@@ -42,13 +42,13 @@
|
||||
|
||||
- name: "Generate hash for Configuration Admin password"
|
||||
shell: |
|
||||
docker exec {{ openldap_name }} \
|
||||
docker exec {{ OPENLDAP_CONTAINER }} \
|
||||
slappasswd -s "{{ applications | get_app_conf(application_id, 'credentials.administrator_password', True) }}"
|
||||
register: config_admin_pw_hash
|
||||
|
||||
- name: "Reset Configuration Admin password in LDAP (olcRootPW)"
|
||||
shell: |
|
||||
docker exec -i {{ openldap_name }} ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF
|
||||
docker exec -i {{ OPENLDAP_CONTAINER }} ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF
|
||||
dn: {{ config_backend_dn }}
|
||||
changetype: modify
|
||||
replace: olcRootPW
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
- name: Ensure LDAP users exist
|
||||
community.general.ldap_entry:
|
||||
dn: "{{ LDAP.USER.ATTRIBUTES.ID }}={{ item.key }},{{ LDAP.DN.OU.USERS }}"
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
server_uri: "{{ OPENLDAP_SERVER_URI }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
objectClass: "{{ LDAP.USER.OBJECTS.STRUCTURAL }}"
|
||||
@@ -30,7 +30,7 @@
|
||||
- name: Ensure required objectClass values and mail address are present
|
||||
community.general.ldap_attrs:
|
||||
dn: "{{ LDAP.USER.ATTRIBUTES.ID }}={{ item.key }},{{ LDAP.DN.OU.USERS }}"
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
server_uri: "{{ OPENLDAP_SERVER_URI }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
attributes:
|
||||
@@ -46,7 +46,7 @@
|
||||
- name: "Ensure container for application roles exists"
|
||||
community.general.ldap_entry:
|
||||
dn: "{{ LDAP.DN.OU.ROLES }}"
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
server_uri: "{{ OPENLDAP_SERVER_URI }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
objectClass: organizationalUnit
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- name: Gather all users with their current objectClass list
|
||||
community.general.ldap_search:
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
server_uri: "{{ OPENLDAP_SERVER_URI }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
dn: "{{ LDAP.DN.OU.USERS }}"
|
||||
@@ -14,16 +14,16 @@
|
||||
|
||||
- name: Add only missing auxiliary classes
|
||||
community.general.ldap_attrs:
|
||||
server_uri: "{{ openldap_server_uri }}"
|
||||
server_uri: "{{ OPENLDAP_SERVER_URI }}"
|
||||
bind_dn: "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
bind_pw: "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
dn: "{{ item.dn }}"
|
||||
attributes:
|
||||
objectClass: "{{ missing_auxiliary }}"
|
||||
state: present
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
loop: "{{ ldap_users_with_classes.results }}"
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
loop: "{{ ldap_users_with_classes.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.dn }}"
|
||||
vars:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- name: "Create LDIF files at {{ openldap_ldif_host_path }}{{ folder }}"
|
||||
- name: "Create LDIF files at {{ OPENLDAP_LDIF_PATH_HOST }}{{ folder }}"
|
||||
template:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ openldap_ldif_host_path }}{{ folder }}/{{ item | basename | regex_replace('\\.j2$', '') }}"
|
||||
dest: "{{ OPENLDAP_LDIF_PATH_HOST }}{{ folder }}/{{ item | basename | regex_replace('\\.j2$', '') }}"
|
||||
mode: "0770"
|
||||
loop: >-
|
||||
{{
|
||||
@@ -1,25 +1,25 @@
|
||||
---
|
||||
|
||||
- name: "include docker-compose role"
|
||||
include_role:
|
||||
include_role:
|
||||
name: docker-compose
|
||||
|
||||
- name: Create {{ domains | get_domain(application_id) }}.conf if LDAP is exposed to internet
|
||||
template:
|
||||
template:
|
||||
src: "nginx.stream.conf.j2"
|
||||
dest: "{{ NGINX.DIRECTORIES.STREAMS }}{{ domains | get_domain(application_id) }}.conf"
|
||||
notify: restart openresty
|
||||
when: applications | get_app_conf(application_id, 'network.public', True) | bool
|
||||
when: OPENLDAP_NETWORK_SWITCH_PUBLIC | bool
|
||||
|
||||
- name: Remove {{ domains | get_domain(application_id) }}.conf if LDAP is not exposed to internet
|
||||
file:
|
||||
path: "{{ NGINX.DIRECTORIES.STREAMS }}{{ domains | get_domain(application_id) }}.conf"
|
||||
state: absent
|
||||
when: not applications | get_app_conf(application_id, 'network.public', True) | bool
|
||||
when: not OPENLDAP_NETWORK_SWITCH_PUBLIC | bool
|
||||
|
||||
- name: create docker network for LDAP, so that other applications can access it
|
||||
community.docker.docker_network:
|
||||
name: "{{ openldap_network }}"
|
||||
name: "{{ OPENLDAP_NETWORK }}"
|
||||
state: present
|
||||
ipam_config:
|
||||
- subnet: "{{ networks.local[application_id].subnet }}"
|
||||
@@ -37,23 +37,23 @@
|
||||
- name: "Reset LDAP Credentials"
|
||||
include_tasks: 01_credentials.yml
|
||||
when:
|
||||
- applications | get_app_conf(application_id, 'network.local')
|
||||
- applications | get_app_conf(application_id, 'provisioning.credentials', True)
|
||||
- OPENLDAP_NETWORK_SWITCH_LOCAL | bool
|
||||
- applications | get_app_conf(application_id, 'provisioning.credentials')
|
||||
|
||||
- name: "create directory {{openldap_ldif_host_path}}{{item}}"
|
||||
- name: "create directory {{ OPENLDAP_LDIF_PATH_HOST }}{{ item }}"
|
||||
file:
|
||||
path: "{{openldap_ldif_host_path}}{{item}}"
|
||||
path: "{{ OPENLDAP_LDIF_PATH_HOST }}{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop: "{{openldap_ldif_types}}"
|
||||
loop: "{{ OPENLDAP_LDIF_TYPES }}"
|
||||
|
||||
- name: "Import LDIF Configuration"
|
||||
include_tasks: ldifs_creation.yml
|
||||
include_tasks: _ldifs_creation.yml
|
||||
loop:
|
||||
- configuration
|
||||
loop_control:
|
||||
loop_var: folder
|
||||
when: applications | get_app_conf(application_id, 'provisioning.configuration', True)
|
||||
when: applications | get_app_conf(application_id, 'provisioning.configuration')
|
||||
|
||||
- name: flush LDIF handlers
|
||||
meta: flush_handlers
|
||||
@@ -66,20 +66,22 @@
|
||||
|
||||
- name: "Include Schemas (if enabled)"
|
||||
include_tasks: 02_schemas.yml
|
||||
when: applications | get_app_conf(application_id, 'provisioning.schemas', True)
|
||||
when: applications | get_app_conf(application_id, 'provisioning.schemas')
|
||||
|
||||
- name: "Import LDAP Entries (if enabled)"
|
||||
include_tasks: 03_users.yml
|
||||
when: applications | get_app_conf(application_id, 'provisioning.users', True)
|
||||
when: applications | get_app_conf(application_id, 'provisioning.users')
|
||||
|
||||
- name: "Import LDIF Data (if enabled)"
|
||||
include_tasks: ldifs_creation.yml
|
||||
include_tasks: _ldifs_creation.yml
|
||||
loop:
|
||||
- groups
|
||||
loop_control:
|
||||
loop_var: folder
|
||||
when: applications | get_app_conf(application_id, 'provisioning.groups', True)
|
||||
when: applications | get_app_conf(application_id, 'provisioning.groups')
|
||||
|
||||
- meta: flush_handlers
|
||||
|
||||
- name: "Add Objects to all users"
|
||||
include_tasks: 04_update.yml
|
||||
when: applications | get_app_conf(application_id, 'provisioning.update', True)
|
||||
when: applications | get_app_conf(application_id, 'provisioning.update')
|
||||
@@ -13,9 +13,9 @@
|
||||
- "( 1.3.6.1.4.1.99999.2 NAME '{{ LDAP.USER.OBJECTS.AUXILIARY.NEXTCLOUD_USER }}' DESC 'Auxiliary class for Nextcloud attributes' AUXILIARY MAY ( {{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }} ) )"
|
||||
command: >
|
||||
ldapsm
|
||||
-s {{ openldap_server_uri }}
|
||||
-D '{{ openldap_bind_dn }}'
|
||||
-W '{{ openldap_bind_pw }}'
|
||||
-s {{ OPENLDAP_SERVER_URI }}
|
||||
-D '{{ OPENLDAP_BIND_DN }}'
|
||||
-W '{{ OPENLDAP_BIND_PW }}'
|
||||
-n {{ schema_name }}
|
||||
{% for at in attribute_defs %}
|
||||
-a "{{ at }}"
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
command: >
|
||||
ldapsm
|
||||
-s {{ openldap_server_uri }}
|
||||
-D '{{ openldap_bind_dn }}'
|
||||
-W '{{ openldap_bind_pw }}'
|
||||
-s {{ OPENLDAP_SERVER_URI }}
|
||||
-D '{{ OPENLDAP_BIND_DN }}'
|
||||
-W '{{ OPENLDAP_BIND_PW }}'
|
||||
-n {{ schema_name }}
|
||||
{% for at in attribute_defs %}
|
||||
-a "{{ at }}"
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
||||
|
||||
application:
|
||||
image: "{{ openldap_image }}:{{ openldap_version }}"
|
||||
container_name: "{{ openldap_name }}"
|
||||
image: "{{ OPENLDAP_IMAGE }}:{{ OPENLDAP_VERSION }}"
|
||||
container_name: "{{ OPENLDAP_CONTAINER }}"
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
{% if openldap_network_expose_local %}
|
||||
{% if OPENLDAP_NETWORK_EXPOSE_LOCAL | bool %}
|
||||
ports:
|
||||
- 127.0.0.1:{{ports.localhost.ldap['svc-db-openldap']}}:{{openldap_docker_port_open}}
|
||||
- 127.0.0.1:{{ports.localhost.ldap['svc-db-openldap']}}:{{ OPENLDAP_DOCKER_PORT_OPEN }}
|
||||
{% endif %}
|
||||
volumes:
|
||||
- 'data:/bitnami/openldap'
|
||||
- '{{openldap_ldif_host_path}}:{{ openldap_ldif_docker_path }}:ro'
|
||||
- '{{ OPENLDAP_LDIF_PATH_HOST }}:{{ OPENLDAP_LDIF_PATH_DOCKER }}:ro'
|
||||
healthcheck:
|
||||
test: >
|
||||
bash -c '
|
||||
ldapsearch -x -H ldap://localhost:{{ openldap_docker_port_open }} \
|
||||
ldapsearch -x -H ldap://localhost:{{ OPENLDAP_DOCKER_PORT_OPEN }} \
|
||||
-D "{{ LDAP.DN.ADMINISTRATOR.DATA }}" -w "{{ LDAP.BIND_CREDENTIAL }}" -b "{{ LDAP.DN.ROOT }}" > /dev/null \
|
||||
&& ldapsearch -Y EXTERNAL -H ldapi:/// \
|
||||
-b cn=config "(&(objectClass=olcOverlayConfig)(olcOverlay=memberof))" \
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||
data:
|
||||
name: "{{ openldap_volume }}"
|
||||
name: "{{ OPENLDAP_VOLUME }}"
|
||||
|
||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||
@@ -3,24 +3,24 @@
|
||||
|
||||
# GENERAL
|
||||
## Admin (Data)
|
||||
LDAP_ADMIN_USERNAME= {{ applications | get_app_conf(application_id, 'users.administrator.username') }} # LDAP database admin user.
|
||||
LDAP_ADMIN_PASSWORD= {{ LDAP.BIND_CREDENTIAL }} # LDAP database admin password.
|
||||
LDAP_ADMIN_USERNAME= {{ applications | get_app_conf(application_id, 'users.administrator.username') }} # LDAP database admin user.
|
||||
LDAP_ADMIN_PASSWORD= {{ LDAP.BIND_CREDENTIAL }} # LDAP database admin password.
|
||||
|
||||
## Users
|
||||
LDAP_USERS= ' ' # Comma separated list of LDAP users to create in the default LDAP tree. Default: user01,user02
|
||||
LDAP_PASSWORDS= ' ' # Comma separated list of passwords to use for LDAP users. Default: bitnami1,bitnami2
|
||||
LDAP_ROOT= {{ LDAP.DN.ROOT }} # LDAP baseDN (or suffix) of the LDAP tree. Default: dc=example,dc=org
|
||||
LDAP_USERS= ' ' # Comma separated list of LDAP users to create in the default LDAP tree. Default: user01,user02
|
||||
LDAP_PASSWORDS= ' ' # Comma separated list of passwords to use for LDAP users. Default: bitnami1,bitnami2
|
||||
LDAP_ROOT= {{ LDAP.DN.ROOT }} # LDAP baseDN (or suffix) of the LDAP tree. Default: dc=example,dc=org
|
||||
|
||||
## Admin (Config)
|
||||
LDAP_ADMIN_DN= {{LDAP.DN.ADMINISTRATOR.DATA}}
|
||||
LDAP_ADMIN_DN= {{ LDAP.DN.ADMINISTRATOR.DATA }}
|
||||
LDAP_CONFIG_ADMIN_ENABLED= yes
|
||||
LDAP_CONFIG_ADMIN_USERNAME= {{ applications | get_app_conf(application_id, 'users.administrator.username') }}
|
||||
LDAP_CONFIG_ADMIN_PASSWORD= {{ applications | get_app_conf(application_id, 'credentials.administrator_password') }}
|
||||
|
||||
# Network
|
||||
LDAP_PORT_NUMBER= {{openldap_docker_port_open}} # Route to default port
|
||||
LDAP_ENABLE_TLS= no # Using nginx proxy for tls
|
||||
LDAP_LDAPS_PORT_NUMBER= {{openldap_docker_port_secure}} # Port used for TLS secure traffic. Priviledged port is supported (e.g. 636). Default: 1636 (non privileged port).
|
||||
LDAP_PORT_NUMBER= {{ OPENLDAP_DOCKER_PORT_OPEN }} # Route to default port
|
||||
LDAP_ENABLE_TLS= no # Using nginx proxy for tls
|
||||
LDAP_LDAPS_PORT_NUMBER= {{ OPENLDAP_DOCKER_PORT_SECURE }} # Port used for TLS secure traffic. Priviledged port is supported (e.g. 636). Default: 1636 (non privileged port).
|
||||
|
||||
# Security
|
||||
LDAP_ALLOW_ANON_BINDING= no # Allow anonymous bindings to the LDAP server. Default: yes.
|
||||
LDAP_ALLOW_ANON_BINDING= no # Allow anonymous bindings to the LDAP server. Default: yes.
|
||||
@@ -1,30 +0,0 @@
|
||||
{#
|
||||
@todo: activate
|
||||
{% for dn, entry in (applications | build_ldap_role_entries(users, ldap)).items() %}
|
||||
|
||||
dn: {{ dn }}
|
||||
{% for oc in entry.objectClass %}
|
||||
objectClass: {{ oc }}
|
||||
{% endfor %}
|
||||
{% if entry.ou is defined %}
|
||||
ou: {{ entry.ou }}
|
||||
{% else %}
|
||||
cn: {{ entry.cn }}
|
||||
{% endif %}
|
||||
{% if entry.gidNumber is defined %}
|
||||
gidNumber: {{ entry.gidNumber }}
|
||||
{% endif %}
|
||||
description: {{ entry.description }}
|
||||
{% if entry.memberUid is defined %}
|
||||
{% for uid in entry.memberUid %}
|
||||
memberUid: {{ uid }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if entry.member is defined %}
|
||||
{% for m in entry.member %}
|
||||
member: {{ m }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
#}
|
||||
@@ -1,4 +1,4 @@
|
||||
{% for dn, entry in (applications | build_ldap_role_entries(users, ldap)).items() %}
|
||||
{% for dn, entry in (applications | build_ldap_role_entries(users, LDAP)).items() %}
|
||||
|
||||
dn: {{ dn }}
|
||||
{% for oc in entry.objectClass %}
|
||||
@@ -1,24 +1,27 @@
|
||||
application_id: "svc-db-openldap"
|
||||
|
||||
# LDAP Variables
|
||||
openldap_docker_port_secure: 636
|
||||
openldap_docker_port_open: 389
|
||||
openldap_server_uri: "ldap://127.0.0.1:{{ ports.localhost.ldap[application_id] }}"
|
||||
openldap_bind_dn: "{{ LDAP.DN.ADMINISTRATOR.CONFIGURATION }}"
|
||||
openldap_bind_pw: "{{ applications | get_app_conf(application_id, 'credentials.administrator_password', True) }}"
|
||||
OPENLDAP_DOCKER_PORT_SECURE: 636
|
||||
OPENLDAP_DOCKER_PORT_OPEN: 389
|
||||
OPENLDAP_SERVER_URI: "ldap://127.0.0.1:{{ ports.localhost.ldap[application_id] }}"
|
||||
OPENLDAP_BIND_DN: "{{ LDAP.DN.ADMINISTRATOR.CONFIGURATION }}"
|
||||
OPENLDAP_BIND_PW: "{{ applications | get_app_conf(application_id, 'credentials.administrator_password') }}"
|
||||
|
||||
# LDIF Variables
|
||||
openldap_ldif_host_path: "{{ docker_compose.directories.volumes }}ldif/"
|
||||
openldap_ldif_docker_path: "/tmp/ldif/"
|
||||
openldap_ldif_types:
|
||||
OPENLDAP_LDIF_PATH_HOST: "{{ docker_compose.directories.volumes }}ldif/"
|
||||
OPENLDAP_LDIF_PATH_DOCKER: "/tmp/ldif/"
|
||||
OPENLDAP_LDIF_TYPES:
|
||||
- configuration
|
||||
- groups
|
||||
- schema # Don't know if this is still needed, it's now setup via tasks
|
||||
|
||||
openldap_name: "{{ applications | get_app_conf(application_id, 'docker.services.openldap.name', True) }}"
|
||||
openldap_image: "{{ applications | get_app_conf(application_id, 'docker.services.openldap.image', True) }}"
|
||||
openldap_version: "{{ applications | get_app_conf(application_id, 'docker.services.openldap.version', True) }}"
|
||||
openldap_volume: "{{ applications | get_app_conf(application_id, 'docker.volumes.data', True) }}"
|
||||
openldap_network: "{{ applications | get_app_conf(application_id, 'docker.network', True) }}"
|
||||
# Container
|
||||
OPENLDAP_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.openldap.name') }}"
|
||||
OPENLDAP_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.openldap.image') }}"
|
||||
OPENLDAP_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.openldap.version') }}"
|
||||
OPENLDAP_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
|
||||
OPENLDAP_NETWORK: "{{ applications | get_app_conf(application_id, 'docker.network') }}"
|
||||
|
||||
openldap_network_expose_local: "{{ applications | get_app_conf(application_id, 'network.public', True) | bool or applications | get_app_conf(application_id, 'network.local') | bool }}"
|
||||
# Network
|
||||
OPENLDAP_NETWORK_SWITCH_PUBLIC: "{{ applications | get_app_conf(application_id, 'network.public') }}"
|
||||
OPENLDAP_NETWORK_SWITCH_LOCAL: "{{ applications | get_app_conf(application_id, 'network.local') }}"
|
||||
OPENLDAP_NETWORK_EXPOSE_LOCAL: "{{ OPENLDAP_NETWORK_SWITCH_PUBLIC | bool or OPENLDAP_NETWORK_SWITCH_LOCAL | bool }}"
|
||||
@@ -2,13 +2,17 @@ docker:
|
||||
services:
|
||||
postgres:
|
||||
# Postgis is necessary for mobilizon
|
||||
image: postgis/postgis
|
||||
name: postgres
|
||||
image: postgis/postgis
|
||||
name: postgres
|
||||
# Please set an version in your inventory file!
|
||||
# Rolling release isn't recommended
|
||||
version: "latest"
|
||||
version: "17-3.5"
|
||||
backup:
|
||||
database_routine: true
|
||||
cpus: "2.0"
|
||||
mem_reservation: "4g"
|
||||
mem_limit: "6g"
|
||||
pids_limit: 1024
|
||||
volumes:
|
||||
data: "postgres_data"
|
||||
network: "postgres"
|
||||
data: "postgres_data"
|
||||
network: "postgres"
|
||||
@@ -5,7 +5,7 @@
|
||||
flush_handlers: true
|
||||
when: run_once_svc_db_postgres is not defined
|
||||
|
||||
- include_tasks: "{{ playbook_dir }}/tasks/utils/load_handlers.yml"
|
||||
- include_tasks: "{{ [ playbook_dir, 'tasks/utils/load_handlers.yml' ] | path_join }}"
|
||||
# Necessary because docker handlers are overwritten by condition
|
||||
vars:
|
||||
handler_role_name: "docker-compose"
|
||||
|
||||
@@ -5,7 +5,7 @@ RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
git \
|
||||
postgresql-server-dev-all \
|
||||
postgresql-server-dev-{{ POSTGRES_VERSION_MAJOR | default('all', true) }} \
|
||||
&& git clone https://github.com/pgvector/pgvector.git /tmp/pgvector \
|
||||
&& cd /tmp/pgvector \
|
||||
&& make \
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
postgres:
|
||||
container_name: "{{ POSTGRES_CONTAINER }}"
|
||||
image: "{{ POSTGRES_CUSTOM_IMAGE_NAME }}"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
pull_policy: never
|
||||
{{ lookup('template', 'roles/docker-container/templates/build.yml.j2') | indent(4) }}
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# General
|
||||
application_id: svc-db-postgres
|
||||
entity_name: "{{ application_id | get_entity_name }}"
|
||||
|
||||
# Docker
|
||||
docker_compose_flush_handlers: true
|
||||
@@ -9,11 +10,12 @@ database_type: "{{ application_id | get_entity_name }
|
||||
|
||||
## Postgres
|
||||
POSTGRES_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
|
||||
POSTGRES_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.name') }}"
|
||||
POSTGRES_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.image') }}"
|
||||
POSTGRES_SUBNET: "{{ networks.local['svc-db-postgres'].subnet }}"
|
||||
POSTGRES_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ entity_name ~ '.name') }}"
|
||||
POSTGRES_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ entity_name ~ '.image') }}"
|
||||
POSTGRES_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ entity_name ~ '.version') }}"
|
||||
POSTGRES_VERSION_MAJOR: "{{ POSTGRES_VERSION | regex_replace('^([0-9]+).*', '\\1') }}"
|
||||
POSTGRES_NETWORK_NAME: "{{ applications | get_app_conf(application_id, 'docker.network') }}"
|
||||
POSTGRES_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.postgres.version') }}"
|
||||
POSTGRES_SUBNET: "{{ networks.local['svc-db-postgres'].subnet }}"
|
||||
POSTGRES_PASSWORD: "{{ applications | get_app_conf(application_id, 'credentials.POSTGRES_PASSWORD') }}"
|
||||
POSTGRES_PORT: "{{ database_port | default(ports.localhost.database[ application_id ]) }}"
|
||||
POSTGRES_INIT: "{{ database_username is defined and database_password is defined and database_name is defined }}"
|
||||
|
||||
@@ -16,4 +16,5 @@
|
||||
retries: 30
|
||||
networks:
|
||||
- default
|
||||
{{ lookup('template', 'roles/docker-container/templates/resource.yml.j2',vars={'service_name':'redis'}) | indent(4) }}
|
||||
{{ "\n" }}
|
||||
14
roles/svc-opt-swapfile/tasks/01_core.yml
Normal file
14
roles/svc-opt-swapfile/tasks/01_core.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
- name: Install '
|
||||
include_role:
|
||||
name: pkgmgr-install
|
||||
vars:
|
||||
package_name: "{{ SWAPFILE_PKG }}"
|
||||
when: run_once_pkgmgr_install is not defined
|
||||
|
||||
- name: Execute create swapfile script
|
||||
shell: "{{ SWAPFILE_PKG }} '{{ SWAPFILE_SIZE }}'"
|
||||
become: true
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
@@ -1,17 +1,3 @@
|
||||
- block:
|
||||
- name: Include dependency 'pkgmgr-install'
|
||||
include_role:
|
||||
name: pkgmgr-install
|
||||
when: run_once_pkgmgr_install is not defined
|
||||
- include_tasks: utils/run_once.yml
|
||||
- include_tasks: 01_core.yml
|
||||
when: run_once_svc_opt_swapfile is not defined
|
||||
|
||||
- name: "pkgmgr install"
|
||||
include_role:
|
||||
name: pkgmgr-install
|
||||
vars:
|
||||
package_name: swap-forge
|
||||
|
||||
- name: Execute create swapfile script
|
||||
shell: swap-forge "{{swapfile_size}}"
|
||||
become: true
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
application_id: "svc-opt-swapfile"
|
||||
swapfile_size: "{{ applications | get_app_conf(application_id, 'swapfile_size') }}"
|
||||
|
||||
SWAPFILE_SIZE: "{{ applications | get_app_conf(application_id, 'swapfile_size') }}"
|
||||
SWAPFILE_PKG: "swap-forge"
|
||||
@@ -1,7 +1,10 @@
|
||||
docker:
|
||||
services:
|
||||
openresty:
|
||||
name: "openresty"
|
||||
name: "openresty"
|
||||
cpus: 0.5
|
||||
mem_reservation: 1g
|
||||
mem_limit: 2g
|
||||
volumes:
|
||||
www: "/var/www/"
|
||||
nginx: "/etc/nginx/"
|
||||
@@ -1,6 +1,6 @@
|
||||
- block:
|
||||
- name: "For '{{ application_id }}': Load docker-compose"
|
||||
include_role:
|
||||
include_role:
|
||||
name: docker-compose
|
||||
vars:
|
||||
docker_compose_flush_handlers: true
|
||||
|
||||
@@ -5,21 +5,23 @@
|
||||
- sys-ctl-alm-telegram
|
||||
- sys-ctl-alm-email
|
||||
vars:
|
||||
flush_handlers: true
|
||||
system_service_timer_enabled: false
|
||||
system_service_copy_files: true
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} %I"
|
||||
system_service_tpl_on_failure: ""
|
||||
flush_handlers: true
|
||||
system_service_timer_enabled: false
|
||||
system_service_copy_files: true
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} %I"
|
||||
system_service_tpl_on_failure: ""
|
||||
system_service_force_linear_sync: false
|
||||
|
||||
- name: "Include core service for '{{ system_service_id }}'"
|
||||
include_role:
|
||||
name: sys-service
|
||||
vars:
|
||||
flush_handlers: true
|
||||
system_service_timer_enabled: false
|
||||
system_service_copy_files: true
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} %I"
|
||||
system_service_tpl_on_failure: "" # No on failure needed, because it's anyhow the default on failure procedure
|
||||
flush_handlers: true
|
||||
system_service_timer_enabled: false
|
||||
system_service_copy_files: true
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} %I"
|
||||
system_service_tpl_on_failure: "" # No on failure needed, because it's anyhow the default on failure procedure
|
||||
system_service_force_linear_sync: false
|
||||
|
||||
- name: Assert '{{ system_service_id }}'
|
||||
block:
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
vars:
|
||||
system_service_copy_files: false
|
||||
system_service_timer_enabled: true
|
||||
system_service_force_linear_sync: true
|
||||
system_service_force_flush: "{{ MODE_BACKUP | bool }}"
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_BACKUP_DOCKER_TO_LOCAL }}"
|
||||
system_service_tpl_exec_start_pre: '/usr/bin/python {{ PATH_SYSTEM_LOCK_SCRIPT }} {{ SYS_SERVICE_GROUP_MANIPULATION | join(" ") }} --ignore {{ SYS_SERVICE_BACKUP_DOCKER_2_LOC }} --timeout "{{ SYS_TIMEOUT_BACKUP_SERVICES }}"'
|
||||
system_service_tpl_exec_start: "/bin/sh -c '{{ BKP_DOCKER_2_LOC_EXEC }}'"
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }} {{ SYS_SERVICE_CLEANUP_BACKUPS_FAILED }}"
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
# system_service_tpl_exec_start_post: "/usr/bin/systemctl start {{ SYS_SERVICE_CLEANUP_BACKUPS }}" # Not possible to use it because it's a deathlock. Keep this line for documentation purposes
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
@@ -12,6 +12,7 @@
|
||||
system_service_tpl_exec_start: dockreap --no-confirmation
|
||||
system_service_tpl_exec_start_pre: "" # Anonymous volumes can allways be removed. It isn't necessary to wait for any service to stop.
|
||||
system_service_copy_files: false
|
||||
system_service_force_linear_sync: false
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
when:
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} --backups-folder-path {{ BACKUPS_FOLDER_PATH }} --maximum-backup-size-percent {{SIZE_PERCENT_MAXIMUM_BACKUP}}"
|
||||
system_service_tpl_exec_start_pre: '/usr/bin/python {{ PATH_SYSTEM_LOCK_SCRIPT }} {{ SYS_SERVICE_GROUP_MANIPULATION | join(" ") }} --ignore {{ SYS_SERVICE_GROUP_CLEANUP | join(" ") }} --timeout "{{ SYS_TIMEOUT_BACKUP_SERVICES }}"'
|
||||
system_service_copy_files: true
|
||||
system_service_force_linear_sync: false
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
vars:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
- include_role:
|
||||
name: sys-service
|
||||
vars:
|
||||
system_service_timer_enabled: true
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_CLEANUP_CERTS }}"
|
||||
system_service_copy_files: false
|
||||
system_service_timer_enabled: true
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_CLEANUP_CERTS }}"
|
||||
system_service_copy_files: false
|
||||
system_service_force_linear_sync: false
|
||||
@@ -14,3 +14,4 @@
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} {{ SIZE_PERCENT_CLEANUP_DISC_SPACE }}"
|
||||
system_service_tpl_exec_start_pre: '/usr/bin/python {{ PATH_SYSTEM_LOCK_SCRIPT }} {{ SYS_SERVICE_GROUP_MANIPULATION | join(" ") }} --ignore {{ SYS_SERVICE_GROUP_CLEANUP | join(" ") }} --timeout "{{ SYS_TIMEOUT_BACKUP_SERVICES }}"'
|
||||
system_service_force_linear_sync: false
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_CLEANUP_FAILED_BACKUPS }}"
|
||||
system_service_copy_files: false
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_tpl_exec_start: '/bin/sh -c "{{ CLEANUP_FAILED_BACKUPS_PKG }} --all --workers {{ CLEANUP_FAILED_BACKUPS_WORKERS }} --yes"'
|
||||
system_service_tpl_exec_start_pre: '/usr/bin/python {{ PATH_SYSTEM_LOCK_SCRIPT }} {{ SYS_SERVICE_GROUP_MANIPULATION | join(" ") }} --ignore {{ SYS_SERVICE_GROUP_CLEANUP| join(" ") }} --timeout "{{ SYS_TIMEOUT_CLEANUP_SERVICES }}"'
|
||||
|
||||
system_service_tpl_exec_start: '/bin/sh -c "{{ CLEANUP_FAILED_BACKUPS_PKG }} --all --workers {{ CLEANUP_FAILED_BACKUPS_WORKERS }} --yes"'
|
||||
system_service_force_linear_sync: false
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -14,6 +14,32 @@ Designed for Archlinux systems, this role periodically checks whether web resour
|
||||
- **Domain Extraction:** Parses all `.conf` files in the NGINX config folder to determine the list of domains to check.
|
||||
- **Automated Execution:** Registers a systemd service and timer for recurring health checks.
|
||||
- **Error Notification:** Integrates with `sys-ctl-alm-compose` for alerting on failure.
|
||||
- **Ignore List Support:** Optional variable to suppress network block reports from specific external domains.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Variables
|
||||
|
||||
- **`HEALTH_CSP_IGNORE_NETWORK_BLOCKS_FROM`** (list, default: `[]`)
|
||||
Optional list of domains whose network block failures (e.g., ORB) should be ignored during CSP checks.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
HEALTH_CSP_IGNORE_NETWORK_BLOCKS_FROM:
|
||||
- pxscdn.com
|
||||
- cdn.example.org
|
||||
```
|
||||
|
||||
This will run the CSP checker with:
|
||||
|
||||
```bash
|
||||
checkcsp start --short --ignore-network-blocks-from pxscdn.com -- cdn.example.org <domains...>
|
||||
```
|
||||
|
||||
### Systemd Integration
|
||||
|
||||
The role configures a systemd service and timer which executes the CSP crawler periodically against all NGINX domains.
|
||||
|
||||
## License
|
||||
|
||||
@@ -24,4 +50,4 @@ Infinito.Nexus NonCommercial License
|
||||
|
||||
Kevin Veen-Birkenbach
|
||||
Consulting & Coaching Solutions
|
||||
[https://www.veen.world](https://www.veen.world)
|
||||
[https://www.veen.world](https://www.veen.world)
|
||||
|
||||
5
roles/sys-ctl-hlth-csp/defaults/main.yml
Normal file
5
roles/sys-ctl-hlth-csp/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
# List of domains whose network block failures (e.g., ORB) should be ignored
|
||||
# during CSP checks. This is useful for suppressing known external resources
|
||||
# (e.g., third-party CDNs) that cannot be influenced but otherwise cause
|
||||
# unnecessary alerts in the crawler reports.
|
||||
HEALTH_CSP_IGNORE_NETWORK_BLOCKS_FROM: []
|
||||
@@ -21,11 +21,20 @@ def extract_domains(config_path):
|
||||
print(f"Directory {config_path} not found.", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def run_checkcsp(domains):
|
||||
def run_checkcsp(domains, ignore_network_blocks_from):
|
||||
"""
|
||||
Executes the 'checkcsp' command with the given domains.
|
||||
Executes the 'checkcsp' command with the given domains and optional ignores.
|
||||
"""
|
||||
cmd = ["checkcsp", "start", "--short"] + domains
|
||||
cmd = ["checkcsp", "start", "--short"]
|
||||
|
||||
# pass through ignore list only if not empty
|
||||
if ignore_network_blocks_from:
|
||||
cmd.append("--ignore-network-blocks-from")
|
||||
cmd.extend(ignore_network_blocks_from)
|
||||
cmd.append("--")
|
||||
|
||||
cmd += domains
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, check=True)
|
||||
return result.returncode
|
||||
@@ -45,6 +54,12 @@ def main():
|
||||
required=True,
|
||||
help="Directory containing NGINX .conf files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ignore-network-blocks-from",
|
||||
nargs="*",
|
||||
default=[],
|
||||
help="Optional: one or more domains whose network block failures should be ignored"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
domains = extract_domains(args.nginx_config_dir)
|
||||
@@ -55,7 +70,7 @@ def main():
|
||||
print("No domains found to check.")
|
||||
sys.exit(0)
|
||||
|
||||
rc = run_checkcsp(domains)
|
||||
rc = run_checkcsp(domains, args.ignore_network_blocks_from)
|
||||
sys.exit(rc)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_tpl_timeout_start_sec: "{{ CURRENT_PLAY_DOMAINS_ALL | timeout_start_sec_for_domains }}"
|
||||
system_service_tpl_exec_start: "{{ system_service_script_exec }} --nginx-config-dir={{ NGINX.DIRECTORIES.HTTP.SERVERS }}"
|
||||
system_service_tpl_exec_start: >-
|
||||
{{ system_service_script_exec }}
|
||||
--nginx-config-dir={{ NGINX.DIRECTORIES.HTTP.SERVERS }}
|
||||
--ignore-network-blocks-from {{ HEALTH_CSP_IGNORE_NETWORK_BLOCKS_FROM | join(' ') }}
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# roles/sys-ctl-hlth-webserver/filter_plugins/web_health_expectations.py
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
@@ -94,6 +93,26 @@ def _normalize_selection(group_names):
|
||||
raise ValueError("web_health_expectations: 'group_names' must be provided and non-empty")
|
||||
return sel
|
||||
|
||||
def _normalize_codes(x):
|
||||
"""
|
||||
Accepts:
|
||||
- single code (int or str)
|
||||
- list/tuple/set of codes
|
||||
Returns a de-duplicated list of valid ints (100..599) in original order.
|
||||
"""
|
||||
if x is None:
|
||||
return []
|
||||
if isinstance(x, (list, tuple, set)):
|
||||
out = []
|
||||
seen = set()
|
||||
for v in x:
|
||||
c = _valid_http_code(v)
|
||||
if c is not None and c not in seen:
|
||||
seen.add(c)
|
||||
out.append(c)
|
||||
return out
|
||||
c = _valid_http_code(x)
|
||||
return [c] if c is not None else []
|
||||
|
||||
def web_health_expectations(applications, www_enabled: bool = False, group_names=None, redirect_maps=None):
|
||||
"""Produce a **flat mapping**: domain -> [expected_status_codes].
|
||||
@@ -138,17 +157,15 @@ def web_health_expectations(applications, www_enabled: bool = False, group_names
|
||||
sc_map = {}
|
||||
if isinstance(sc_raw, Mapping):
|
||||
for k, v in sc_raw.items():
|
||||
code = _valid_http_code(v)
|
||||
if code is not None:
|
||||
sc_map[str(k)] = code
|
||||
codes = _normalize_codes(v)
|
||||
if codes:
|
||||
sc_map[str(k)] = codes
|
||||
|
||||
if isinstance(canonical_raw, Mapping) and canonical_raw:
|
||||
for key, domains in canonical_raw.items():
|
||||
domains_list = _to_list(domains, allow_mapping=False)
|
||||
code = _valid_http_code(sc_map.get(key))
|
||||
if code is None:
|
||||
code = _valid_http_code(sc_map.get("default"))
|
||||
expected = [code] if code is not None else list(DEFAULT_OK)
|
||||
codes = sc_map.get(key) or sc_map.get("default")
|
||||
expected = list(codes) if codes else list(DEFAULT_OK)
|
||||
for d in domains_list:
|
||||
if d:
|
||||
expectations[d] = expected
|
||||
@@ -156,8 +173,8 @@ def web_health_expectations(applications, www_enabled: bool = False, group_names
|
||||
for d in _to_list(canonical_raw, allow_mapping=True):
|
||||
if not d:
|
||||
continue
|
||||
code = _valid_http_code(sc_map.get("default"))
|
||||
expectations[d] = [code] if code is not None else list(DEFAULT_OK)
|
||||
codes = sc_map.get("default")
|
||||
expectations[d] = list(codes) if codes else list(DEFAULT_OK)
|
||||
|
||||
for d in aliases:
|
||||
if d:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
system_service_tpl_exec_start: >
|
||||
{{ system_service_script_exec }}
|
||||
--web-protocol {{ WEB_PROTOCOL }}
|
||||
--expectations '{{ applications | web_health_expectations(www_enabled=WWW_REDIRECT_ENABLED, group_names=group_names) | to_json }}'
|
||||
--expectations '{{ applications | web_health_expectations(www_enabled=WWW_REDIRECT_ENABLED | bool, group_names=group_names) | to_json }}'
|
||||
system_service_suppress_flush: true # The healthcheck will just work after all routines passed
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
- include_role:
|
||||
name: sys-service
|
||||
vars:
|
||||
system_service_state: restarted
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_MAINTANANCE_LETSENCRYPT_DEPLOY }}"
|
||||
persistent: "true"
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_state: restarted
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_MAINTANANCE_LETSENCRYPT_DEPLOY }}"
|
||||
persistent: "true"
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_force_linear_sync: false
|
||||
@@ -3,7 +3,7 @@
|
||||
name: '{{ item }}'
|
||||
loop:
|
||||
- sys-svc-certbot
|
||||
- sys-svc-webserver
|
||||
- sys-svc-webserver-core
|
||||
- sys-ctl-alm-compose
|
||||
|
||||
- name: install certbot
|
||||
@@ -15,8 +15,9 @@
|
||||
- include_role:
|
||||
name: sys-service
|
||||
vars:
|
||||
system_service_copy_files: false
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_MAINTANANCE_LETSENCRYPT_RENEW }}"
|
||||
persistent: true
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_copy_files: false
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_MAINTANANCE_LETSENCRYPT_RENEW }}"
|
||||
persistent: true
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_force_linear_sync: false
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
- include_role:
|
||||
name: sys-service
|
||||
vars:
|
||||
system_service_suppress_flush: true # It takes a super long time - Better wait for failure of timed service instead of executing it on every play
|
||||
system_service_copy_files: false
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_REPAIR_BTRFS_AUTO_BALANCER }}"
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_tpl_exec_start: "/bin/sh -c 'btrfs-auto-balancer 90 10'"
|
||||
system_service_suppress_flush: true # It takes a super long time - Better wait for failure of timed service instead of executing it on every play
|
||||
system_service_copy_files: false
|
||||
system_service_on_calendar: "{{ SYS_SCHEDULE_REPAIR_BTRFS_AUTO_BALANCER }}"
|
||||
system_service_timer_enabled: true
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_tpl_exec_start: "/bin/sh -c 'btrfs-auto-balancer 90 10'"
|
||||
system_service_force_linear_sync: true
|
||||
@@ -12,5 +12,6 @@
|
||||
system_service_tpl_exec_start: '{{ system_service_script_exec }} {{ PATH_DOCKER_COMPOSE_INSTANCES }}'
|
||||
system_service_tpl_exec_start_post: "/usr/bin/systemctl start {{ SYS_SERVICE_CLEANUP_ANONYMOUS_VOLUMES }}"
|
||||
system_service_tpl_on_failure: "{{ SYS_SERVICE_ON_FAILURE_COMPOSE }}"
|
||||
system_service_force_linear_sync: true
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
system_service_tpl_exec_start_pre: "/usr/bin/python {{ PATH_SYSTEM_LOCK_SCRIPT }} {{ SYS_SERVICE_GROUP_MANIPULATION | join(' ') }} --ignore {{ SYS_SERVICE_GROUP_CLEANUP| join(' ') }} {{ SYS_SERVICE_REPAIR_DOCKER_SOFT }} --timeout '{{ SYS_TIMEOUT_DOCKER_RPR_SOFT }}'"
|
||||
system_service_tpl_exec_start: >
|
||||
/bin/sh -c '{{ system_service_script_exec }} --manipulation-string "{{ SYS_SERVICE_GROUP_MANIPULATION | join(" ") }}" {{ PATH_DOCKER_COMPOSE_INSTANCES }}'
|
||||
system_service_force_linear_sync: true
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
proxied: "{{ item.proxied | default(false) }}"
|
||||
ttl: "{{ item.ttl | default(1) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
solo: "{{ item.solo | default(false) }}"
|
||||
loop: "{{ cloudflare_records | selectattr('type','in',['A','AAAA']) | list }}"
|
||||
loop_control: { label: "{{ item.type }} {{ item.name }} -> {{ item.content }}" }
|
||||
async: "{{ cloudflare_async_enabled | ternary(cloudflare_async_time, omit) }}"
|
||||
@@ -48,6 +49,7 @@
|
||||
ttl: "{{ item.ttl | default(1) }}"
|
||||
priority: "{{ (item.type == 'MX') | ternary(item.priority | default(10), omit) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
solo: "{{ item.solo | default(false) }}"
|
||||
loop: "{{ cloudflare_records | selectattr('type','in',['CNAME','MX','TXT']) | list }}"
|
||||
loop_control: { label: "{{ item.type }} {{ item.name }} -> {{ item.value }}" }
|
||||
async: "{{ cloudflare_async_enabled | ternary(cloudflare_async_time, omit) }}"
|
||||
@@ -83,6 +85,7 @@
|
||||
value: "{{ item.value }}"
|
||||
ttl: "{{ item.ttl | default(1) }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
solo: "{{ item.solo | default(false) }}"
|
||||
loop: "{{ cloudflare_records | selectattr('type','equalto','SRV') | list }}"
|
||||
loop_control: { label: "SRV {{ item.service }}.{{ item.proto }} {{ item.name }} -> {{ item.value }}:{{ item.port }}" }
|
||||
ignore_errors: "{{ item.ignore_errors | default(true) }}"
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
include_role:
|
||||
name: sys-dns-cloudflare-records
|
||||
vars:
|
||||
cloudflare_records: "{{ SYN_DNS_WILDCARD_RECORDS }}"
|
||||
cloudflare_records: "{{ SYN_DNS_WILDCARD_RECORDS }}"
|
||||
cloudflare_async_enabled: "{{ ASYNC_ENABLED | bool }}"
|
||||
cloudflare_async_time: "{{ ASYNC_TIME }}"
|
||||
cloudflare_async_poll: "{{ ASYNC_POLL }}"
|
||||
when: DNS_PROVIDER == 'cloudflare'
|
||||
|
||||
- include_tasks: utils/run_once.yml
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
when: inj_enabled.logout
|
||||
|
||||
- block:
|
||||
- name: Include dependency 'sys-svc-webserver'
|
||||
- name: Include dependency 'sys-svc-webserver-core'
|
||||
include_role:
|
||||
name: sys-svc-webserver
|
||||
when: run_once_sys_svc_webserver is not defined
|
||||
name: sys-svc-webserver-core
|
||||
when: run_once_sys_svc_webserver_core is not defined
|
||||
- include_tasks: utils/run_once.yml
|
||||
when: run_once_sys_front_inj_all is not defined
|
||||
@@ -1,7 +1,7 @@
|
||||
- name: Include dependency 'sys-svc-webserver'
|
||||
- name: Include dependency 'sys-svc-webserver-core'
|
||||
include_role:
|
||||
name: sys-svc-webserver
|
||||
when: run_once_sys_svc_webserver is not defined
|
||||
name: sys-svc-webserver-core
|
||||
when: run_once_sys_svc_webserver_core is not defined
|
||||
|
||||
- name: Generate color palette with colorscheme-generator
|
||||
set_fact:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
- block:
|
||||
- name: Include dependency 'sys-svc-webserver'
|
||||
- name: Include dependency 'sys-svc-webserver-core'
|
||||
include_role:
|
||||
name: sys-svc-webserver
|
||||
when: run_once_sys_svc_webserver is not defined
|
||||
name: sys-svc-webserver-core
|
||||
when: run_once_sys_svc_webserver_core is not defined
|
||||
- include_tasks: 01_deploy.yml
|
||||
- include_tasks: utils/run_once.yml
|
||||
when: run_once_sys_front_inj_desktop is not defined
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
- block:
|
||||
|
||||
- name: Include dependency 'sys-svc-webserver'
|
||||
- name: Include dependency 'sys-svc-webserver-core'
|
||||
include_role:
|
||||
name: sys-svc-webserver
|
||||
when: run_once_sys_svc_webserver is not defined
|
||||
name: sys-svc-webserver-core
|
||||
when: run_once_sys_svc_webserver_core is not defined
|
||||
- include_tasks: utils/run_once.yml
|
||||
when: run_once_sys_front_inj_javascript is not defined
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
- name: Include dependency 'sys-svc-webserver'
|
||||
- name: Include dependency 'sys-svc-webserver-core'
|
||||
include_role:
|
||||
name: sys-svc-webserver
|
||||
name: sys-svc-webserver-core
|
||||
when:
|
||||
- run_once_sys_svc_webserver is not defined
|
||||
- run_once_sys_svc_webserver_core is not defined
|
||||
|
||||
- name: "deploy the logout.js"
|
||||
include_tasks: "02_deploy.yml"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user