Compare commits
76 Commits
bd23339edd
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c89b8129ec | |||
| c98b2d8232 | |||
| 3a9b3786c5 | |||
| 1131eceb94 | |||
| 586be50567 | |||
| d0e08b8fc1 | |||
| 487117b31a | |||
| 82fa90d843 | |||
| ef1e7432ee | |||
| 427ea20e43 | |||
| 3298219ccf | |||
| b30c7f3a8a | |||
| 374511c123 | |||
| 313f7f1c21 | |||
| 6aa78525c2 | |||
| 5e0de23ff7 | |||
| 3c655dabcf | |||
| 60e59f95a8 | |||
| cdf37fb2c9 | |||
| f56b734cb4 | |||
| 10c4373c0e | |||
| 2619d86dc1 | |||
| 1b679a4f09 | |||
| 02033cd3f9 | |||
| d932bc57d0 | |||
| c4cfa8081f | |||
| 90c9094a6f | |||
| 36c5f2b1e3 | |||
| 7bc2729ef6 | |||
| 9d79f828e4 | |||
| 22b3659cdf | |||
| 4440fddb2b | |||
| d74000fbd0 | |||
| 9efdb5c781 | |||
| e7fd52616d | |||
| b7b4b4d36d | |||
| 40f5a3ce0d | |||
| 8e43118661 | |||
| 0e76f3cef1 | |||
| 8d18ab7059 | |||
| 6a959d85c3 | |||
| 79c583eca3 | |||
| 7fcafed3f9 | |||
| 0b67830d7f | |||
| d878a0f9b8 | |||
| 9c38910a67 | |||
| 4a38a9421d | |||
| 5d32693925 | |||
| 696cecfecb | |||
| c0360a14b9 | |||
| 5589594d2c | |||
| 0bd41b2fb1 | |||
| 5f10d0366e | |||
| 9f98101c5d | |||
| 0c20676590 | |||
| 59e2c2b9a3 | |||
| 61d49e85a3 | |||
| 8f84ed6d83 | |||
| 24cbb02bff | |||
| 685a472572 | |||
| 9de1bee542 | |||
| 306e2c14db | |||
| 63b47b59b5 | |||
| 52bd2d9fa2 | |||
| 034ad17cf9 | |||
| ef2846562b | |||
| 52e2102f93 | |||
| a9b21912db | |||
| c9e4aeb8d6 | |||
| 96a44d5da6 | |||
| 0d936677e5 | |||
| 5c38b92bc8 | |||
| fc5c882193 | |||
| a4501d4034 | |||
| c0958d8f02 | |||
| 2a97864a06 |
@@ -0,0 +1,71 @@
|
||||
name: Generate Docs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_HOST: tcp://docker-socket-proxy:2375
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install docker CLI
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y docker.io docker-compose
|
||||
|
||||
- name: Generate docs
|
||||
run: |
|
||||
scripts/docs/generate-all.sh
|
||||
|
||||
- name: Validate generated docs
|
||||
run: |
|
||||
set -e
|
||||
|
||||
test -s docs/generated/docker-compose.resolved.yml
|
||||
test -s docs/generated/host-topology.md
|
||||
test -s docs/public/physical-topology.svg
|
||||
test -s docs/public/docker-traefik-dynu.svg
|
||||
|
||||
! grep -R "Host inventory JSON not found" docs/public docs/diagrams
|
||||
! grep -R "Generate terraform inventory" docs/public docs/diagrams
|
||||
|
||||
# Ensure no obvious secrets leaked
|
||||
! grep -R -E -i "password|token|api[_-]?key|secret" docs/public \
|
||||
|| (echo "Secret-like string detected"; exit 1)
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config user.name "docs-bot"
|
||||
git config user.email "docs-bot@local"
|
||||
|
||||
git add docs/generated docs/diagrams docs/public data/terraform/proxmox-inventory.json || true
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "docs: regenerate documentation artifacts"
|
||||
|
||||
- name: Push to Gitea
|
||||
run: |
|
||||
git push origin HEAD:main
|
||||
|
||||
- name: Push to GitHub mirror
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_PUSH_TOKEN }}
|
||||
GITHUB_MIRROR_REPO: ${{ vars.GITHUB_MIRROR_REPO }}
|
||||
run: |
|
||||
test -n "$GITHUB_TOKEN"
|
||||
test -n "$GITHUB_MIRROR_REPO"
|
||||
git remote add github "https://$GITHUB_TOKEN@github.com/$GITHUB_MIRROR_REPO.git" || true
|
||||
git push github HEAD:main
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Validate Docs (Gitea)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate docs
|
||||
run: |
|
||||
set -e
|
||||
|
||||
test -d docs/public
|
||||
test -s docs/public/physical-topology.svg
|
||||
|
||||
! grep -R "Host inventory JSON not found" docs/public
|
||||
! grep -R "Generate terraform inventory" docs/public
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Validate committed public docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate-public-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Ensure committed docs/public exists
|
||||
run: |
|
||||
test -d docs/public
|
||||
test -n "$(find docs/public -mindepth 1 -print -quit)"
|
||||
- name: Install MkDocs
|
||||
run: |
|
||||
python3 -m pip install --user mkdocs
|
||||
- name: Validate docs content
|
||||
run: |
|
||||
set -e
|
||||
test -s docs/public/physical-topology.svg
|
||||
test -s docs/public/docker-traefik-dynu.svg
|
||||
! grep -R "Host inventory JSON not found" docs/public
|
||||
! grep -R "Generate terraform inventory" docs/public
|
||||
! rg -n -i "password|token|api[_-]?key|secret" docs/public
|
||||
- name: Build MkDocs site
|
||||
run: |
|
||||
python3 -m mkdocs build -f mkdocs-public.yml --strict
|
||||
@@ -0,0 +1,84 @@
|
||||
name: Publish documentation site
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: github-pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Ensure committed docs/public exists
|
||||
run: |
|
||||
test -d docs/public
|
||||
test -n "$(find docs/public -mindepth 1 -print -quit)"
|
||||
|
||||
- name: Install Graphviz
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y graphviz
|
||||
dot -V
|
||||
|
||||
- name: Validate sanitized diagram artifacts
|
||||
run: |
|
||||
test -f docs/public/physical-topology.svg
|
||||
test -f docs/public/docker-traefik-dynu.svg
|
||||
! rg -n "Graphviz dot not found" docs/public/*.svg
|
||||
! rg -n "lan\.ddnsgeek\.com" docs/public/*.svg docs/public/*.md
|
||||
! rg -n -i "password|token|api_key|secret" docs/public/*.svg
|
||||
|
||||
- name: Install MkDocs
|
||||
run: |
|
||||
python3 -m pip install --user mkdocs
|
||||
|
||||
- name: Build public MkDocs site
|
||||
run: |
|
||||
python3 -m mkdocs build -f mkdocs-public.yml --strict
|
||||
|
||||
- name: Verify published content excludes internal/generated docs
|
||||
run: |
|
||||
test -d site-public
|
||||
test ! -e site-public/generated
|
||||
test ! -e site-public/docker
|
||||
|
||||
- name: Verify expected 404-only paths are not generated
|
||||
run: |
|
||||
test ! -e site-public/generated/compose-inventory/index.html
|
||||
test ! -e site-public/generated/prometheus-rules/index.html
|
||||
test ! -e site-public/docker/index.html
|
||||
|
||||
- name: Configure GitHub Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload GitHub Pages artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: site-public
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
+23
@@ -4,6 +4,7 @@
|
||||
#!**/Dockerfile
|
||||
#!docker-compose.yml
|
||||
**/data/
|
||||
apps/gitea/runner-data/
|
||||
**/db/
|
||||
**/database/
|
||||
apps/nextcloud/config/
|
||||
@@ -28,3 +29,25 @@ secrets/*
|
||||
!.env.example
|
||||
core/traefik/certs/*
|
||||
!core/traefik/certs/.gitkeep
|
||||
site/
|
||||
|
||||
# Docs generation artifacts intentionally tracked
|
||||
!data/terraform/proxmox-inventory.json
|
||||
!infrastructure/terraform/dynu/generated/
|
||||
!infrastructure/terraform/dynu/generated/dynu_dns_records_inventory.json
|
||||
!docs/generated/
|
||||
!docs/generated/docker-compose.resolved.yml
|
||||
!docs/generated/host-topology.md
|
||||
!docs/diagrams/
|
||||
!docs/diagrams/*.svg
|
||||
!docs/public/
|
||||
!docs/public/*.md
|
||||
!docs/public/*.svg
|
||||
|
||||
# Terraform local/state artifacts
|
||||
**/.terraform/
|
||||
**/.terraform.lock.hcl
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
@@ -18,6 +18,7 @@ If you only read one section, read **[Source-of-truth boundaries](docs/source-of
|
||||
- Terraform workflows (brownfield import/reconciliation): [docs/terraform-workflows.md](docs/terraform-workflows.md)
|
||||
- Infrastructure inventory intent and outputs: [docs/infrastructure-inventory.md](docs/infrastructure-inventory.md)
|
||||
- Dynu DNS read-only inventory workflow: [docs/dynu-dns-inventory.md](docs/dynu-dns-inventory.md)
|
||||
- Generated host topology doc: [docs/generated/host-topology.md](docs/generated/host-topology.md)
|
||||
- Ansible bootstrap workflows: [docs/ansible-workflows.md](docs/ansible-workflows.md)
|
||||
- Deployment prerequisites and secrets setup: [docs/deployment-prerequisites.md](docs/deployment-prerequisites.md)
|
||||
- Secrets inventory: [docs/security-secrets.md](docs/security-secrets.md)
|
||||
@@ -119,6 +120,52 @@ flowchart TB
|
||||
|
||||
For request-flow and network detail, see [docs/architecture.md](docs/architecture.md).
|
||||
|
||||
## Public docs publication workflow
|
||||
|
||||
Public docs are generated on the Docker host and committed to this repository. GitHub Actions only publishes committed content from `docs/public`.
|
||||
|
||||
1. Generate public docs locally from the repository root:
|
||||
|
||||
```bash
|
||||
./scripts/generate-public-docs.sh
|
||||
```
|
||||
|
||||
2. Inspect the generated changes:
|
||||
|
||||
```bash
|
||||
git diff -- docs/public docs/generated docs/diagrams
|
||||
```
|
||||
|
||||
3. Commit the generated public docs (and any supporting generated files you intend to version):
|
||||
|
||||
```bash
|
||||
git add docs/public docs/generated docs/diagrams
|
||||
git commit -m "docs: regenerate public docs"
|
||||
```
|
||||
|
||||
4. Push your branch:
|
||||
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
|
||||
Only files under `docs/public` are published by GitHub Pages. Internal/generated documentation is not published unless it is deliberately copied/sanitized into `docs/public`.
|
||||
|
||||
### Regenerating architecture docs (Prometheus + Dynu DNS)
|
||||
|
||||
```bash
|
||||
# Refresh Dynu live inventory and generated resources/import helpers
|
||||
cd infrastructure/terraform/dynu
|
||||
terraform apply -refresh-only
|
||||
python3 scripts/generate-brownfield-records.py --overwrite
|
||||
|
||||
# Regenerate architecture docs from Prometheus + Dynu inventory
|
||||
cd ../../..
|
||||
python3 scripts/render_prometheus_docs.py \
|
||||
--inventory-file docs/runtime/prometheus-inventory.json \
|
||||
--dynu-dns-inventory-file infrastructure/terraform/dynu/generated/dynu_dns_records_inventory.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Codex setup and maintenance scripts
|
||||
|
||||
@@ -15,7 +15,7 @@ GITEA_DB_TYPE=sqlite3
|
||||
GITEA_ROOT_URL=https://gitea.lan.ddnsgeek.com/
|
||||
# Generate a token in Gitea: Site Administration → Actions → Runners
|
||||
# or Repo → Settings → Actions → Runners
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN=
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN=vYDNxzMvayREkXoaAR3x3UREkxQB2PU4eORzmkZ9
|
||||
GITEA_RUNNER_NAME=docker-runner-01
|
||||
GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://node:20-bookworm,linux:docker://node:20-bookworm,docker:docker://docker:cli
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Generated Documentation
|
||||
|
||||
## Local generation
|
||||
|
||||
Install prerequisites:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y graphviz
|
||||
```
|
||||
|
||||
Then generate and validate public docs:
|
||||
|
||||
```bash
|
||||
chmod +x scripts/docs/*.sh
|
||||
scripts/docs/generate-all.sh
|
||||
python3 -m mkdocs build -f mkdocs-public.yml --strict
|
||||
```
|
||||
|
||||
NixOS-friendly alternative:
|
||||
|
||||
```bash
|
||||
nix shell nixpkgs#graphviz nixpkgs#python3 nixpkgs#python3Packages.pyyaml
|
||||
```
|
||||
|
||||
This pipeline only runs `docker compose config` and static parsing. It does **not** start containers.
|
||||
|
||||
## CI behaviour
|
||||
|
||||
GitHub Actions workflow `.github/workflows/generate-docs.yml` validates committed public docs and diagrams and runs a strict public MkDocs build.
|
||||
|
||||
## Outputs
|
||||
|
||||
- `docs/generated`: resolved compose config and markdown inventories
|
||||
- `docs/diagrams`: generated DOT and SVG diagrams
|
||||
- `docs/public`: sanitized copy for public sharing
|
||||
|
||||
## Publication safety
|
||||
|
||||
- `docs/public` is intended for public sharing after sanitization.
|
||||
- `docs/generated` and `docs/diagrams` may include internal details and should be treated as internal by default.
|
||||
@@ -88,6 +88,7 @@ Use architecture docs together with:
|
||||
- [docs/source-of-truth.md](source-of-truth.md)
|
||||
- [docs/terraform-workflows.md](terraform-workflows.md)
|
||||
- [docs/infrastructure-inventory.md](infrastructure-inventory.md)
|
||||
- [docs/generated/host-topology.md](generated/host-topology.md)
|
||||
|
||||
## Notes on runtime vs declared state
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Automation
|
||||
|
||||
This section describes automation systems associated with this environment.
|
||||
|
||||
Current state:
|
||||
|
||||
- CI documentation generation in GitHub Actions.
|
||||
- Static parsing and rendering from repository configuration.
|
||||
- No live service interaction during documentation generation.
|
||||
|
||||
Future content can document:
|
||||
|
||||
- Docker update automation.
|
||||
- Node-RED automation flows and operational patterns.
|
||||
@@ -0,0 +1,75 @@
|
||||
digraph Compose {
|
||||
rankdir=LR;
|
||||
node [fontname="Helvetica"];
|
||||
"svc:authelia" [label="authelia", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:crowdsec" [label="crowdsec", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:docker-socket-proxy" [label="docker-socket-proxy", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:docker-update-exporter" [label="docker-update-exporter", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:error-pages" [label="error-pages", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:gitea" [label="gitea", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:gitea-runner" [label="gitea-runner", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:gotify" [label="gotify", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:grafana" [label="grafana", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:gramps-redis" [label="gramps-redis", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:grampsweb" [label="grampsweb", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:grampsweb_celery" [label="grampsweb_celery", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:influxdb" [label="influxdb", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:monitor-kuma" [label="monitor-kuma", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:mtls-bridge" [label="mtls-bridge", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:nextcloud-db" [label="nextcloud-db", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:nextcloud-redis" [label="nextcloud-redis", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:nextcloud-webapp" [label="nextcloud-webapp", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:node-exporter" [label="node-exporter", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:node-red" [label="node-red", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:passbolt-db" [label="passbolt-db", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:passbolt-webapp" [label="passbolt-webapp", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:pihole-exporter" [label="pihole-exporter", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:portainer" [label="portainer", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:prometheus" [label="prometheus", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:searxng-webapp" [label="searxng-webapp", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:telegraf" [label="telegraf", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"svc:traefik" [label="traefik", shape=box, style=filled, fillcolor="#dfefff"];
|
||||
"net:gramps" [label="gramps", shape=ellipse, style=filled, fillcolor="#f4f4f4"];
|
||||
"net:monitor" [label="monitor", shape=ellipse, style=filled, fillcolor="#f4f4f4"];
|
||||
"net:nextcloud" [label="nextcloud", shape=ellipse, style=filled, fillcolor="#f4f4f4"];
|
||||
"net:passbolt" [label="passbolt", shape=ellipse, style=filled, fillcolor="#f4f4f4"];
|
||||
"net:traefik" [label="traefik", shape=ellipse, style=filled, fillcolor="#f4f4f4"];
|
||||
"svc:authelia" -> "net:traefik";
|
||||
"svc:crowdsec" -> "net:traefik";
|
||||
"svc:docker-socket-proxy" -> "net:monitor";
|
||||
"svc:docker-socket-proxy" -> "net:traefik";
|
||||
"svc:docker-update-exporter" -> "net:monitor";
|
||||
"svc:error-pages" -> "net:traefik";
|
||||
"svc:gitea" -> "net:traefik";
|
||||
"svc:gitea-runner" -> "net:traefik";
|
||||
"svc:gotify" -> "net:traefik";
|
||||
"svc:grafana" -> "net:monitor";
|
||||
"svc:grafana" -> "net:traefik";
|
||||
"svc:gramps-redis" -> "net:gramps";
|
||||
"svc:grampsweb" -> "net:gramps";
|
||||
"svc:grampsweb" -> "net:traefik";
|
||||
"svc:grampsweb_celery" -> "net:gramps";
|
||||
"svc:influxdb" -> "net:monitor";
|
||||
"svc:influxdb" -> "net:traefik";
|
||||
"svc:monitor-kuma" -> "net:monitor";
|
||||
"svc:monitor-kuma" -> "net:traefik";
|
||||
"svc:mtls-bridge" -> "net:monitor";
|
||||
"svc:mtls-bridge" -> "net:traefik";
|
||||
"svc:nextcloud-db" -> "net:nextcloud";
|
||||
"svc:nextcloud-redis" -> "net:nextcloud";
|
||||
"svc:nextcloud-webapp" -> "net:nextcloud";
|
||||
"svc:nextcloud-webapp" -> "net:traefik";
|
||||
"svc:node-exporter" -> "net:monitor";
|
||||
"svc:node-red" -> "net:monitor";
|
||||
"svc:node-red" -> "net:traefik";
|
||||
"svc:passbolt-db" -> "net:passbolt";
|
||||
"svc:passbolt-webapp" -> "net:passbolt";
|
||||
"svc:passbolt-webapp" -> "net:traefik";
|
||||
"svc:pihole-exporter" -> "net:monitor";
|
||||
"svc:portainer" -> "net:traefik";
|
||||
"svc:prometheus" -> "net:monitor";
|
||||
"svc:prometheus" -> "net:traefik";
|
||||
"svc:searxng-webapp" -> "net:traefik";
|
||||
"svc:telegraf" -> "net:monitor";
|
||||
"svc:traefik" -> "net:traefik";
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: Compose Pages: 1 -->
|
||||
<svg width="334pt" height="1502pt"
|
||||
viewBox="0.00 0.00 334.49 1502.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1498)">
|
||||
<title>Compose</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1498 330.49,-1498 330.49,4 -4,4"/>
|
||||
<!-- svc:authelia -->
|
||||
<g id="node1" class="node">
|
||||
<title>svc:authelia</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="126,-738 54,-738 54,-702 126,-702 126,-738"/>
|
||||
<text text-anchor="middle" x="90" y="-716.3" font-family="Helvetica,sans-Serif" font-size="14.00">authelia</text>
|
||||
</g>
|
||||
<!-- net:traefik -->
|
||||
<g id="node33" class="node">
|
||||
<title>net:traefik</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-774" rx="40.09" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-770.3" font-family="Helvetica,sans-Serif" font-size="14.00">traefik</text>
|
||||
</g>
|
||||
<!-- svc:authelia->net:traefik -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>svc:authelia->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M126.41,-730.67C155.5,-739.43 196.8,-751.87 227.69,-761.18"/>
|
||||
<polygon fill="black" stroke="black" points="226.74,-764.55 237.32,-764.08 228.76,-757.85 226.74,-764.55"/>
|
||||
</g>
|
||||
<!-- svc:crowdsec -->
|
||||
<g id="node2" class="node">
|
||||
<title>svc:crowdsec</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="130.5,-684 49.5,-684 49.5,-648 130.5,-648 130.5,-684"/>
|
||||
<text text-anchor="middle" x="90" y="-662.3" font-family="Helvetica,sans-Serif" font-size="14.00">crowdsec</text>
|
||||
</g>
|
||||
<!-- svc:crowdsec->net:traefik -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>svc:crowdsec->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M130.61,-674.06C146.59,-678.3 164.81,-684.44 180,-693 206.25,-707.78 231.35,-731.35 248.38,-749.26"/>
|
||||
<polygon fill="black" stroke="black" points="246.24,-752.1 255.62,-757.03 251.37,-747.33 246.24,-752.1"/>
|
||||
</g>
|
||||
<!-- svc:docker-socket-proxy -->
|
||||
<g id="node3" class="node">
|
||||
<title>svc:docker-socket-proxy</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="167.5,-954 12.5,-954 12.5,-918 167.5,-918 167.5,-954"/>
|
||||
<text text-anchor="middle" x="90" y="-932.3" font-family="Helvetica,sans-Serif" font-size="14.00">docker-socket-proxy</text>
|
||||
</g>
|
||||
<!-- net:monitor -->
|
||||
<g id="node30" class="node">
|
||||
<title>net:monitor</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-1206" rx="46.29" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-1202.3" font-family="Helvetica,sans-Serif" font-size="14.00">monitor</text>
|
||||
</g>
|
||||
<!-- svc:docker-socket-proxy->net:monitor -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>svc:docker-socket-proxy->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M167.53,-953.71C172.02,-956.38 176.24,-959.46 180,-963 242.3,-1021.6 261.83,-1127.12 267.77,-1177.6"/>
|
||||
<polygon fill="black" stroke="black" points="264.31,-1178.21 268.86,-1187.78 271.27,-1177.46 264.31,-1178.21"/>
|
||||
</g>
|
||||
<!-- svc:docker-socket-proxy->net:traefik -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>svc:docker-socket-proxy->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M165.31,-917.9C170.5,-915.32 175.46,-912.37 180,-909 218.01,-880.82 244.97,-831.56 259.03,-800.98"/>
|
||||
<polygon fill="black" stroke="black" points="262.27,-802.31 263.14,-791.76 255.87,-799.46 262.27,-802.31"/>
|
||||
</g>
|
||||
<!-- svc:docker-update-exporter -->
|
||||
<g id="node4" class="node">
|
||||
<title>svc:docker-update-exporter</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="180,-1494 0,-1494 0,-1458 180,-1458 180,-1494"/>
|
||||
<text text-anchor="middle" x="90" y="-1472.3" font-family="Helvetica,sans-Serif" font-size="14.00">docker-update-exporter</text>
|
||||
</g>
|
||||
<!-- svc:docker-update-exporter->net:monitor -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>svc:docker-update-exporter->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M168.37,-1457.79C172.55,-1455.23 176.47,-1452.32 180,-1449 242.3,-1390.4 261.83,-1284.88 267.77,-1234.4"/>
|
||||
<polygon fill="black" stroke="black" points="271.27,-1234.54 268.86,-1224.22 264.31,-1233.79 271.27,-1234.54"/>
|
||||
</g>
|
||||
<!-- svc:error-pages -->
|
||||
<g id="node5" class="node">
|
||||
<title>svc:error-pages</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="137.5,-630 42.5,-630 42.5,-594 137.5,-594 137.5,-630"/>
|
||||
<text text-anchor="middle" x="90" y="-608.3" font-family="Helvetica,sans-Serif" font-size="14.00">error-pages</text>
|
||||
</g>
|
||||
<!-- svc:error-pages->net:traefik -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>svc:error-pages->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M137.71,-619.76C152.25,-623.79 167.68,-629.86 180,-639 218.01,-667.18 244.97,-716.44 259.03,-747.02"/>
|
||||
<polygon fill="black" stroke="black" points="255.87,-748.54 263.14,-756.24 262.27,-745.69 255.87,-748.54"/>
|
||||
</g>
|
||||
<!-- svc:gitea -->
|
||||
<g id="node6" class="node">
|
||||
<title>svc:gitea</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="117,-576 63,-576 63,-540 117,-540 117,-576"/>
|
||||
<text text-anchor="middle" x="90" y="-554.3" font-family="Helvetica,sans-Serif" font-size="14.00">gitea</text>
|
||||
</g>
|
||||
<!-- svc:gitea->net:traefik -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>svc:gitea->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M117.07,-560.28C136.41,-563.2 162.37,-569.85 180,-585 229.98,-627.95 254.42,-704.8 264.44,-746.04"/>
|
||||
<polygon fill="black" stroke="black" points="261.07,-747 266.73,-755.95 267.88,-745.42 261.07,-747"/>
|
||||
</g>
|
||||
<!-- svc:gitea-runner -->
|
||||
<g id="node7" class="node">
|
||||
<title>svc:gitea-runner</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="142,-522 38,-522 38,-486 142,-486 142,-522"/>
|
||||
<text text-anchor="middle" x="90" y="-500.3" font-family="Helvetica,sans-Serif" font-size="14.00">gitea-runner</text>
|
||||
</g>
|
||||
<!-- svc:gitea-runner->net:traefik -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>svc:gitea-runner->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M142.31,-511.04C155.9,-515.03 169.65,-521.26 180,-531 242.3,-589.6 261.83,-695.12 267.77,-745.6"/>
|
||||
<polygon fill="black" stroke="black" points="264.31,-746.21 268.86,-755.78 271.27,-745.46 264.31,-746.21"/>
|
||||
</g>
|
||||
<!-- svc:gotify -->
|
||||
<g id="node8" class="node">
|
||||
<title>svc:gotify</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="118,-468 62,-468 62,-432 118,-432 118,-468"/>
|
||||
<text text-anchor="middle" x="90" y="-446.3" font-family="Helvetica,sans-Serif" font-size="14.00">gotify</text>
|
||||
</g>
|
||||
<!-- svc:gotify->net:traefik -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>svc:gotify->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M118.17,-451.6C137.81,-454.16 163.67,-460.68 180,-477 254.99,-551.96 268.02,-687.13 270.03,-745.69"/>
|
||||
<polygon fill="black" stroke="black" points="266.53,-745.79 270.29,-755.69 273.53,-745.61 266.53,-745.79"/>
|
||||
</g>
|
||||
<!-- svc:grafana -->
|
||||
<g id="node9" class="node">
|
||||
<title>svc:grafana</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="125.5,-1278 54.5,-1278 54.5,-1242 125.5,-1242 125.5,-1278"/>
|
||||
<text text-anchor="middle" x="90" y="-1256.3" font-family="Helvetica,sans-Serif" font-size="14.00">grafana</text>
|
||||
</g>
|
||||
<!-- svc:grafana->net:monitor -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>svc:grafana->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M125.56,-1249.59C153.74,-1241.1 193.78,-1229.04 224.6,-1219.75"/>
|
||||
<polygon fill="black" stroke="black" points="225.69,-1223.08 234.26,-1216.84 223.67,-1216.38 225.69,-1223.08"/>
|
||||
</g>
|
||||
<!-- svc:grafana->net:traefik -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>svc:grafana->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M125.54,-1257.93C144.23,-1254.97 166.25,-1248.21 180,-1233 238.17,-1168.67 262.1,-892.17 268.43,-802.32"/>
|
||||
<polygon fill="black" stroke="black" points="271.93,-802.3 269.13,-792.08 264.95,-801.82 271.93,-802.3"/>
|
||||
</g>
|
||||
<!-- svc:gramps-redis -->
|
||||
<g id="node10" class="node">
|
||||
<title>svc:gramps-redis</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="144.5,-360 35.5,-360 35.5,-324 144.5,-324 144.5,-360"/>
|
||||
<text text-anchor="middle" x="90" y="-338.3" font-family="Helvetica,sans-Serif" font-size="14.00">gramps-redis</text>
|
||||
</g>
|
||||
<!-- net:gramps -->
|
||||
<g id="node29" class="node">
|
||||
<title>net:gramps</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-342" rx="45.49" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-338.3" font-family="Helvetica,sans-Serif" font-size="14.00">gramps</text>
|
||||
</g>
|
||||
<!-- svc:gramps-redis->net:gramps -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>svc:gramps-redis->net:gramps</title>
|
||||
<path fill="none" stroke="black" d="M144.78,-342C167.14,-342 193.05,-342 215.51,-342"/>
|
||||
<polygon fill="black" stroke="black" points="215.56,-345.5 225.56,-342 215.56,-338.5 215.56,-345.5"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb -->
|
||||
<g id="node11" class="node">
|
||||
<title>svc:grampsweb</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="139,-414 41,-414 41,-378 139,-378 139,-414"/>
|
||||
<text text-anchor="middle" x="90" y="-392.3" font-family="Helvetica,sans-Serif" font-size="14.00">grampsweb</text>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:gramps -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>svc:grampsweb->net:gramps</title>
|
||||
<path fill="none" stroke="black" d="M139.03,-381.53C165.7,-373.5 198.68,-363.56 224.9,-355.66"/>
|
||||
<polygon fill="black" stroke="black" points="226.03,-358.98 234.59,-352.74 224.01,-352.27 226.03,-358.98"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:traefik -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>svc:grampsweb->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M139.27,-401.34C154.08,-405.21 169.28,-411.81 180,-423 225.12,-470.09 256.34,-671.08 266.59,-745.84"/>
|
||||
<polygon fill="black" stroke="black" points="263.14,-746.48 267.95,-755.92 270.08,-745.55 263.14,-746.48"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb_celery -->
|
||||
<g id="node12" class="node">
|
||||
<title>svc:grampsweb_celery</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="163.5,-306 16.5,-306 16.5,-270 163.5,-270 163.5,-306"/>
|
||||
<text text-anchor="middle" x="90" y="-284.3" font-family="Helvetica,sans-Serif" font-size="14.00">grampsweb_celery</text>
|
||||
</g>
|
||||
<!-- svc:grampsweb_celery->net:gramps -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>svc:grampsweb_celery->net:gramps</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-306.13C175.28,-313.39 202.57,-321.61 224.94,-328.35"/>
|
||||
<polygon fill="black" stroke="black" points="223.94,-331.71 234.52,-331.24 225.96,-325 223.94,-331.71"/>
|
||||
</g>
|
||||
<!-- svc:influxdb -->
|
||||
<g id="node13" class="node">
|
||||
<title>svc:influxdb</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="127,-1224 53,-1224 53,-1188 127,-1188 127,-1224"/>
|
||||
<text text-anchor="middle" x="90" y="-1202.3" font-family="Helvetica,sans-Serif" font-size="14.00">influxdb</text>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:monitor -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>svc:influxdb->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M127.27,-1206C152.33,-1206 186.13,-1206 214.57,-1206"/>
|
||||
<polygon fill="black" stroke="black" points="214.79,-1209.5 224.79,-1206 214.79,-1202.5 214.79,-1209.5"/>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:traefik -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>svc:influxdb->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M127.17,-1203.5C145.42,-1200.36 166.5,-1193.56 180,-1179 231.57,-1123.4 259.36,-885.3 267.6,-802.51"/>
|
||||
<polygon fill="black" stroke="black" points="271.1,-802.62 268.59,-792.33 264.14,-801.94 271.1,-802.62"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma -->
|
||||
<g id="node14" class="node">
|
||||
<title>svc:monitor-kuma</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="146.5,-1170 33.5,-1170 33.5,-1134 146.5,-1134 146.5,-1170"/>
|
||||
<text text-anchor="middle" x="90" y="-1148.3" font-family="Helvetica,sans-Serif" font-size="14.00">monitor-kuma</text>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:monitor -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>svc:monitor-kuma->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M146.73,-1168.79C171.73,-1176.32 200.85,-1185.1 224.54,-1192.23"/>
|
||||
<polygon fill="black" stroke="black" points="223.75,-1195.65 234.33,-1195.18 225.77,-1188.94 223.75,-1195.65"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:traefik -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>svc:monitor-kuma->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M146.69,-1144.49C159.02,-1140.46 171.06,-1134.33 180,-1125 225.12,-1077.91 256.34,-876.92 266.59,-802.16"/>
|
||||
<polygon fill="black" stroke="black" points="270.08,-802.45 267.95,-792.08 263.14,-801.52 270.08,-802.45"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge -->
|
||||
<g id="node15" class="node">
|
||||
<title>svc:mtls-bridge</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="138.5,-1116 41.5,-1116 41.5,-1080 138.5,-1080 138.5,-1116"/>
|
||||
<text text-anchor="middle" x="90" y="-1094.3" font-family="Helvetica,sans-Serif" font-size="14.00">mtls-bridge</text>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:monitor -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>svc:mtls-bridge->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M138.76,-1108.34C152.58,-1112.41 167.34,-1117.87 180,-1125 206.25,-1139.78 231.35,-1163.35 248.38,-1181.26"/>
|
||||
<polygon fill="black" stroke="black" points="246.24,-1184.1 255.62,-1189.03 251.37,-1179.33 246.24,-1184.1"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:traefik -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>svc:mtls-bridge->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M138.53,-1092.51C153.46,-1088.63 168.92,-1082.07 180,-1071 254.99,-996.04 268.02,-860.87 270.03,-802.31"/>
|
||||
<polygon fill="black" stroke="black" points="273.53,-802.39 270.29,-792.31 266.53,-802.21 273.53,-802.39"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-db -->
|
||||
<g id="node16" class="node">
|
||||
<title>svc:nextcloud-db</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="144,-90 36,-90 36,-54 144,-54 144,-90"/>
|
||||
<text text-anchor="middle" x="90" y="-68.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud-db</text>
|
||||
</g>
|
||||
<!-- net:nextcloud -->
|
||||
<g id="node31" class="node">
|
||||
<title>net:nextcloud</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-180" rx="55.49" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-176.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-db->net:nextcloud -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>svc:nextcloud-db->net:nextcloud</title>
|
||||
<path fill="none" stroke="black" d="M144.33,-84.04C156.48,-87.9 169.03,-92.82 180,-99 206.09,-113.7 231.04,-137.06 248.07,-154.93"/>
|
||||
<polygon fill="black" stroke="black" points="245.93,-157.77 255.31,-162.7 251.06,-153 245.93,-157.77"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-redis -->
|
||||
<g id="node17" class="node">
|
||||
<title>svc:nextcloud-redis</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="152,-198 28,-198 28,-162 152,-162 152,-198"/>
|
||||
<text text-anchor="middle" x="90" y="-176.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud-redis</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-redis->net:nextcloud -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>svc:nextcloud-redis->net:nextcloud</title>
|
||||
<path fill="none" stroke="black" d="M152.18,-180C169.48,-180 188.35,-180 205.83,-180"/>
|
||||
<polygon fill="black" stroke="black" points="205.94,-183.5 215.94,-180 205.94,-176.5 205.94,-183.5"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp -->
|
||||
<g id="node18" class="node">
|
||||
<title>svc:nextcloud-webapp</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="162.5,-252 17.5,-252 17.5,-216 162.5,-216 162.5,-252"/>
|
||||
<text text-anchor="middle" x="90" y="-230.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud-webapp</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:nextcloud -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:nextcloud</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-215.87C173.57,-209.12 198.72,-201.55 220.11,-195.1"/>
|
||||
<polygon fill="black" stroke="black" points="221.4,-198.37 229.97,-192.13 219.38,-191.67 221.4,-198.37"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:traefik -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M162.89,-247.71C169.31,-251.2 175.18,-255.56 180,-261 212.66,-297.85 255.07,-643.36 267,-745.62"/>
|
||||
<polygon fill="black" stroke="black" points="263.55,-746.27 268.18,-755.8 270.51,-745.47 263.55,-746.27"/>
|
||||
</g>
|
||||
<!-- svc:node-exporter -->
|
||||
<g id="node19" class="node">
|
||||
<title>svc:node-exporter</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="148,-1440 32,-1440 32,-1404 148,-1404 148,-1440"/>
|
||||
<text text-anchor="middle" x="90" y="-1418.3" font-family="Helvetica,sans-Serif" font-size="14.00">node-exporter</text>
|
||||
</g>
|
||||
<!-- svc:node-exporter->net:monitor -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>svc:node-exporter->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M148.25,-1412.27C159.68,-1408.33 170.94,-1402.79 180,-1395 229.98,-1352.05 254.42,-1275.2 264.44,-1233.96"/>
|
||||
<polygon fill="black" stroke="black" points="267.88,-1234.58 266.73,-1224.05 261.07,-1233 267.88,-1234.58"/>
|
||||
</g>
|
||||
<!-- svc:node-red -->
|
||||
<g id="node20" class="node">
|
||||
<title>svc:node-red</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="129.5,-1062 50.5,-1062 50.5,-1026 129.5,-1026 129.5,-1062"/>
|
||||
<text text-anchor="middle" x="90" y="-1040.3" font-family="Helvetica,sans-Serif" font-size="14.00">node-red</text>
|
||||
</g>
|
||||
<!-- svc:node-red->net:monitor -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>svc:node-red->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M129.57,-1049.69C146.29,-1053.6 165.34,-1060.13 180,-1071 218.01,-1099.18 244.97,-1148.44 259.03,-1179.02"/>
|
||||
<polygon fill="black" stroke="black" points="255.87,-1180.54 263.14,-1188.24 262.27,-1177.69 255.87,-1180.54"/>
|
||||
</g>
|
||||
<!-- svc:node-red->net:traefik -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>svc:node-red->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M129.95,-1040.03C147.14,-1036.46 166.48,-1029.72 180,-1017 242.3,-958.4 261.83,-852.88 267.77,-802.4"/>
|
||||
<polygon fill="black" stroke="black" points="271.27,-802.54 268.86,-792.22 264.31,-801.79 271.27,-802.54"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-db -->
|
||||
<g id="node21" class="node">
|
||||
<title>svc:passbolt-db</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="139,-36 41,-36 41,0 139,0 139,-36"/>
|
||||
<text text-anchor="middle" x="90" y="-14.3" font-family="Helvetica,sans-Serif" font-size="14.00">passbolt-db</text>
|
||||
</g>
|
||||
<!-- net:passbolt -->
|
||||
<g id="node32" class="node">
|
||||
<title>net:passbolt</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-72" rx="48.99" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-68.3" font-family="Helvetica,sans-Serif" font-size="14.00">passbolt</text>
|
||||
</g>
|
||||
<!-- svc:passbolt-db->net:passbolt -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>svc:passbolt-db->net:passbolt</title>
|
||||
<path fill="none" stroke="black" d="M139.03,-32.47C165.15,-40.34 197.32,-50.03 223.27,-57.85"/>
|
||||
<polygon fill="black" stroke="black" points="222.3,-61.21 232.88,-60.74 224.32,-54.51 222.3,-61.21"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp -->
|
||||
<g id="node22" class="node">
|
||||
<title>svc:passbolt-webapp</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="157.5,-144 22.5,-144 22.5,-108 157.5,-108 157.5,-144"/>
|
||||
<text text-anchor="middle" x="90" y="-122.3" font-family="Helvetica,sans-Serif" font-size="14.00">passbolt-webapp</text>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:passbolt -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>svc:passbolt-webapp->net:passbolt</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-107.87C174.64,-100.8 201.13,-92.82 223.16,-86.19"/>
|
||||
<polygon fill="black" stroke="black" points="224.37,-89.48 232.94,-83.24 222.35,-82.77 224.37,-89.48"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:traefik -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>svc:passbolt-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M157.85,-136.93C166.26,-140.81 174,-146.02 180,-153 189.88,-164.49 250.79,-625.33 266.53,-745.56"/>
|
||||
<polygon fill="black" stroke="black" points="263.1,-746.33 267.87,-755.8 270.04,-745.43 263.1,-746.33"/>
|
||||
</g>
|
||||
<!-- svc:pihole-exporter -->
|
||||
<g id="node23" class="node">
|
||||
<title>svc:pihole-exporter</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="151.5,-1386 28.5,-1386 28.5,-1350 151.5,-1350 151.5,-1386"/>
|
||||
<text text-anchor="middle" x="90" y="-1364.3" font-family="Helvetica,sans-Serif" font-size="14.00">pihole-exporter</text>
|
||||
</g>
|
||||
<!-- svc:pihole-exporter->net:monitor -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>svc:pihole-exporter->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M151.62,-1355.78C161.68,-1352.07 171.57,-1347.25 180,-1341 218.01,-1312.82 244.97,-1263.56 259.03,-1232.98"/>
|
||||
<polygon fill="black" stroke="black" points="262.27,-1234.31 263.14,-1223.76 255.87,-1231.46 262.27,-1234.31"/>
|
||||
</g>
|
||||
<!-- svc:portainer -->
|
||||
<g id="node24" class="node">
|
||||
<title>svc:portainer</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="130,-900 50,-900 50,-864 130,-864 130,-900"/>
|
||||
<text text-anchor="middle" x="90" y="-878.3" font-family="Helvetica,sans-Serif" font-size="14.00">portainer</text>
|
||||
</g>
|
||||
<!-- svc:portainer->net:traefik -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>svc:portainer->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M130.17,-874.06C146.25,-869.82 164.67,-863.64 180,-855 206.25,-840.22 231.35,-816.65 248.38,-798.74"/>
|
||||
<polygon fill="black" stroke="black" points="251.37,-800.67 255.62,-790.97 246.24,-795.9 251.37,-800.67"/>
|
||||
</g>
|
||||
<!-- svc:prometheus -->
|
||||
<g id="node25" class="node">
|
||||
<title>svc:prometheus</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="140,-1008 40,-1008 40,-972 140,-972 140,-1008"/>
|
||||
<text text-anchor="middle" x="90" y="-986.3" font-family="Helvetica,sans-Serif" font-size="14.00">prometheus</text>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:monitor -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>svc:prometheus->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M140.37,-997.26C154.39,-1001.24 168.86,-1007.42 180,-1017 229.98,-1059.95 254.42,-1136.8 264.44,-1178.04"/>
|
||||
<polygon fill="black" stroke="black" points="261.07,-1179 266.73,-1187.95 267.88,-1177.42 261.07,-1179"/>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:traefik -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>svc:prometheus->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M140.37,-982.74C154.39,-978.76 168.86,-972.58 180,-963 229.98,-920.05 254.42,-843.2 264.44,-801.96"/>
|
||||
<polygon fill="black" stroke="black" points="267.88,-802.58 266.73,-792.05 261.07,-801 267.88,-802.58"/>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp -->
|
||||
<g id="node26" class="node">
|
||||
<title>svc:searxng-webapp</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="156,-846 24,-846 24,-810 156,-810 156,-846"/>
|
||||
<text text-anchor="middle" x="90" y="-824.3" font-family="Helvetica,sans-Serif" font-size="14.00">searxng-webapp</text>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp->net:traefik -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>svc:searxng-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-809.87C176.26,-802.32 204.79,-793.72 227.63,-786.84"/>
|
||||
<polygon fill="black" stroke="black" points="228.81,-790.14 237.37,-783.9 226.79,-783.44 228.81,-790.14"/>
|
||||
</g>
|
||||
<!-- svc:telegraf -->
|
||||
<g id="node27" class="node">
|
||||
<title>svc:telegraf</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="125.5,-1332 54.5,-1332 54.5,-1296 125.5,-1296 125.5,-1332"/>
|
||||
<text text-anchor="middle" x="90" y="-1310.3" font-family="Helvetica,sans-Serif" font-size="14.00">telegraf</text>
|
||||
</g>
|
||||
<!-- svc:telegraf->net:monitor -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>svc:telegraf->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M125.8,-1307.18C142.85,-1302.93 163.26,-1296.43 180,-1287 206.25,-1272.22 231.35,-1248.65 248.38,-1230.74"/>
|
||||
<polygon fill="black" stroke="black" points="251.37,-1232.67 255.62,-1222.97 246.24,-1227.9 251.37,-1232.67"/>
|
||||
</g>
|
||||
<!-- svc:traefik -->
|
||||
<g id="node28" class="node">
|
||||
<title>svc:traefik</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="121,-792 59,-792 59,-756 121,-756 121,-792"/>
|
||||
<text text-anchor="middle" x="90" y="-770.3" font-family="Helvetica,sans-Serif" font-size="14.00">traefik</text>
|
||||
</g>
|
||||
<!-- svc:traefik->net:traefik -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>svc:traefik->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M121,-774C148.16,-774 188.69,-774 220.66,-774"/>
|
||||
<polygon fill="black" stroke="black" points="220.71,-777.5 230.71,-774 220.71,-770.5 220.71,-777.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,118 @@
|
||||
digraph DockerTraefikDynu {
|
||||
graph [rankdir=LR, compound=true, splines=polyline, nodesep=0.9, ranksep=1.6, fontname="Helvetica", concentrate=true, newrank=true];
|
||||
node [fontname="Helvetica", fontsize=11, style="rounded,filled"];
|
||||
edge [fontname="Helvetica", fontsize=9, color="#334155"];
|
||||
"dynu" [label="Dynu / Public DNS", shape=box, fillcolor="#fde68a"];
|
||||
"svc:traefik" [label="Traefik", shape=box, fillcolor="#bfdbfe"];
|
||||
"dynu" -> "svc:traefik" [penwidth=1.6];
|
||||
"svc:authelia" [label="authelia
|
||||
[TLS]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:authelia" [penwidth=1.4];
|
||||
"dns:auth.<domain>" [label="auth.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:auth.<domain>" -> "dynu";
|
||||
"svc:gitea" [label="gitea
|
||||
:3000
|
||||
[TLS]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:gitea" [penwidth=1.4];
|
||||
"dns:gitea.<domain>" [label="gitea.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:gitea.<domain>" -> "dynu";
|
||||
"svc:gotify" [label="gotify
|
||||
:80", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:gotify" [penwidth=1.4];
|
||||
"dns:gotify.<domain>" [label="gotify.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:gotify.<domain>" -> "dynu";
|
||||
"svc:grafana" [label="grafana
|
||||
:3000", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:grafana" [penwidth=1.4];
|
||||
"dns:grafana.<domain>" [label="grafana.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:grafana.<domain>" -> "dynu";
|
||||
"svc:grampsweb" [label="grampsweb", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:grampsweb" [penwidth=1.4];
|
||||
"dns:familytree.<domain>" [label="familytree.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:familytree.<domain>" -> "dynu";
|
||||
"svc:influxdb" [label="influxdb
|
||||
:8086
|
||||
[authelia]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:influxdb" [penwidth=1.4];
|
||||
"dns:influxdb.<domain>" [label="influxdb.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:influxdb.<domain>" -> "dynu";
|
||||
"svc:monitor-kuma" [label="monitor-kuma
|
||||
[TLS]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:monitor-kuma" [penwidth=1.4];
|
||||
"dns:monitor-kuma.<domain>" [label="monitor-kuma.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:monitor-kuma.<domain>" -> "dynu";
|
||||
"svc:mtls-bridge" [label="mtls-bridge
|
||||
:8080
|
||||
[mTLS]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:mtls-bridge" [penwidth=1.4];
|
||||
"dns:mtls-bridge.<domain>" [label="mtls-bridge.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:mtls-bridge.<domain>" -> "dynu";
|
||||
"svc:nextcloud-webapp" [label="nextcloud-webapp", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:nextcloud-webapp" [penwidth=1.4];
|
||||
"dns:nextcloud.<domain>" [label="nextcloud.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:nextcloud.<domain>" -> "dynu";
|
||||
"svc:node-red" [label="node-red
|
||||
:1880
|
||||
[authelia]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:node-red" [penwidth=1.4];
|
||||
"dns:node-red.<domain>" [label="node-red.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:node-red.<domain>" -> "dynu";
|
||||
"svc:passbolt-webapp" [label="passbolt-webapp", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:passbolt-webapp" [penwidth=1.4];
|
||||
"dns:passbolt.<domain>" [label="passbolt.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:passbolt.<domain>" -> "dynu";
|
||||
"svc:portainer" [label="portainer
|
||||
:9000
|
||||
[TLS]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:portainer" [penwidth=1.4];
|
||||
"dns:portainer.<domain>" [label="portainer.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:portainer.<domain>" -> "dynu";
|
||||
"svc:prometheus" [label="prometheus
|
||||
:9090
|
||||
[authelia]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:prometheus" [penwidth=1.4];
|
||||
"dns:prometheus.<domain>" [label="prometheus.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:prometheus.<domain>" -> "dynu";
|
||||
"svc:searxng-webapp" [label="searxng-webapp", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:searxng-webapp" [penwidth=1.4];
|
||||
"dns:searxng.<domain>" [label="searxng.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:searxng.<domain>" -> "dynu";
|
||||
"svc:traefik" [label="traefik
|
||||
[authelia]", shape=box, fillcolor="#dcfce7"];
|
||||
"svc:traefik" -> "svc:traefik" [penwidth=1.4];
|
||||
"dns:traefik.<domain>" [label="traefik.<domain>", shape=note, fillcolor="#fef3c7"];
|
||||
"dns:traefik.<domain>" -> "dynu";
|
||||
{ rank=same; "dns:auth.<domain>"; "dns:familytree.<domain>"; "dns:gitea.<domain>"; "dns:gotify.<domain>"; "dns:grafana.<domain>"; "dns:influxdb.<domain>"; "dns:monitor-kuma.<domain>"; "dns:mtls-bridge.<domain>"; "dns:nextcloud.<domain>"; "dns:node-red.<domain>"; "dns:passbolt.<domain>"; "dns:portainer.<domain>"; "dns:prometheus.<domain>"; "dns:searxng.<domain>"; "dns:traefik.<domain>"; }
|
||||
subgraph "cluster_networks" {
|
||||
label="Docker backend networks"; style="rounded,dashed"; color="#d1d5db";
|
||||
"net:gramps" [label="gramps", shape=ellipse, fillcolor="#f8fafc"];
|
||||
"net:monitor" [label="monitor", shape=ellipse, fillcolor="#f8fafc"];
|
||||
"net:nextcloud" [label="nextcloud", shape=ellipse, fillcolor="#f8fafc"];
|
||||
"net:passbolt" [label="passbolt", shape=ellipse, fillcolor="#f8fafc"];
|
||||
"net:traefik" [label="traefik", shape=ellipse, fillcolor="#f8fafc"];
|
||||
}
|
||||
"svc:authelia" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:gitea" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:gotify" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:grafana" -> "net:monitor" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:grafana" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:grampsweb" -> "net:gramps" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:grampsweb" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:influxdb" -> "net:monitor" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:influxdb" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:monitor-kuma" -> "net:monitor" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:monitor-kuma" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:mtls-bridge" -> "net:monitor" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:mtls-bridge" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:nextcloud-webapp" -> "net:nextcloud" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:nextcloud-webapp" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:node-red" -> "net:monitor" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:node-red" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:passbolt-webapp" -> "net:passbolt" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:passbolt-webapp" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:portainer" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:prometheus" -> "net:monitor" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:prometheus" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:searxng-webapp" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
"svc:traefik" -> "net:traefik" [style=dashed, color="#94a3b8", arrowsize=0.7];
|
||||
}
|
||||
@@ -0,0 +1,611 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: DockerTraefikDynu Pages: 1 -->
|
||||
<svg width="1113pt" height="1536pt"
|
||||
viewBox="0.00 0.00 1113.39 1536.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1532)">
|
||||
<title>DockerTraefikDynu</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1532 1109.39,-1532 1109.39,4 -4,4"/>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_networks</title>
|
||||
<path fill="none" stroke="#d1d5db" stroke-dasharray="5,2" d="M922,-650C922,-650 1093.39,-650 1093.39,-650 1099.39,-650 1105.39,-656 1105.39,-662 1105.39,-662 1105.39,-1117 1105.39,-1117 1105.39,-1123 1099.39,-1129 1093.39,-1129 1093.39,-1129 922,-1129 922,-1129 916,-1129 910,-1123 910,-1117 910,-1117 910,-662 910,-662 910,-656 916,-650 922,-650"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-1113.8" font-family="Helvetica,sans-Serif" font-size="14.00">Docker backend networks</text>
|
||||
</g>
|
||||
<!-- dynu -->
|
||||
<g id="node1" class="node">
|
||||
<title>dynu</title>
|
||||
<path fill="#fde68a" stroke="black" d="M374,-789C374,-789 282,-789 282,-789 276,-789 270,-783 270,-777 270,-777 270,-765 270,-765 270,-759 276,-753 282,-753 282,-753 374,-753 374,-753 380,-753 386,-759 386,-765 386,-765 386,-777 386,-777 386,-783 380,-789 374,-789"/>
|
||||
<text text-anchor="middle" x="328" y="-768.2" font-family="Helvetica,sans-Serif" font-size="11.00">Dynu / Public DNS</text>
|
||||
</g>
|
||||
<!-- svc:traefik -->
|
||||
<g id="node2" class="node">
|
||||
<title>svc:traefik</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M559,-789C559,-789 513,-789 513,-789 507,-789 501,-783 501,-777 501,-777 501,-765 501,-765 501,-759 507,-753 513,-753 513,-753 559,-753 559,-753 565,-753 571,-759 571,-765 571,-765 571,-777 571,-777 571,-783 565,-789 559,-789"/>
|
||||
<text text-anchor="middle" x="536" y="-774.2" font-family="Helvetica,sans-Serif" font-size="11.00">traefik</text>
|
||||
<text text-anchor="middle" x="536" y="-762.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- dynu->svc:traefik -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>dynu->svc:traefik</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.6" d="M386.11,-771C419.13,-771 460.06,-771 490.64,-771"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.6" points="491,-774.5 501,-771 491,-767.5 491,-774.5"/>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:traefik -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>svc:traefik->svc:traefik</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M511.86,-789.35C485.91,-817.82 493.95,-854 536,-854 574.1,-854 584.29,-824.29 566.55,-797.52"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="569.07,-795.06 560.14,-789.35 563.55,-799.38 569.07,-795.06"/>
|
||||
</g>
|
||||
<!-- svc:authelia -->
|
||||
<g id="node3" class="node">
|
||||
<title>svc:authelia</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M763,-789C763,-789 726,-789 726,-789 720,-789 714,-783 714,-777 714,-777 714,-765 714,-765 714,-759 720,-753 726,-753 726,-753 763,-753 763,-753 769,-753 775,-759 775,-765 775,-765 775,-777 775,-777 775,-783 769,-789 763,-789"/>
|
||||
<text text-anchor="middle" x="744.5" y="-774.2" font-family="Helvetica,sans-Serif" font-size="11.00">authelia</text>
|
||||
<text text-anchor="middle" x="744.5" y="-762.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:authelia -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>svc:traefik->svc:authelia</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M571.1,-771C607.43,-771 664.91,-771 703.39,-771"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="703.71,-774.5 713.71,-771 703.71,-767.5 703.71,-774.5"/>
|
||||
</g>
|
||||
<!-- svc:gitea -->
|
||||
<g id="node5" class="node">
|
||||
<title>svc:gitea</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M759.5,-688C759.5,-688 729.5,-688 729.5,-688 723.5,-688 717.5,-682 717.5,-676 717.5,-676 717.5,-656 717.5,-656 717.5,-650 723.5,-644 729.5,-644 729.5,-644 759.5,-644 759.5,-644 765.5,-644 771.5,-650 771.5,-656 771.5,-656 771.5,-676 771.5,-676 771.5,-682 765.5,-688 759.5,-688"/>
|
||||
<text text-anchor="middle" x="744.5" y="-675.2" font-family="Helvetica,sans-Serif" font-size="11.00">gitea</text>
|
||||
<text text-anchor="middle" x="744.5" y="-663.2" font-family="Helvetica,sans-Serif" font-size="11.00">:3000</text>
|
||||
<text text-anchor="middle" x="744.5" y="-651.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:gitea -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>svc:traefik->svc:gitea</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M571.1,-753.66C608.91,-734.44 669.61,-703.57 707.98,-684.06"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="709.89,-687.01 717.22,-679.36 706.72,-680.77 709.89,-687.01"/>
|
||||
</g>
|
||||
<!-- svc:gotify -->
|
||||
<g id="node7" class="node">
|
||||
<title>svc:gotify</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M759.5,-579C759.5,-579 729.5,-579 729.5,-579 723.5,-579 717.5,-573 717.5,-567 717.5,-567 717.5,-555 717.5,-555 717.5,-549 723.5,-543 729.5,-543 729.5,-543 759.5,-543 759.5,-543 765.5,-543 771.5,-549 771.5,-555 771.5,-555 771.5,-567 771.5,-567 771.5,-573 765.5,-579 759.5,-579"/>
|
||||
<text text-anchor="middle" x="744.5" y="-564.2" font-family="Helvetica,sans-Serif" font-size="11.00">gotify</text>
|
||||
<text text-anchor="middle" x="744.5" y="-552.2" font-family="Helvetica,sans-Serif" font-size="11.00">:80</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:gotify -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>svc:traefik->svc:gotify</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M553.8,-752.96C592.77,-711.11 686,-611 686,-611 686,-611 700.43,-598.45 714.83,-585.93"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="717.49,-588.25 722.74,-579.05 712.9,-582.97 717.49,-588.25"/>
|
||||
</g>
|
||||
<!-- svc:grafana -->
|
||||
<g id="node9" class="node">
|
||||
<title>svc:grafana</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M762,-999C762,-999 727,-999 727,-999 721,-999 715,-993 715,-987 715,-987 715,-975 715,-975 715,-969 721,-963 727,-963 727,-963 762,-963 762,-963 768,-963 774,-969 774,-975 774,-975 774,-987 774,-987 774,-993 768,-999 762,-999"/>
|
||||
<text text-anchor="middle" x="744.5" y="-984.2" font-family="Helvetica,sans-Serif" font-size="11.00">grafana</text>
|
||||
<text text-anchor="middle" x="744.5" y="-972.2" font-family="Helvetica,sans-Serif" font-size="11.00">:3000</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:grafana -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>svc:traefik->svc:grafana</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M553.8,-789.04C592.77,-830.89 686,-931 686,-931 686,-931 700.43,-943.55 714.83,-956.07"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="712.9,-959.03 722.74,-962.95 717.49,-953.75 712.9,-959.03"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb -->
|
||||
<g id="node11" class="node">
|
||||
<title>svc:grampsweb</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M772.5,-1528C772.5,-1528 716.5,-1528 716.5,-1528 710.5,-1528 704.5,-1522 704.5,-1516 704.5,-1516 704.5,-1504 704.5,-1504 704.5,-1498 710.5,-1492 716.5,-1492 716.5,-1492 772.5,-1492 772.5,-1492 778.5,-1492 784.5,-1498 784.5,-1504 784.5,-1504 784.5,-1516 784.5,-1516 784.5,-1522 778.5,-1528 772.5,-1528"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1507.2" font-family="Helvetica,sans-Serif" font-size="11.00">grampsweb</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:grampsweb -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>svc:traefik->svc:grampsweb</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M540.91,-789.07C564.42,-897.78 686,-1460 686,-1460 686,-1460 700.43,-1472.55 714.83,-1485.07"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="712.9,-1488.03 722.74,-1491.95 717.49,-1482.75 712.9,-1488.03"/>
|
||||
</g>
|
||||
<!-- svc:influxdb -->
|
||||
<g id="node13" class="node">
|
||||
<title>svc:influxdb</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M767.5,-898C767.5,-898 721.5,-898 721.5,-898 715.5,-898 709.5,-892 709.5,-886 709.5,-886 709.5,-866 709.5,-866 709.5,-860 715.5,-854 721.5,-854 721.5,-854 767.5,-854 767.5,-854 773.5,-854 779.5,-860 779.5,-866 779.5,-866 779.5,-886 779.5,-886 779.5,-892 773.5,-898 767.5,-898"/>
|
||||
<text text-anchor="middle" x="744.5" y="-885.2" font-family="Helvetica,sans-Serif" font-size="11.00">influxdb</text>
|
||||
<text text-anchor="middle" x="744.5" y="-873.2" font-family="Helvetica,sans-Serif" font-size="11.00">:8086</text>
|
||||
<text text-anchor="middle" x="744.5" y="-861.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:influxdb -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>svc:traefik->svc:influxdb</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M571.1,-788.34C606.37,-806.27 661.57,-834.34 699.97,-853.87"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="698.85,-857.22 709.35,-858.64 702.02,-850.98 698.85,-857.22"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma -->
|
||||
<g id="node15" class="node">
|
||||
<title>svc:monitor-kuma</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M778.5,-1427C778.5,-1427 710.5,-1427 710.5,-1427 704.5,-1427 698.5,-1421 698.5,-1415 698.5,-1415 698.5,-1403 698.5,-1403 698.5,-1397 704.5,-1391 710.5,-1391 710.5,-1391 778.5,-1391 778.5,-1391 784.5,-1391 790.5,-1397 790.5,-1403 790.5,-1403 790.5,-1415 790.5,-1415 790.5,-1421 784.5,-1427 778.5,-1427"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1412.2" font-family="Helvetica,sans-Serif" font-size="11.00">monitor-kuma</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1400.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:monitor-kuma -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>svc:traefik->svc:monitor-kuma</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M541.62,-789.24C566.77,-888.49 686,-1359 686,-1359 686,-1359 700.43,-1371.55 714.83,-1384.07"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="712.9,-1387.03 722.74,-1390.95 717.49,-1381.75 712.9,-1387.03"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge -->
|
||||
<g id="node17" class="node">
|
||||
<title>svc:mtls-bridge</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M772,-1326C772,-1326 717,-1326 717,-1326 711,-1326 705,-1320 705,-1314 705,-1314 705,-1294 705,-1294 705,-1288 711,-1282 717,-1282 717,-1282 772,-1282 772,-1282 778,-1282 784,-1288 784,-1294 784,-1294 784,-1314 784,-1314 784,-1320 778,-1326 772,-1326"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1313.2" font-family="Helvetica,sans-Serif" font-size="11.00">mtls-bridge</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1301.2" font-family="Helvetica,sans-Serif" font-size="11.00">:8080</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1289.2" font-family="Helvetica,sans-Serif" font-size="11.00">[mTLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:mtls-bridge -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>svc:traefik->svc:mtls-bridge</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M542.66,-789.19C569.88,-876.69 686,-1250 686,-1250 686,-1250 698.77,-1262 712.28,-1274.68"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="710.1,-1277.43 719.78,-1281.72 714.89,-1272.33 710.1,-1277.43"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp -->
|
||||
<g id="node19" class="node">
|
||||
<title>svc:nextcloud-webapp</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M791,-137C791,-137 698,-137 698,-137 692,-137 686,-131 686,-125 686,-125 686,-113 686,-113 686,-107 692,-101 698,-101 698,-101 791,-101 791,-101 797,-101 803,-107 803,-113 803,-113 803,-125 803,-125 803,-131 797,-137 791,-137"/>
|
||||
<text text-anchor="middle" x="744.5" y="-116.2" font-family="Helvetica,sans-Serif" font-size="11.00">nextcloud-webapp</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:nextcloud-webapp -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>svc:traefik->svc:nextcloud-webapp</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M541.62,-752.85C566.77,-654.11 686,-186 686,-186 686,-186 704.96,-163.91 721.1,-145.1"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="723.93,-147.18 727.79,-137.31 718.62,-142.62 723.93,-147.18"/>
|
||||
</g>
|
||||
<!-- svc:node-red -->
|
||||
<g id="node21" class="node">
|
||||
<title>svc:node-red</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M767.5,-1217C767.5,-1217 721.5,-1217 721.5,-1217 715.5,-1217 709.5,-1211 709.5,-1205 709.5,-1205 709.5,-1185 709.5,-1185 709.5,-1179 715.5,-1173 721.5,-1173 721.5,-1173 767.5,-1173 767.5,-1173 773.5,-1173 779.5,-1179 779.5,-1185 779.5,-1185 779.5,-1205 779.5,-1205 779.5,-1211 773.5,-1217 767.5,-1217"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1204.2" font-family="Helvetica,sans-Serif" font-size="11.00">node-red</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1192.2" font-family="Helvetica,sans-Serif" font-size="11.00">:1880</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1180.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:node-red -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>svc:traefik->svc:node-red</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M544.29,-789.1C574.2,-863.38 686,-1141 686,-1141 686,-1141 698.77,-1153 712.28,-1165.68"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="710.1,-1168.43 719.78,-1172.72 714.89,-1163.33 710.1,-1168.43"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp -->
|
||||
<g id="node23" class="node">
|
||||
<title>svc:passbolt-webapp</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M787.5,-36C787.5,-36 701.5,-36 701.5,-36 695.5,-36 689.5,-30 689.5,-24 689.5,-24 689.5,-12 689.5,-12 689.5,-6 695.5,0 701.5,0 701.5,0 787.5,0 787.5,0 793.5,0 799.5,-6 799.5,-12 799.5,-12 799.5,-24 799.5,-24 799.5,-30 793.5,-36 787.5,-36"/>
|
||||
<text text-anchor="middle" x="744.5" y="-15.2" font-family="Helvetica,sans-Serif" font-size="11.00">passbolt-webapp</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:passbolt-webapp -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>svc:traefik->svc:passbolt-webapp</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M540.83,-752.92C564.15,-642.88 686,-68 686,-68 686,-68 700.43,-55.45 714.83,-42.93"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="717.49,-45.25 722.74,-36.05 712.9,-39.97 717.49,-45.25"/>
|
||||
</g>
|
||||
<!-- svc:portainer -->
|
||||
<g id="node25" class="node">
|
||||
<title>svc:portainer</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M766,-478C766,-478 723,-478 723,-478 717,-478 711,-472 711,-466 711,-466 711,-446 711,-446 711,-440 717,-434 723,-434 723,-434 766,-434 766,-434 772,-434 778,-440 778,-446 778,-446 778,-466 778,-466 778,-472 772,-478 766,-478"/>
|
||||
<text text-anchor="middle" x="744.5" y="-465.2" font-family="Helvetica,sans-Serif" font-size="11.00">portainer</text>
|
||||
<text text-anchor="middle" x="744.5" y="-453.2" font-family="Helvetica,sans-Serif" font-size="11.00">:9000</text>
|
||||
<text text-anchor="middle" x="744.5" y="-441.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:portainer -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>svc:traefik->svc:portainer</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M547.48,-752.65C581.39,-693.24 686,-510 686,-510 686,-510 698.77,-498 712.28,-485.32"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="714.89,-487.67 719.78,-478.28 710.1,-482.57 714.89,-487.67"/>
|
||||
</g>
|
||||
<!-- svc:prometheus -->
|
||||
<g id="node27" class="node">
|
||||
<title>svc:prometheus</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M774,-1108C774,-1108 715,-1108 715,-1108 709,-1108 703,-1102 703,-1096 703,-1096 703,-1076 703,-1076 703,-1070 709,-1064 715,-1064 715,-1064 774,-1064 774,-1064 780,-1064 786,-1070 786,-1076 786,-1076 786,-1096 786,-1096 786,-1102 780,-1108 774,-1108"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1095.2" font-family="Helvetica,sans-Serif" font-size="11.00">prometheus</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1083.2" font-family="Helvetica,sans-Serif" font-size="11.00">:9090</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1071.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:prometheus -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>svc:traefik->svc:prometheus</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M547.48,-789.35C581.39,-848.76 686,-1032 686,-1032 686,-1032 698.77,-1044 712.28,-1056.68"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="710.1,-1059.43 719.78,-1063.72 714.89,-1054.33 710.1,-1059.43"/>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp -->
|
||||
<g id="node29" class="node">
|
||||
<title>svc:searxng-webapp</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M786,-369C786,-369 703,-369 703,-369 697,-369 691,-363 691,-357 691,-357 691,-345 691,-345 691,-339 697,-333 703,-333 703,-333 786,-333 786,-333 792,-333 798,-339 798,-345 798,-345 798,-357 798,-357 798,-363 792,-369 786,-369"/>
|
||||
<text text-anchor="middle" x="744.5" y="-348.2" font-family="Helvetica,sans-Serif" font-size="11.00">searxng-webapp</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:searxng-webapp -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>svc:traefik->svc:searxng-webapp</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M544.29,-752.9C574.2,-678.62 686,-401 686,-401 686,-401 700.43,-388.45 714.83,-375.93"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="717.49,-378.25 722.74,-369.05 712.9,-372.97 717.49,-378.25"/>
|
||||
</g>
|
||||
<!-- net:traefik -->
|
||||
<g id="node36" class="node">
|
||||
<title>net:traefik</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-878" rx="31.04" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-875.2" font-family="Helvetica,sans-Serif" font-size="11.00">traefik</text>
|
||||
</g>
|
||||
<!-- svc:traefik->net:traefik -->
|
||||
<g id="edge55" class="edge">
|
||||
<title>svc:traefik->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M542.75,-752.82C570.13,-666.27 686,-300 686,-300 686,-300 803,-300 803,-300 803,-300 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- svc:authelia->net:traefik -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>svc:authelia->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M775.21,-783.17C824.86,-803.51 924.25,-844.23 975.12,-865.06"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="974.26,-867.36 981.67,-867.75 976.12,-862.83 974.26,-867.36"/>
|
||||
</g>
|
||||
<!-- dns:auth.<domain> -->
|
||||
<g id="node4" class="node">
|
||||
<title>dns:auth.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="123.5,-1496 25.5,-1496 25.5,-1460 129.5,-1460 129.5,-1490 123.5,-1496"/>
|
||||
<polyline fill="none" stroke="black" points="123.5,-1496 123.5,-1490 "/>
|
||||
<polyline fill="none" stroke="black" points="129.5,-1490 123.5,-1490 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1475.2" font-family="Helvetica,sans-Serif" font-size="11.00">auth.<domain></text>
|
||||
</g>
|
||||
<!-- dns:auth.<domain>->dynu -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>dns:auth.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1459.95C128.03,-1445.63 155,-1428 155,-1428 155,-1428 286.65,-925.11 319.6,-799.28"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="323.07,-799.82 322.22,-789.26 316.3,-798.05 323.07,-799.82"/>
|
||||
</g>
|
||||
<!-- svc:gitea->net:traefik -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>svc:gitea->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M768.56,-688.05C784.55,-703.36 803,-721 803,-721 803,-721 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:gitea.<domain> -->
|
||||
<g id="node6" class="node">
|
||||
<title>dns:gitea.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="125,-1395 24,-1395 24,-1359 131,-1359 131,-1389 125,-1395"/>
|
||||
<polyline fill="none" stroke="black" points="125,-1395 125,-1389 "/>
|
||||
<polyline fill="none" stroke="black" points="131,-1389 125,-1389 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1374.2" font-family="Helvetica,sans-Serif" font-size="11.00">gitea.<domain></text>
|
||||
</g>
|
||||
<!-- dns:gitea.<domain>->dynu -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>dns:gitea.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1358.95C128.03,-1344.63 155,-1327 155,-1327 155,-1327 283.58,-911.36 318.4,-798.82"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="321.76,-799.77 321.38,-789.18 315.08,-797.7 321.76,-799.77"/>
|
||||
</g>
|
||||
<!-- svc:gotify->net:traefik -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>svc:gotify->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-579.05C782.73,-593.37 803,-611 803,-611 803,-611 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:gotify.<domain> -->
|
||||
<g id="node8" class="node">
|
||||
<title>dns:gotify.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="126,-1294 23,-1294 23,-1258 132,-1258 132,-1288 126,-1294"/>
|
||||
<polyline fill="none" stroke="black" points="126,-1294 126,-1288 "/>
|
||||
<polyline fill="none" stroke="black" points="132,-1288 126,-1288 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1273.2" font-family="Helvetica,sans-Serif" font-size="11.00">gotify.<domain></text>
|
||||
</g>
|
||||
<!-- dns:gotify.<domain>->dynu -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>dns:gotify.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1257.95C128.03,-1243.63 155,-1226 155,-1226 155,-1226 279.21,-897.41 316.53,-798.71"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="319.89,-799.71 320.15,-789.12 313.34,-797.23 319.89,-799.71"/>
|
||||
</g>
|
||||
<!-- net:monitor -->
|
||||
<g id="node33" class="node">
|
||||
<title>net:monitor</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-979" rx="35.46" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-976.2" font-family="Helvetica,sans-Serif" font-size="11.00">monitor</text>
|
||||
</g>
|
||||
<!-- svc:grafana->net:monitor -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>svc:grafana->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M774.2,-980.78C820.4,-980.43 911.47,-979.73 964.89,-979.32"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="965.19,-981.77 972.17,-979.26 965.15,-976.87 965.19,-981.77"/>
|
||||
</g>
|
||||
<!-- svc:grafana->net:traefik -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>svc:grafana->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M774.2,-969.68C823.41,-950.28 923.53,-910.8 974.83,-890.57"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="975.82,-892.81 981.44,-887.96 974.03,-888.25 975.82,-892.81"/>
|
||||
</g>
|
||||
<!-- dns:grafana.<domain> -->
|
||||
<g id="node10" class="node">
|
||||
<title>dns:grafana.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="132,-1193 17,-1193 17,-1157 138,-1157 138,-1187 132,-1193"/>
|
||||
<polyline fill="none" stroke="black" points="132,-1193 132,-1187 "/>
|
||||
<polyline fill="none" stroke="black" points="138,-1187 132,-1187 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1172.2" font-family="Helvetica,sans-Serif" font-size="11.00">grafana.<domain></text>
|
||||
</g>
|
||||
<!-- dns:grafana.<domain>->dynu -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>dns:grafana.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1156.95C128.03,-1142.63 155,-1125 155,-1125 155,-1125 273.61,-880.89 313.84,-798.09"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="317.01,-799.57 318.23,-789.04 310.72,-796.51 317.01,-799.57"/>
|
||||
</g>
|
||||
<!-- net:gramps -->
|
||||
<g id="node32" class="node">
|
||||
<title>net:gramps</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-1080" rx="34.76" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-1077.2" font-family="Helvetica,sans-Serif" font-size="11.00">gramps</text>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:gramps -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>svc:grampsweb->net:gramps</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1491.95C782.73,-1477.63 803,-1460 803,-1460 803,-1460 949.23,-1187.21 993.89,-1103.88"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="996.27,-1104.64 997.41,-1097.32 991.95,-1102.33 996.27,-1104.64"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:traefik -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>svc:grampsweb->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1491.95C782.73,-1477.63 803,-1460 803,-1460 803,-1460 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:familytree.<domain> -->
|
||||
<g id="node12" class="node">
|
||||
<title>dns:familytree.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="139,-1092 10,-1092 10,-1056 145,-1056 145,-1086 139,-1092"/>
|
||||
<polyline fill="none" stroke="black" points="139,-1092 139,-1086 "/>
|
||||
<polyline fill="none" stroke="black" points="145,-1086 139,-1086 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1071.2" font-family="Helvetica,sans-Serif" font-size="11.00">familytree.<domain></text>
|
||||
</g>
|
||||
<!-- dns:familytree.<domain>->dynu -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>dns:familytree.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1055.95C128.03,-1041.63 155,-1024 155,-1024 155,-1024 264.64,-862.73 308.84,-797.71"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="311.9,-799.43 314.63,-789.2 306.11,-795.5 311.9,-799.43"/>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:monitor -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>svc:influxdb->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M779.65,-889.47C829.7,-909.2 922.46,-945.78 972.53,-965.53"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="971.89,-967.91 979.3,-968.2 973.69,-963.35 971.89,-967.91"/>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:traefik -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>svc:influxdb->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M779.65,-876.26C828.54,-876.64 918.2,-877.32 968.99,-877.71"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="969.17,-880.16 976.19,-877.77 969.21,-875.26 969.17,-880.16"/>
|
||||
</g>
|
||||
<!-- dns:influxdb.<domain> -->
|
||||
<g id="node14" class="node">
|
||||
<title>dns:influxdb.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="132.5,-991 16.5,-991 16.5,-955 138.5,-955 138.5,-985 132.5,-991"/>
|
||||
<polyline fill="none" stroke="black" points="132.5,-991 132.5,-985 "/>
|
||||
<polyline fill="none" stroke="black" points="138.5,-985 132.5,-985 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-970.2" font-family="Helvetica,sans-Serif" font-size="11.00">influxdb.<domain></text>
|
||||
</g>
|
||||
<!-- dns:influxdb.<domain>->dynu -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>dns:influxdb.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-954.95C128.03,-940.63 155,-923 155,-923 155,-923 250.14,-838.92 298.91,-795.83"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="301.42,-798.28 306.59,-789.03 296.78,-793.03 301.42,-798.28"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:monitor -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>svc:monitor-kuma->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1390.95C782.73,-1376.63 803,-1359 803,-1359 803,-1359 910,-1030 910,-1030 910,-1030 947.73,-1010.1 976.05,-995.16"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="977.25,-997.3 982.29,-991.87 974.96,-992.97 977.25,-997.3"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:traefik -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>svc:monitor-kuma->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1390.95C782.73,-1376.63 803,-1359 803,-1359 803,-1359 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:monitor-kuma.<domain> -->
|
||||
<g id="node16" class="node">
|
||||
<title>dns:monitor-kuma.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="149,-890 0,-890 0,-854 155,-854 155,-884 149,-890"/>
|
||||
<polyline fill="none" stroke="black" points="149,-890 149,-884 "/>
|
||||
<polyline fill="none" stroke="black" points="155,-884 149,-884 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-869.2" font-family="Helvetica,sans-Serif" font-size="11.00">monitor-kuma.<domain></text>
|
||||
</g>
|
||||
<!-- dns:monitor-kuma.<domain>->dynu -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>dns:monitor-kuma.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M122.93,-853.94C165.02,-836.83 228.32,-811.11 273.25,-792.85"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="274.59,-796.08 282.54,-789.07 271.96,-789.59 274.59,-796.08"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:monitor -->
|
||||
<g id="edge43" class="edge">
|
||||
<title>svc:mtls-bridge->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1281.72C785.06,-1266.85 803,-1250 803,-1250 803,-1250 910,-1030 910,-1030 910,-1030 947.73,-1010.1 976.05,-995.16"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="977.25,-997.3 982.29,-991.87 974.96,-992.97 977.25,-997.3"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:traefik -->
|
||||
<g id="edge44" class="edge">
|
||||
<title>svc:mtls-bridge->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1281.72C785.06,-1266.85 803,-1250 803,-1250 803,-1250 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:mtls-bridge.<domain> -->
|
||||
<g id="node18" class="node">
|
||||
<title>dns:mtls-bridge.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="142,-789 7,-789 7,-753 148,-753 148,-783 142,-789"/>
|
||||
<polyline fill="none" stroke="black" points="142,-789 142,-783 "/>
|
||||
<polyline fill="none" stroke="black" points="148,-783 142,-783 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-768.2" font-family="Helvetica,sans-Serif" font-size="11.00">mtls-bridge.<domain></text>
|
||||
</g>
|
||||
<!-- dns:mtls-bridge.<domain>->dynu -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>dns:mtls-bridge.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M148.05,-771C182.94,-771 225.03,-771 259.61,-771"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="259.62,-774.5 269.62,-771 259.62,-767.5 259.62,-774.5"/>
|
||||
</g>
|
||||
<!-- net:nextcloud -->
|
||||
<g id="node34" class="node">
|
||||
<title>net:nextcloud</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-777" rx="42.89" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-774.2" font-family="Helvetica,sans-Serif" font-size="11.00">nextcloud</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:nextcloud -->
|
||||
<g id="edge45" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:nextcloud</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M761.21,-137.31C778.24,-157.15 803,-186 803,-186 803,-186 910,-727 910,-727 910,-727 945.48,-745.35 973.46,-759.81"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="972.67,-762.17 980.02,-763.2 974.92,-757.81 972.67,-762.17"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:traefik -->
|
||||
<g id="edge46" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M761.21,-137.31C778.24,-157.15 803,-186 803,-186 803,-186 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:nextcloud.<domain> -->
|
||||
<g id="node20" class="node">
|
||||
<title>dns:nextcloud.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="138,-688 11,-688 11,-652 144,-652 144,-682 138,-688"/>
|
||||
<polyline fill="none" stroke="black" points="138,-688 138,-682 "/>
|
||||
<polyline fill="none" stroke="black" points="144,-682 138,-682 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-667.2" font-family="Helvetica,sans-Serif" font-size="11.00">nextcloud.<domain></text>
|
||||
</g>
|
||||
<!-- dns:nextcloud.<domain>->dynu -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>dns:nextcloud.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M122.93,-688.06C165.02,-705.17 228.32,-730.89 273.25,-749.15"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="271.96,-752.41 282.54,-752.93 274.59,-745.92 271.96,-752.41"/>
|
||||
</g>
|
||||
<!-- svc:node-red->net:monitor -->
|
||||
<g id="edge47" class="edge">
|
||||
<title>svc:node-red->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1172.72C785.06,-1157.85 803,-1141 803,-1141 803,-1141 910,-1030 910,-1030 910,-1030 947.73,-1010.1 976.05,-995.16"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="977.25,-997.3 982.29,-991.87 974.96,-992.97 977.25,-997.3"/>
|
||||
</g>
|
||||
<!-- svc:node-red->net:traefik -->
|
||||
<g id="edge48" class="edge">
|
||||
<title>svc:node-red->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1172.72C785.06,-1157.85 803,-1141 803,-1141 803,-1141 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:node-red.<domain> -->
|
||||
<g id="node22" class="node">
|
||||
<title>dns:node-red.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="135.5,-587 13.5,-587 13.5,-551 141.5,-551 141.5,-581 135.5,-587"/>
|
||||
<polyline fill="none" stroke="black" points="135.5,-587 135.5,-581 "/>
|
||||
<polyline fill="none" stroke="black" points="141.5,-581 135.5,-581 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-566.2" font-family="Helvetica,sans-Serif" font-size="11.00">node-red.<domain></text>
|
||||
</g>
|
||||
<!-- dns:node-red.<domain>->dynu -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>dns:node-red.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-587.05C128.03,-601.37 155,-619 155,-619 155,-619 250.14,-703.08 298.91,-746.17"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="296.78,-748.97 306.59,-752.97 301.42,-743.72 296.78,-748.97"/>
|
||||
</g>
|
||||
<!-- net:passbolt -->
|
||||
<g id="node35" class="node">
|
||||
<title>net:passbolt</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-676" rx="37.77" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-673.2" font-family="Helvetica,sans-Serif" font-size="11.00">passbolt</text>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:passbolt -->
|
||||
<g id="edge49" class="edge">
|
||||
<title>svc:passbolt-webapp->net:passbolt</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-36.05C782.73,-50.37 803,-68 803,-68 803,-68 960.4,-537.83 998.48,-651.47"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="996.16,-652.27 1000.71,-658.13 1000.81,-650.72 996.16,-652.27"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:traefik -->
|
||||
<g id="edge50" class="edge">
|
||||
<title>svc:passbolt-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-36.05C782.73,-50.37 803,-68 803,-68 803,-68 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:passbolt.<domain> -->
|
||||
<g id="node24" class="node">
|
||||
<title>dns:passbolt.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="134,-486 15,-486 15,-450 140,-450 140,-480 134,-486"/>
|
||||
<polyline fill="none" stroke="black" points="134,-486 134,-480 "/>
|
||||
<polyline fill="none" stroke="black" points="140,-480 134,-480 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-465.2" font-family="Helvetica,sans-Serif" font-size="11.00">passbolt.<domain></text>
|
||||
</g>
|
||||
<!-- dns:passbolt.<domain>->dynu -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>dns:passbolt.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-486.05C128.03,-500.37 155,-518 155,-518 155,-518 264.64,-679.27 308.84,-744.29"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="306.11,-746.5 314.63,-752.8 311.9,-742.57 306.11,-746.5"/>
|
||||
</g>
|
||||
<!-- svc:portainer->net:traefik -->
|
||||
<g id="edge51" class="edge">
|
||||
<title>svc:portainer->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-478.28C785.06,-493.15 803,-510 803,-510 803,-510 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:portainer.<domain> -->
|
||||
<g id="node26" class="node">
|
||||
<title>dns:portainer.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="135.5,-385 13.5,-385 13.5,-349 141.5,-349 141.5,-379 135.5,-385"/>
|
||||
<polyline fill="none" stroke="black" points="135.5,-385 135.5,-379 "/>
|
||||
<polyline fill="none" stroke="black" points="141.5,-379 135.5,-379 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-364.2" font-family="Helvetica,sans-Serif" font-size="11.00">portainer.<domain></text>
|
||||
</g>
|
||||
<!-- dns:portainer.<domain>->dynu -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>dns:portainer.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-385.05C128.03,-399.37 155,-417 155,-417 155,-417 273.61,-661.11 313.84,-743.91"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="310.72,-745.49 318.23,-752.96 317.01,-742.43 310.72,-745.49"/>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:monitor -->
|
||||
<g id="edge52" class="edge">
|
||||
<title>svc:prometheus->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M786.03,-1069.4C837.48,-1048.32 925.31,-1012.34 973.18,-992.73"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="974.11,-995 979.66,-990.07 972.26,-990.46 974.11,-995"/>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:traefik -->
|
||||
<g id="edge53" class="edge">
|
||||
<title>svc:prometheus->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1063.72C785.06,-1048.85 803,-1032 803,-1032 803,-1032 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:prometheus.<domain> -->
|
||||
<g id="node28" class="node">
|
||||
<title>dns:prometheus.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="144,-284 5,-284 5,-248 150,-248 150,-278 144,-284"/>
|
||||
<polyline fill="none" stroke="black" points="144,-284 144,-278 "/>
|
||||
<polyline fill="none" stroke="black" points="150,-278 144,-278 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-263.2" font-family="Helvetica,sans-Serif" font-size="11.00">prometheus.<domain></text>
|
||||
</g>
|
||||
<!-- dns:prometheus.<domain>->dynu -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>dns:prometheus.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-284.05C128.03,-298.37 155,-316 155,-316 155,-316 279.21,-644.59 316.53,-743.29"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="313.34,-744.77 320.15,-752.88 319.89,-742.29 313.34,-744.77"/>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp->net:traefik -->
|
||||
<g id="edge54" class="edge">
|
||||
<title>svc:searxng-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-369.05C782.73,-383.37 803,-401 803,-401 803,-401 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:searxng.<domain> -->
|
||||
<g id="node30" class="node">
|
||||
<title>dns:searxng.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="133,-183 16,-183 16,-147 139,-147 139,-177 133,-183"/>
|
||||
<polyline fill="none" stroke="black" points="133,-183 133,-177 "/>
|
||||
<polyline fill="none" stroke="black" points="139,-177 133,-177 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-162.2" font-family="Helvetica,sans-Serif" font-size="11.00">searxng.<domain></text>
|
||||
</g>
|
||||
<!-- dns:searxng.<domain>->dynu -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>dns:searxng.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-183.05C128.03,-197.37 155,-215 155,-215 155,-215 283.58,-630.64 318.4,-743.18"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="315.08,-744.3 321.38,-752.82 321.76,-742.23 315.08,-744.3"/>
|
||||
</g>
|
||||
<!-- dns:traefik.<domain> -->
|
||||
<g id="node31" class="node">
|
||||
<title>dns:traefik.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="128.5,-82 20.5,-82 20.5,-46 134.5,-46 134.5,-76 128.5,-82"/>
|
||||
<polyline fill="none" stroke="black" points="128.5,-82 128.5,-76 "/>
|
||||
<polyline fill="none" stroke="black" points="134.5,-76 128.5,-76 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-61.2" font-family="Helvetica,sans-Serif" font-size="11.00">traefik.<domain></text>
|
||||
</g>
|
||||
<!-- dns:traefik.<domain>->dynu -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>dns:traefik.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-82.05C128.03,-96.37 155,-114 155,-114 155,-114 286.65,-616.89 319.6,-742.72"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="316.3,-743.95 322.22,-752.74 323.07,-742.18 316.3,-743.95"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,4 @@
|
||||
digraph PhysicalTopology {
|
||||
graph [rankdir=LR, fontname="Helvetica", nodesep=1.0, ranksep=1.5];
|
||||
"placeholder:inventory" [shape=note, style="filled", fillcolor="#fef3c7", label="Host inventory JSON not found.\nGenerate terraform inventory and rerun scripts/docs/generate-all.sh\n(--host-inventory <path>)."];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: PhysicalTopology Pages: 1 -->
|
||||
<svg width="514pt" height="61pt"
|
||||
viewBox="0.00 0.00 514.00 61.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 57)">
|
||||
<title>PhysicalTopology</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-57 510,-57 510,4 -4,4"/>
|
||||
<!-- placeholder:inventory -->
|
||||
<g id="node1" class="node">
|
||||
<title>placeholder:inventory</title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="500,-53 0,-53 0,0 506,0 506,-47 500,-53"/>
|
||||
<polyline fill="none" stroke="black" points="500,-53 500,-47 "/>
|
||||
<polyline fill="none" stroke="black" points="506,-47 500,-47 "/>
|
||||
<text text-anchor="middle" x="253" y="-37.8" font-family="Times,serif" font-size="14.00">Host inventory JSON not found.</text>
|
||||
<text text-anchor="middle" x="253" y="-22.8" font-family="Times,serif" font-size="14.00">Generate terraform inventory and rerun scripts/docs/generate-all.sh</text>
|
||||
<text text-anchor="middle" x="253" y="-7.8" font-family="Times,serif" font-size="14.00">(--host-inventory <path>).</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,13 @@
|
||||
# Docker Environment
|
||||
|
||||
This environment is orchestrated from central Docker Compose definitions committed in this repository.
|
||||
|
||||
Compose source files are rendered into a resolved configuration during docs generation, then summarized into generated markdown and diagrams.
|
||||
|
||||
Generated outputs:
|
||||
|
||||
- [Compose Inventory](generated/compose-inventory.md)
|
||||
- [Resolved Compose Config](generated/docker-compose.resolved.yml)
|
||||
- [Docker Compose Diagram](diagrams/docker-compose.svg)
|
||||
|
||||
Generated documentation is produced by CI from repository files only. Documentation generation does not start containers.
|
||||
@@ -0,0 +1,26 @@
|
||||
default-network.yml
|
||||
apps/gitea/docker-compose.yml
|
||||
apps/gramps/docker-compose.yml
|
||||
apps/nextcloud/docker-compose.yml
|
||||
apps/passbolt/docker-compose.yml
|
||||
apps/searxng/docker-compose.yml
|
||||
apps/shift-recorder/docker-compose.yml
|
||||
apps/stockfill/docker-compose.yml
|
||||
core/authelia/docker-compose.yml
|
||||
core/crowdsec/docker-compose.yml
|
||||
core/error-pages/docker-compose.yml
|
||||
core/test/docker-compose.yml
|
||||
core/traefik/docker-compose.yml
|
||||
monitoring/docker-exporter/docker-compose.yml
|
||||
monitoring/docker-socket-proxy/docker-compose.yml
|
||||
monitoring/gotify/docker-compose.yml
|
||||
monitoring/grafana/docker-compose.yml
|
||||
monitoring/influxdb/docker-compose.yml
|
||||
monitoring/mtls-bridge/docker-compose.yml
|
||||
monitoring/node-exporter/docker-compose.yml
|
||||
monitoring/node-red/docker-compose.yml
|
||||
monitoring/pihole-exporter/docker-compose.yml
|
||||
monitoring/portainer/docker-compose.yml
|
||||
monitoring/prometheus/docker-compose.yml
|
||||
monitoring/telegraf/docker-compose.yml
|
||||
monitoring/uptime-kuma/docker-compose.yml
|
||||
@@ -0,0 +1,61 @@
|
||||
# Docker Compose Inventory
|
||||
|
||||
Source fingerprint: `0fad36c3fed6`
|
||||
|
||||
## Summary
|
||||
|
||||
| Item | Count |
|
||||
|---|---:|
|
||||
| Services | 30 |
|
||||
| Networks | 5 |
|
||||
| Volumes | 0 |
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Container | Image | Build | Profiles | Networks | Ports | Restart |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| authelia | authelia | authelia/authelia | /home/nixos/docker/core/authelia | core, all, authelia, traefik | traefik | | always |
|
||||
| crowdsec | crowdsec | | /home/nixos/docker/core/crowdsec | core, all, crowdsec, traefik | traefik | | always |
|
||||
| docker-socket-proxy | docker-socket-proxy | tecnativa/docker-socket-proxy:latest | | monitoring, all, docker-socket-proxy, core, traefik, prometheus | monitor, traefik | | unless-stopped |
|
||||
| docker-update-exporter | docker-update-exporter | | /home/nixos/docker/monitoring/docker-exporter | monitoring, all, docker-exporter, prometheus | monitor | | unless-stopped |
|
||||
| error-pages | error-pages | tarampampam/error-pages:3 | | core, all, error-pages, traefik | traefik | | always |
|
||||
| gitea | gitea | gitea/gitea:latest | | apps, all, gitea | traefik | | always |
|
||||
| gitea-runner | gitea-runner | gitea/act_runner:latest | | apps, all, gitea, ci | traefik | | always |
|
||||
| gotify | gotify | gotify/server:latest | | monitoring, all, gotify | traefik | | always |
|
||||
| grafana | grafana | grafana/grafana:latest | | monitoring, all, grafana | monitor, traefik | | unless-stopped |
|
||||
| gramps-redis | gramps-redis | valkey/valkey:8-alpine | | apps, all, gramps | gramps | | always |
|
||||
| grampsweb | gramps-web | ghcr.io/gramps-project/grampsweb:latest | | apps, all, gramps | gramps, traefik | | always |
|
||||
| grampsweb_celery | gramps-web-celery | ghcr.io/gramps-project/grampsweb:latest | | apps, all, gramps | gramps | | always |
|
||||
| influxdb | influxdb | influxdb:2.7 | | monitoring, all, influxdb, prometheus | monitor, traefik | | unless-stopped |
|
||||
| monitor-kuma | monitor-kuma | louislam/uptime-kuma:2.1.1 | | monitoring, all, uptime-kuma | monitor, traefik | | always |
|
||||
| mtls-bridge | mtls-bridge | | /home/nixos/docker/monitoring/mtls-bridge | monitoring, all, mtls-bridge | monitor, traefik | | unless-stopped |
|
||||
| nextcloud-db | nextcloud-db | mariadb:11.4 | | apps, all, nextcloud | nextcloud | | always |
|
||||
| nextcloud-redis | nextcloud-redis | redis | | apps, all, nextcloud | nextcloud | | always |
|
||||
| nextcloud-webapp | nextcloud-webapp | | /home/nixos/docker/apps/nextcloud | apps, all, nextcloud | nextcloud, traefik | | always |
|
||||
| node-exporter | node-exporter | prom/node-exporter:latest | | monitoring, all, node-exporter, prometheus | monitor | | unless-stopped |
|
||||
| node-red | node-red | | /home/nixos/docker/monitoring/node-red | monitoring, all, node-red | monitor, traefik | | unless-stopped |
|
||||
| passbolt-db | passbolt-db | mariadb:12 | | apps, all, passbolt | passbolt | | always |
|
||||
| passbolt-webapp | passbolt-webapp | passbolt/passbolt:latest-ce | | apps, all, passbolt | passbolt, traefik | | always |
|
||||
| pihole-exporter | pihole-exporter | ekofr/pihole-exporter:latest | | monitoring, all, pihole-exporter, prometheus | monitor | {'mode': 'ingress', 'target': 9617, 'published': '9617', 'protocol': 'tcp'} | unless-stopped |
|
||||
| portainer | portainer | portainer/portainer-ce:latest | | monitoring, all, portainer | traefik | | unless-stopped |
|
||||
| prometheus | prometheus | prom/prometheus:latest | | monitoring, all, prometheus | monitor, traefik | | unless-stopped |
|
||||
| searxng-webapp | searxng-webapp | searxng/searxng | | apps, all, searxng | traefik | | always |
|
||||
| shift-recorder-web | shift-recorder | | /home/nixos/docker/apps/shift-recorder | apps, all, shift-recorder | traefik | | unless-stopped |
|
||||
| stockfill | stockfill | | /home/nixos/docker/apps/stockfill | apps, all, stockfill | traefik | | unless-stopped |
|
||||
| telegraf | telegraf | telegraf:latest | | monitoring, all, telegraf, prometheus | monitor | | unless-stopped |
|
||||
| traefik | traefik | traefik:3 | /home/nixos/docker/core | core, all, traefik | traefik | {'mode': 'ingress', 'target': 80, 'published': '80', 'protocol': 'tcp'}, {'mode': 'ingress', 'target': 443, 'published': '443', 'protocol': 'tcp'} | always |
|
||||
|
||||
## Networks
|
||||
|
||||
| Network | Driver | External |
|
||||
|---|---|---|
|
||||
| gramps | | False |
|
||||
| monitor | | False |
|
||||
| nextcloud | | False |
|
||||
| passbolt | | False |
|
||||
| traefik | bridge | False |
|
||||
|
||||
## Volumes
|
||||
|
||||
| Volume | External |
|
||||
|---|---|
|
||||
@@ -3,15 +3,15 @@
|
||||
> This integration is intentionally read-only. No Dynu mutations are permitted in this repo at this stage.
|
||||
|
||||
- Base domain: `lan.ddnsgeek.com`
|
||||
- Dynu fetched at: `2026-04-21T03:55:09+00:00`
|
||||
- Inventory generated at: `2026-04-21T04:08:43+00:00`
|
||||
- Dynu fetched at: `2026-04-21T04:18:38+00:00`
|
||||
- Inventory generated at: `2026-04-21T04:18:39+00:00`
|
||||
|
||||
## Summary
|
||||
|
||||
- Traefik hostnames discovered: **15**
|
||||
- Traefik hostnames discovered: **17**
|
||||
- Dynu hostnames discovered: **20**
|
||||
- Mapped hostnames: **15**
|
||||
- DNS-only hostnames: **5**
|
||||
- Mapped hostnames: **17**
|
||||
- DNS-only hostnames: **3**
|
||||
- Traefik-only hostnames: **0**
|
||||
- Ambiguous hostnames: **0**
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
- Validation ok: **false**
|
||||
- Allowed unmapped hostnames: `edge.lan.ddnsgeek.com`
|
||||
- Unexpected unmapped hostnames: **3**
|
||||
- Unexpected unmapped hostnames: **1**
|
||||
- Duplicate hostnames: **1**
|
||||
- Ambiguous hostnames: **0**
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
### Unexpected unmapped hostnames
|
||||
|
||||
- `kuma.lan.ddnsgeek.com`
|
||||
- `shifts.lan.ddnsgeek.com`
|
||||
- `stockfill.lan.ddnsgeek.com`
|
||||
|
||||
### Duplicate hostnames
|
||||
|
||||
@@ -62,6 +60,6 @@ _None._
|
||||
| `portainer.lan.ddnsgeek.com` | `mapped` | `mapped` | monitoring/portainer | portainer [tls=true, mtls=true, authelia=false, tls_options=mtls-private-admin@file, middlewares=-] | A: |
|
||||
| `prometheus.lan.ddnsgeek.com` | `mapped` | `mapped` | monitoring/prometheus | prometheus [tls=true, mtls=true, authelia=true, tls_options=mtls-private-admin@file, middlewares=authelia] | A: |
|
||||
| `searxng.lan.ddnsgeek.com` | `mapped` | `mapped` | apps/searxng-webapp | searxng [tls=true, mtls=false, authelia=false, tls_options=-, middlewares=-] | A: |
|
||||
| `shifts.lan.ddnsgeek.com` | `unexpected_unmapped` | `unexpected_unmapped, dns_only` | - | - | A: |
|
||||
| `stockfill.lan.ddnsgeek.com` | `unexpected_unmapped` | `unexpected_unmapped, dns_only` | - | - | A: |
|
||||
| `shifts.lan.ddnsgeek.com` | `mapped` | `mapped` | apps/shift-recorder-web | shifts [tls=true, mtls=false, authelia=false, tls_options=-, middlewares=-] | A: |
|
||||
| `stockfill.lan.ddnsgeek.com` | `mapped` | `mapped` | apps/stockfill | stockfill [tls=true, mtls=false, authelia=false, tls_options=-, middlewares=-] | A: |
|
||||
| `traefik.lan.ddnsgeek.com` | `mapped` | `mapped` | core/traefik | traefik [tls=true, mtls=true, authelia=true, tls_options=mtls-private-admin@file, middlewares=authelia] | A: |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
# Host Topology
|
||||
|
||||
> Generated by `scripts/docs/generate_host_topology.py` on 2026-05-12T18:32:09+00:00.
|
||||
|
||||
## Topology Diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
phys_pve["pve\nphysical"]
|
||||
phys_raspberrypi["raspberrypi\nphysical"]
|
||||
virt_docker["docker\nvirtual"]
|
||||
phys_pve --> virt_docker
|
||||
virt_nix_cache["nix-cache\nvirtual"]
|
||||
phys_pve --> virt_nix_cache
|
||||
virt_pbs["pbs\nvirtual"]
|
||||
phys_pve --> virt_pbs
|
||||
virt_pihole["pihole\nvirtual"]
|
||||
phys_pve --> virt_pihole
|
||||
virt_server_nixos["server-nixos\nvirtual"]
|
||||
phys_pve --> virt_server_nixos
|
||||
```
|
||||
|
||||
## Physical Hosts
|
||||
|
||||
| Name | Type | Role | Management | OS | Hypervisor | Location | Notes |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| pve | physical | proxmox | pve.sweet.home | debian | proxmox | home | Primary Proxmox VE host |
|
||||
| raspberrypi | physical | edge | raspberrypi.tail13f623.ts.net | debian | | riverglades | Raspberry Pi host |
|
||||
|
||||
## Virtual Hosts
|
||||
|
||||
| Name | Type | Role | Parent/Node | Management | OS | Notes |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| docker | virtual | docker-host | pve | | linux | Primary Docker VM |
|
||||
| nix-cache | virtual | cache | pve | | linux | Nix binary cache VM |
|
||||
| pbs | virtual | backup | pve | | linux | Proxmox Backup Server VM |
|
||||
| pihole | virtual | dns | pve | | linux | DNS filtering VM |
|
||||
| server-nixos | virtual | nixos-server | pve | | nixos | General-purpose NixOS VM |
|
||||
@@ -0,0 +1,11 @@
|
||||
# Generated Documentation
|
||||
|
||||
This directory contains documentation generated automatically from repository configuration.
|
||||
|
||||
## Files
|
||||
|
||||
- [Compose file list](compose-files.txt)
|
||||
- [Resolved Docker Compose config](docker-compose.resolved.yml)
|
||||
- [Compose inventory](compose-inventory.md)
|
||||
- [Traefik routes](traefik-routes.md)
|
||||
- [Docker Compose diagram](../diagrams/docker-compose.svg)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Traefik Routes
|
||||
|
||||
| Service | Router | Rule | Entrypoints | TLS | Middlewares | Target Port |
|
||||
|---|---|---|---|---|---|---|
|
||||
| authelia | authelia | Host(`auth.lan.ddnsgeek.com`) | websecure | true | | |
|
||||
| error-pages | error-pages-router | HostRegexp(`{host:.+}`) | web | | error-pages-middleware | |
|
||||
| gitea | gitea | Host(`gitea.lan.ddnsgeek.com`) | websecure | true | | 3000 |
|
||||
| gotify | gotify | Host(`gotify.lan.ddnsgeek.com`) | websecure | | | 80 |
|
||||
| grafana | grafana | Host(`grafana.lan.ddnsgeek.com`) | websecure | | | 3000 |
|
||||
| grampsweb | gramps | Host(`familytree.lan.ddnsgeek.com`) | websecure | | | 5000 |
|
||||
| influxdb | influxdb | Host(`influxdb.lan.ddnsgeek.com`) | websecure | | authelia | 8086 |
|
||||
| monitor-kuma | monitor | Host(`monitor-kuma.lan.ddnsgeek.com`) | websecure | true | | 3001 |
|
||||
| mtls-bridge | mtls-bridge | Host(`mtls-bridge.lan.ddnsgeek.com`) | websecure | | mtls-bridge-auth,mtls-bridge-cors | 8080 |
|
||||
| mtls-bridge | mtls-bridge-preflight | Host(`mtls-bridge.lan.ddnsgeek.com`) && Method(`OPTIONS`) | websecure | | mtls-bridge-cors | |
|
||||
| nextcloud-webapp | nextcloud | Host(`nextcloud.lan.ddnsgeek.com`) | websecure | | nextcloud-dav, nextcloud-webfinger | |
|
||||
| node-red | node-red | Host(`node-red.lan.ddnsgeek.com`) | websecure | | authelia | 1880 |
|
||||
| passbolt-webapp | passbolt | Host(`passbolt.lan.ddnsgeek.com`) | websecure | | | |
|
||||
| portainer | portainer | Host(`portainer.lan.ddnsgeek.com`) | websecure | true | | 9000 |
|
||||
| prometheus | prometheus | Host(`prometheus.lan.ddnsgeek.com`) | websecure | | authelia | 9090 |
|
||||
| searxng-webapp | searxng | Host(`searxng.lan.ddnsgeek.com`) | websecure | | | 8080 |
|
||||
| shift-recorder-web | shifts | Host(`shifts.lan.ddnsgeek.com`) | websecure | true | | 80 |
|
||||
| stockfill | stockfill | Host(`stockfill.lan.ddnsgeek.com`) | websecure | true | | 80 |
|
||||
| traefik | traefik | Host(`traefik.lan.ddnsgeek.com`) | websecure | | authelia | |
|
||||
@@ -0,0 +1,29 @@
|
||||
# Infrastructure Documentation
|
||||
|
||||
This documentation describes the Docker-based infrastructure, reverse proxy configuration, monitoring stack, automation services, and generated inventory for this environment.
|
||||
|
||||
Some sections are manually written. Other sections are generated automatically by GitHub Actions from the repository configuration.
|
||||
|
||||
## Sections
|
||||
|
||||
- [Docker Environment](docker.md)
|
||||
- [Networking and Reverse Proxy](networking.md)
|
||||
- [Monitoring and Alerting](monitoring.md)
|
||||
- [Automation](automation.md)
|
||||
- [Operations](operations.md)
|
||||
- [Public Showcase](showcase.md)
|
||||
|
||||
## Generated Documentation
|
||||
|
||||
- [Compose Inventory](generated/compose-inventory.md)
|
||||
- [Traefik Routes](generated/traefik-routes.md)
|
||||
- [Resolved Compose Config](generated/docker-compose.resolved.yml)
|
||||
- [Docker Compose Diagram](diagrams/docker-compose.svg)
|
||||
|
||||
## Public-safe Output
|
||||
|
||||
Sanitized documentation intended for public sharing is generated under:
|
||||
|
||||
```text
|
||||
docs/public/
|
||||
```
|
||||
@@ -50,6 +50,12 @@ Compose files define intended service runtime composition, networking, labels, a
|
||||
|
||||
Dynu write operations are intentionally blocked in this repository stage.
|
||||
|
||||
### 7) Terraform Dynu DNS layer
|
||||
|
||||
`infrastructure/terraform/dynu/` is the brownfield Terraform DNS mirror/reconciliation root for Dynu domain/record inventory outputs.
|
||||
|
||||
At this stage it is primarily documentation-oriented catalog output, ready for one-object-at-a-time imports.
|
||||
|
||||
## Output shaping expectations
|
||||
|
||||
When adding Terraform outputs for documentation/tooling:
|
||||
@@ -61,6 +67,7 @@ When adding Terraform outputs for documentation/tooling:
|
||||
|
||||
## Limitations today
|
||||
|
||||
- Generated host topology document: `docs/generated/host-topology.md` (via `scripts/docs/build_host_topology.sh`).
|
||||
- No full generated inventory document pipeline is present yet.
|
||||
- Some Terraform files still include generated boilerplate comments requiring ongoing cleanup.
|
||||
- Ansible is currently a bootstrap inventory/configuration layer and is not authoritative for full operations yet.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Monitoring and Alerting
|
||||
|
||||
Monitoring documentation is generated from static rule files committed in this repository.
|
||||
|
||||
- Prometheus rule files are parsed statically.
|
||||
- Live Prometheus APIs are not queried.
|
||||
- Future improvements can include Grafana dashboard and alert contact-point summaries.
|
||||
|
||||
Generated asset:
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Networking and Reverse Proxy
|
||||
|
||||
Networking and reverse-proxy documentation is generated from Docker Compose metadata.
|
||||
|
||||
- Traefik labels are parsed statically from Compose definitions.
|
||||
- Service-to-network relationships are represented in generated diagrams.
|
||||
- Sensitive internal values are redacted in public-facing output.
|
||||
|
||||
Related generated assets:
|
||||
|
||||
- [Traefik Routes](generated/traefik-routes.md)
|
||||
- [Docker Compose Diagram](diagrams/docker-compose.svg)
|
||||
@@ -0,0 +1,30 @@
|
||||
# Operations
|
||||
|
||||
## Local docs generation
|
||||
|
||||
```bash
|
||||
chmod +x scripts/docs/*.sh
|
||||
scripts/docs/generate-all.sh
|
||||
```
|
||||
|
||||
## Inspect generated changes
|
||||
|
||||
```bash
|
||||
git status -- docs/generated docs/diagrams docs/public
|
||||
```
|
||||
|
||||
## GitHub Actions artifacts
|
||||
|
||||
Use the **Generate documentation** workflow run and download the `generated-documentation` artifact from the run summary.
|
||||
|
||||
## Commit behavior
|
||||
|
||||
- Pull requests generate docs and upload artifacts only.
|
||||
- Pushes to `main` generate docs, upload artifacts, and commit generated docs when changes exist.
|
||||
- Manual workflow runs can commit generated docs when enabled through workflow input.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Confirm required tooling (`python3`, `jq`, `graphviz`, `docker compose`) is available.
|
||||
- Re-run generation after documentation script changes.
|
||||
- Review `docs/public` output for redaction coverage before sharing.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Docker Compose Inventory
|
||||
|
||||
Source fingerprint: `232be78ef441`
|
||||
|
||||
## Summary
|
||||
|
||||
| Item | Count |
|
||||
|---|---:|
|
||||
| Services | 28 |
|
||||
| Networks | 5 |
|
||||
| Volumes | 0 |
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Container | Image | Build | Profiles | Networks | Ports | Restart |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| authelia | authelia | authelia/authelia | /home/nixos/docker/core/authelia | core, all, authelia, traefik | traefik | | always |
|
||||
| crowdsec | crowdsec | | /home/nixos/docker/core/crowdsec | core, all, crowdsec, traefik | traefik | | always |
|
||||
| docker-socket-proxy | docker-socket-proxy | tecnativa/docker-socket-proxy:latest | | monitoring, all, docker-socket-proxy, core, traefik, prometheus | monitor, traefik | | unless-stopped |
|
||||
| docker-update-exporter | docker-update-exporter | | /home/nixos/docker/monitoring/docker-exporter | monitoring, all, docker-exporter, prometheus | monitor | | unless-stopped |
|
||||
| error-pages | error-pages | tarampampam/error-pages:3 | | core, all, error-pages, traefik | traefik | | always |
|
||||
| gitea | gitea | gitea/gitea:latest | | apps, all, gitea | traefik | | always |
|
||||
| gitea-runner | gitea-runner | gitea/act_runner:latest | | apps, all, gitea, ci | traefik | | always |
|
||||
| gotify | gotify | gotify/server:latest | | monitoring, all, gotify | traefik | | always |
|
||||
| grafana | grafana | grafana/grafana:latest | | monitoring, all, grafana | monitor, traefik | | unless-stopped |
|
||||
| gramps-redis | gramps-redis | valkey/valkey:8-alpine | | apps, all, gramps | gramps | | always |
|
||||
| grampsweb | gramps-web | ghcr.io/gramps-project/grampsweb:latest | | apps, all, gramps | gramps, traefik | | always |
|
||||
| grampsweb_celery | gramps-web-celery | ghcr.io/gramps-project/grampsweb:latest | | apps, all, gramps | gramps | | always |
|
||||
| influxdb | influxdb | influxdb:2.7 | | monitoring, all, influxdb, prometheus | monitor, traefik | | unless-stopped |
|
||||
| monitor-kuma | monitor-kuma | louislam/uptime-kuma:2.1.1 | | monitoring, all, uptime-kuma | monitor, traefik | | always |
|
||||
| mtls-bridge | mtls-bridge | | /home/nixos/docker/monitoring/mtls-bridge | monitoring, all, mtls-bridge | monitor, traefik | | unless-stopped |
|
||||
| nextcloud-db | nextcloud-db | mariadb:11.4 | | apps, all, nextcloud | nextcloud | | always |
|
||||
| nextcloud-redis | nextcloud-redis | redis | | apps, all, nextcloud | nextcloud | | always |
|
||||
| nextcloud-webapp | nextcloud-webapp | | /home/nixos/docker/apps/nextcloud | apps, all, nextcloud | nextcloud, traefik | | always |
|
||||
| node-exporter | node-exporter | prom/node-exporter:latest | | monitoring, all, node-exporter, prometheus | monitor | | unless-stopped |
|
||||
| node-red | node-red | | /home/nixos/docker/monitoring/node-red | monitoring, all, node-red | monitor, traefik | | unless-stopped |
|
||||
| passbolt-db | passbolt-db | mariadb:12 | | apps, all, passbolt | passbolt | | always |
|
||||
| passbolt-webapp | passbolt-webapp | passbolt/passbolt:latest-ce | | apps, all, passbolt | passbolt, traefik | | always |
|
||||
| pihole-exporter | pihole-exporter | ekofr/pihole-exporter:latest | | monitoring, all, pihole-exporter, prometheus | monitor | {'mode': 'ingress', 'target': 9617, 'published': '9617', 'protocol': 'tcp'} | unless-stopped |
|
||||
| portainer | portainer | portainer/portainer-ce:latest | | monitoring, all, portainer | traefik | | unless-stopped |
|
||||
| prometheus | prometheus | prom/prometheus:latest | | monitoring, all, prometheus | monitor, traefik | | unless-stopped |
|
||||
| searxng-webapp | searxng-webapp | searxng/searxng | | apps, all, searxng | traefik | | always |
|
||||
| telegraf | telegraf | telegraf:latest | | monitoring, all, telegraf, prometheus | monitor | | unless-stopped |
|
||||
| traefik | traefik | traefik:3 | /home/nixos/docker/core | core, all, traefik | traefik | {'mode': 'ingress', 'target': 80, 'published': '80', 'protocol': 'tcp'}, {'mode': 'ingress', 'target': 443, 'published': '443', 'protocol': 'tcp'} | always |
|
||||
|
||||
## Networks
|
||||
|
||||
| Network | Driver | External |
|
||||
|---|---|---|
|
||||
| gramps | | False |
|
||||
| monitor | | False |
|
||||
| nextcloud | | False |
|
||||
| passbolt | | False |
|
||||
| traefik | bridge | False |
|
||||
|
||||
## Volumes
|
||||
|
||||
| Volume | External |
|
||||
|---|---|
|
||||
@@ -0,0 +1,19 @@
|
||||
# Infrastructure diagrams
|
||||
|
||||
## Physical / virtual topology
|
||||
|
||||
This view groups containers by inferred host and service role (edge/proxy/auth, monitoring, automation, apps, and supporting storage/services).
|
||||
|
||||
<div class="diagram-wrap">
|
||||
<img src="physical-topology.svg" alt="Physical topology">
|
||||
</div>
|
||||
|
||||
## Docker, Traefik and Dynu routing
|
||||
|
||||
This view shows sanitised public DNS names flowing to Traefik, then to exposed Docker services, with backend Docker network membership shown as secondary context.
|
||||
|
||||
_Diagrams are generated from Compose data and Traefik labels._
|
||||
|
||||
<div class="diagram-wrap">
|
||||
<img src="docker-traefik-dynu.svg" alt="Docker Traefik Dynu">
|
||||
</div>
|
||||
@@ -0,0 +1,439 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: Compose Pages: 1 -->
|
||||
<svg width="334pt" height="1502pt"
|
||||
viewBox="0.00 0.00 334.49 1502.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1498)">
|
||||
<title>Compose</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1498 330.49,-1498 330.49,4 -4,4"/>
|
||||
<!-- svc:authelia -->
|
||||
<g id="node1" class="node">
|
||||
<title>svc:authelia</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="126,-738 54,-738 54,-702 126,-702 126,-738"/>
|
||||
<text text-anchor="middle" x="90" y="-716.3" font-family="Helvetica,sans-Serif" font-size="14.00">authelia</text>
|
||||
</g>
|
||||
<!-- net:traefik -->
|
||||
<g id="node33" class="node">
|
||||
<title>net:traefik</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-774" rx="40.09" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-770.3" font-family="Helvetica,sans-Serif" font-size="14.00">traefik</text>
|
||||
</g>
|
||||
<!-- svc:authelia->net:traefik -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>svc:authelia->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M126.41,-730.67C155.5,-739.43 196.8,-751.87 227.69,-761.18"/>
|
||||
<polygon fill="black" stroke="black" points="226.74,-764.55 237.32,-764.08 228.76,-757.85 226.74,-764.55"/>
|
||||
</g>
|
||||
<!-- svc:crowdsec -->
|
||||
<g id="node2" class="node">
|
||||
<title>svc:crowdsec</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="130.5,-684 49.5,-684 49.5,-648 130.5,-648 130.5,-684"/>
|
||||
<text text-anchor="middle" x="90" y="-662.3" font-family="Helvetica,sans-Serif" font-size="14.00">crowdsec</text>
|
||||
</g>
|
||||
<!-- svc:crowdsec->net:traefik -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>svc:crowdsec->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M130.61,-674.06C146.59,-678.3 164.81,-684.44 180,-693 206.25,-707.78 231.35,-731.35 248.38,-749.26"/>
|
||||
<polygon fill="black" stroke="black" points="246.24,-752.1 255.62,-757.03 251.37,-747.33 246.24,-752.1"/>
|
||||
</g>
|
||||
<!-- svc:docker-socket-proxy -->
|
||||
<g id="node3" class="node">
|
||||
<title>svc:docker-socket-proxy</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="167.5,-954 12.5,-954 12.5,-918 167.5,-918 167.5,-954"/>
|
||||
<text text-anchor="middle" x="90" y="-932.3" font-family="Helvetica,sans-Serif" font-size="14.00">docker-socket-proxy</text>
|
||||
</g>
|
||||
<!-- net:monitor -->
|
||||
<g id="node30" class="node">
|
||||
<title>net:monitor</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-1206" rx="46.29" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-1202.3" font-family="Helvetica,sans-Serif" font-size="14.00">monitor</text>
|
||||
</g>
|
||||
<!-- svc:docker-socket-proxy->net:monitor -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>svc:docker-socket-proxy->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M167.53,-953.71C172.02,-956.38 176.24,-959.46 180,-963 242.3,-1021.6 261.83,-1127.12 267.77,-1177.6"/>
|
||||
<polygon fill="black" stroke="black" points="264.31,-1178.21 268.86,-1187.78 271.27,-1177.46 264.31,-1178.21"/>
|
||||
</g>
|
||||
<!-- svc:docker-socket-proxy->net:traefik -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>svc:docker-socket-proxy->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M165.31,-917.9C170.5,-915.32 175.46,-912.37 180,-909 218.01,-880.82 244.97,-831.56 259.03,-800.98"/>
|
||||
<polygon fill="black" stroke="black" points="262.27,-802.31 263.14,-791.76 255.87,-799.46 262.27,-802.31"/>
|
||||
</g>
|
||||
<!-- svc:docker-update-exporter -->
|
||||
<g id="node4" class="node">
|
||||
<title>svc:docker-update-exporter</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="180,-1494 0,-1494 0,-1458 180,-1458 180,-1494"/>
|
||||
<text text-anchor="middle" x="90" y="-1472.3" font-family="Helvetica,sans-Serif" font-size="14.00">docker-update-exporter</text>
|
||||
</g>
|
||||
<!-- svc:docker-update-exporter->net:monitor -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>svc:docker-update-exporter->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M168.37,-1457.79C172.55,-1455.23 176.47,-1452.32 180,-1449 242.3,-1390.4 261.83,-1284.88 267.77,-1234.4"/>
|
||||
<polygon fill="black" stroke="black" points="271.27,-1234.54 268.86,-1224.22 264.31,-1233.79 271.27,-1234.54"/>
|
||||
</g>
|
||||
<!-- svc:error-pages -->
|
||||
<g id="node5" class="node">
|
||||
<title>svc:error-pages</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="137.5,-630 42.5,-630 42.5,-594 137.5,-594 137.5,-630"/>
|
||||
<text text-anchor="middle" x="90" y="-608.3" font-family="Helvetica,sans-Serif" font-size="14.00">error-pages</text>
|
||||
</g>
|
||||
<!-- svc:error-pages->net:traefik -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>svc:error-pages->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M137.71,-619.76C152.25,-623.79 167.68,-629.86 180,-639 218.01,-667.18 244.97,-716.44 259.03,-747.02"/>
|
||||
<polygon fill="black" stroke="black" points="255.87,-748.54 263.14,-756.24 262.27,-745.69 255.87,-748.54"/>
|
||||
</g>
|
||||
<!-- svc:gitea -->
|
||||
<g id="node6" class="node">
|
||||
<title>svc:gitea</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="117,-576 63,-576 63,-540 117,-540 117,-576"/>
|
||||
<text text-anchor="middle" x="90" y="-554.3" font-family="Helvetica,sans-Serif" font-size="14.00">gitea</text>
|
||||
</g>
|
||||
<!-- svc:gitea->net:traefik -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>svc:gitea->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M117.07,-560.28C136.41,-563.2 162.37,-569.85 180,-585 229.98,-627.95 254.42,-704.8 264.44,-746.04"/>
|
||||
<polygon fill="black" stroke="black" points="261.07,-747 266.73,-755.95 267.88,-745.42 261.07,-747"/>
|
||||
</g>
|
||||
<!-- svc:gitea-runner -->
|
||||
<g id="node7" class="node">
|
||||
<title>svc:gitea-runner</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="142,-522 38,-522 38,-486 142,-486 142,-522"/>
|
||||
<text text-anchor="middle" x="90" y="-500.3" font-family="Helvetica,sans-Serif" font-size="14.00">gitea-runner</text>
|
||||
</g>
|
||||
<!-- svc:gitea-runner->net:traefik -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>svc:gitea-runner->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M142.31,-511.04C155.9,-515.03 169.65,-521.26 180,-531 242.3,-589.6 261.83,-695.12 267.77,-745.6"/>
|
||||
<polygon fill="black" stroke="black" points="264.31,-746.21 268.86,-755.78 271.27,-745.46 264.31,-746.21"/>
|
||||
</g>
|
||||
<!-- svc:gotify -->
|
||||
<g id="node8" class="node">
|
||||
<title>svc:gotify</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="118,-468 62,-468 62,-432 118,-432 118,-468"/>
|
||||
<text text-anchor="middle" x="90" y="-446.3" font-family="Helvetica,sans-Serif" font-size="14.00">gotify</text>
|
||||
</g>
|
||||
<!-- svc:gotify->net:traefik -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>svc:gotify->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M118.17,-451.6C137.81,-454.16 163.67,-460.68 180,-477 254.99,-551.96 268.02,-687.13 270.03,-745.69"/>
|
||||
<polygon fill="black" stroke="black" points="266.53,-745.79 270.29,-755.69 273.53,-745.61 266.53,-745.79"/>
|
||||
</g>
|
||||
<!-- svc:grafana -->
|
||||
<g id="node9" class="node">
|
||||
<title>svc:grafana</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="125.5,-1278 54.5,-1278 54.5,-1242 125.5,-1242 125.5,-1278"/>
|
||||
<text text-anchor="middle" x="90" y="-1256.3" font-family="Helvetica,sans-Serif" font-size="14.00">grafana</text>
|
||||
</g>
|
||||
<!-- svc:grafana->net:monitor -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>svc:grafana->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M125.56,-1249.59C153.74,-1241.1 193.78,-1229.04 224.6,-1219.75"/>
|
||||
<polygon fill="black" stroke="black" points="225.69,-1223.08 234.26,-1216.84 223.67,-1216.38 225.69,-1223.08"/>
|
||||
</g>
|
||||
<!-- svc:grafana->net:traefik -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>svc:grafana->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M125.54,-1257.93C144.23,-1254.97 166.25,-1248.21 180,-1233 238.17,-1168.67 262.1,-892.17 268.43,-802.32"/>
|
||||
<polygon fill="black" stroke="black" points="271.93,-802.3 269.13,-792.08 264.95,-801.82 271.93,-802.3"/>
|
||||
</g>
|
||||
<!-- svc:gramps-redis -->
|
||||
<g id="node10" class="node">
|
||||
<title>svc:gramps-redis</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="144.5,-360 35.5,-360 35.5,-324 144.5,-324 144.5,-360"/>
|
||||
<text text-anchor="middle" x="90" y="-338.3" font-family="Helvetica,sans-Serif" font-size="14.00">gramps-redis</text>
|
||||
</g>
|
||||
<!-- net:gramps -->
|
||||
<g id="node29" class="node">
|
||||
<title>net:gramps</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-342" rx="45.49" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-338.3" font-family="Helvetica,sans-Serif" font-size="14.00">gramps</text>
|
||||
</g>
|
||||
<!-- svc:gramps-redis->net:gramps -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>svc:gramps-redis->net:gramps</title>
|
||||
<path fill="none" stroke="black" d="M144.78,-342C167.14,-342 193.05,-342 215.51,-342"/>
|
||||
<polygon fill="black" stroke="black" points="215.56,-345.5 225.56,-342 215.56,-338.5 215.56,-345.5"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb -->
|
||||
<g id="node11" class="node">
|
||||
<title>svc:grampsweb</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="139,-414 41,-414 41,-378 139,-378 139,-414"/>
|
||||
<text text-anchor="middle" x="90" y="-392.3" font-family="Helvetica,sans-Serif" font-size="14.00">grampsweb</text>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:gramps -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>svc:grampsweb->net:gramps</title>
|
||||
<path fill="none" stroke="black" d="M139.03,-381.53C165.7,-373.5 198.68,-363.56 224.9,-355.66"/>
|
||||
<polygon fill="black" stroke="black" points="226.03,-358.98 234.59,-352.74 224.01,-352.27 226.03,-358.98"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:traefik -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>svc:grampsweb->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M139.27,-401.34C154.08,-405.21 169.28,-411.81 180,-423 225.12,-470.09 256.34,-671.08 266.59,-745.84"/>
|
||||
<polygon fill="black" stroke="black" points="263.14,-746.48 267.95,-755.92 270.08,-745.55 263.14,-746.48"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb_celery -->
|
||||
<g id="node12" class="node">
|
||||
<title>svc:grampsweb_celery</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="163.5,-306 16.5,-306 16.5,-270 163.5,-270 163.5,-306"/>
|
||||
<text text-anchor="middle" x="90" y="-284.3" font-family="Helvetica,sans-Serif" font-size="14.00">grampsweb_celery</text>
|
||||
</g>
|
||||
<!-- svc:grampsweb_celery->net:gramps -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>svc:grampsweb_celery->net:gramps</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-306.13C175.28,-313.39 202.57,-321.61 224.94,-328.35"/>
|
||||
<polygon fill="black" stroke="black" points="223.94,-331.71 234.52,-331.24 225.96,-325 223.94,-331.71"/>
|
||||
</g>
|
||||
<!-- svc:influxdb -->
|
||||
<g id="node13" class="node">
|
||||
<title>svc:influxdb</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="127,-1224 53,-1224 53,-1188 127,-1188 127,-1224"/>
|
||||
<text text-anchor="middle" x="90" y="-1202.3" font-family="Helvetica,sans-Serif" font-size="14.00">influxdb</text>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:monitor -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>svc:influxdb->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M127.27,-1206C152.33,-1206 186.13,-1206 214.57,-1206"/>
|
||||
<polygon fill="black" stroke="black" points="214.79,-1209.5 224.79,-1206 214.79,-1202.5 214.79,-1209.5"/>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:traefik -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>svc:influxdb->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M127.17,-1203.5C145.42,-1200.36 166.5,-1193.56 180,-1179 231.57,-1123.4 259.36,-885.3 267.6,-802.51"/>
|
||||
<polygon fill="black" stroke="black" points="271.1,-802.62 268.59,-792.33 264.14,-801.94 271.1,-802.62"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma -->
|
||||
<g id="node14" class="node">
|
||||
<title>svc:monitor-kuma</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="146.5,-1170 33.5,-1170 33.5,-1134 146.5,-1134 146.5,-1170"/>
|
||||
<text text-anchor="middle" x="90" y="-1148.3" font-family="Helvetica,sans-Serif" font-size="14.00">monitor-kuma</text>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:monitor -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>svc:monitor-kuma->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M146.73,-1168.79C171.73,-1176.32 200.85,-1185.1 224.54,-1192.23"/>
|
||||
<polygon fill="black" stroke="black" points="223.75,-1195.65 234.33,-1195.18 225.77,-1188.94 223.75,-1195.65"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:traefik -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>svc:monitor-kuma->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M146.69,-1144.49C159.02,-1140.46 171.06,-1134.33 180,-1125 225.12,-1077.91 256.34,-876.92 266.59,-802.16"/>
|
||||
<polygon fill="black" stroke="black" points="270.08,-802.45 267.95,-792.08 263.14,-801.52 270.08,-802.45"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge -->
|
||||
<g id="node15" class="node">
|
||||
<title>svc:mtls-bridge</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="138.5,-1116 41.5,-1116 41.5,-1080 138.5,-1080 138.5,-1116"/>
|
||||
<text text-anchor="middle" x="90" y="-1094.3" font-family="Helvetica,sans-Serif" font-size="14.00">mtls-bridge</text>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:monitor -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>svc:mtls-bridge->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M138.76,-1108.34C152.58,-1112.41 167.34,-1117.87 180,-1125 206.25,-1139.78 231.35,-1163.35 248.38,-1181.26"/>
|
||||
<polygon fill="black" stroke="black" points="246.24,-1184.1 255.62,-1189.03 251.37,-1179.33 246.24,-1184.1"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:traefik -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>svc:mtls-bridge->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M138.53,-1092.51C153.46,-1088.63 168.92,-1082.07 180,-1071 254.99,-996.04 268.02,-860.87 270.03,-802.31"/>
|
||||
<polygon fill="black" stroke="black" points="273.53,-802.39 270.29,-792.31 266.53,-802.21 273.53,-802.39"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-db -->
|
||||
<g id="node16" class="node">
|
||||
<title>svc:nextcloud-db</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="144,-90 36,-90 36,-54 144,-54 144,-90"/>
|
||||
<text text-anchor="middle" x="90" y="-68.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud-db</text>
|
||||
</g>
|
||||
<!-- net:nextcloud -->
|
||||
<g id="node31" class="node">
|
||||
<title>net:nextcloud</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-180" rx="55.49" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-176.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-db->net:nextcloud -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>svc:nextcloud-db->net:nextcloud</title>
|
||||
<path fill="none" stroke="black" d="M144.33,-84.04C156.48,-87.9 169.03,-92.82 180,-99 206.09,-113.7 231.04,-137.06 248.07,-154.93"/>
|
||||
<polygon fill="black" stroke="black" points="245.93,-157.77 255.31,-162.7 251.06,-153 245.93,-157.77"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-redis -->
|
||||
<g id="node17" class="node">
|
||||
<title>svc:nextcloud-redis</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="152,-198 28,-198 28,-162 152,-162 152,-198"/>
|
||||
<text text-anchor="middle" x="90" y="-176.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud-redis</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-redis->net:nextcloud -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>svc:nextcloud-redis->net:nextcloud</title>
|
||||
<path fill="none" stroke="black" d="M152.18,-180C169.48,-180 188.35,-180 205.83,-180"/>
|
||||
<polygon fill="black" stroke="black" points="205.94,-183.5 215.94,-180 205.94,-176.5 205.94,-183.5"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp -->
|
||||
<g id="node18" class="node">
|
||||
<title>svc:nextcloud-webapp</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="162.5,-252 17.5,-252 17.5,-216 162.5,-216 162.5,-252"/>
|
||||
<text text-anchor="middle" x="90" y="-230.3" font-family="Helvetica,sans-Serif" font-size="14.00">nextcloud-webapp</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:nextcloud -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:nextcloud</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-215.87C173.57,-209.12 198.72,-201.55 220.11,-195.1"/>
|
||||
<polygon fill="black" stroke="black" points="221.4,-198.37 229.97,-192.13 219.38,-191.67 221.4,-198.37"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:traefik -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M162.89,-247.71C169.31,-251.2 175.18,-255.56 180,-261 212.66,-297.85 255.07,-643.36 267,-745.62"/>
|
||||
<polygon fill="black" stroke="black" points="263.55,-746.27 268.18,-755.8 270.51,-745.47 263.55,-746.27"/>
|
||||
</g>
|
||||
<!-- svc:node-exporter -->
|
||||
<g id="node19" class="node">
|
||||
<title>svc:node-exporter</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="148,-1440 32,-1440 32,-1404 148,-1404 148,-1440"/>
|
||||
<text text-anchor="middle" x="90" y="-1418.3" font-family="Helvetica,sans-Serif" font-size="14.00">node-exporter</text>
|
||||
</g>
|
||||
<!-- svc:node-exporter->net:monitor -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>svc:node-exporter->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M148.25,-1412.27C159.68,-1408.33 170.94,-1402.79 180,-1395 229.98,-1352.05 254.42,-1275.2 264.44,-1233.96"/>
|
||||
<polygon fill="black" stroke="black" points="267.88,-1234.58 266.73,-1224.05 261.07,-1233 267.88,-1234.58"/>
|
||||
</g>
|
||||
<!-- svc:node-red -->
|
||||
<g id="node20" class="node">
|
||||
<title>svc:node-red</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="129.5,-1062 50.5,-1062 50.5,-1026 129.5,-1026 129.5,-1062"/>
|
||||
<text text-anchor="middle" x="90" y="-1040.3" font-family="Helvetica,sans-Serif" font-size="14.00">node-red</text>
|
||||
</g>
|
||||
<!-- svc:node-red->net:monitor -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>svc:node-red->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M129.57,-1049.69C146.29,-1053.6 165.34,-1060.13 180,-1071 218.01,-1099.18 244.97,-1148.44 259.03,-1179.02"/>
|
||||
<polygon fill="black" stroke="black" points="255.87,-1180.54 263.14,-1188.24 262.27,-1177.69 255.87,-1180.54"/>
|
||||
</g>
|
||||
<!-- svc:node-red->net:traefik -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>svc:node-red->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M129.95,-1040.03C147.14,-1036.46 166.48,-1029.72 180,-1017 242.3,-958.4 261.83,-852.88 267.77,-802.4"/>
|
||||
<polygon fill="black" stroke="black" points="271.27,-802.54 268.86,-792.22 264.31,-801.79 271.27,-802.54"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-db -->
|
||||
<g id="node21" class="node">
|
||||
<title>svc:passbolt-db</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="139,-36 41,-36 41,0 139,0 139,-36"/>
|
||||
<text text-anchor="middle" x="90" y="-14.3" font-family="Helvetica,sans-Serif" font-size="14.00">passbolt-db</text>
|
||||
</g>
|
||||
<!-- net:passbolt -->
|
||||
<g id="node32" class="node">
|
||||
<title>net:passbolt</title>
|
||||
<ellipse fill="#f4f4f4" stroke="black" cx="271.25" cy="-72" rx="48.99" ry="18"/>
|
||||
<text text-anchor="middle" x="271.25" y="-68.3" font-family="Helvetica,sans-Serif" font-size="14.00">passbolt</text>
|
||||
</g>
|
||||
<!-- svc:passbolt-db->net:passbolt -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>svc:passbolt-db->net:passbolt</title>
|
||||
<path fill="none" stroke="black" d="M139.03,-32.47C165.15,-40.34 197.32,-50.03 223.27,-57.85"/>
|
||||
<polygon fill="black" stroke="black" points="222.3,-61.21 232.88,-60.74 224.32,-54.51 222.3,-61.21"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp -->
|
||||
<g id="node22" class="node">
|
||||
<title>svc:passbolt-webapp</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="157.5,-144 22.5,-144 22.5,-108 157.5,-108 157.5,-144"/>
|
||||
<text text-anchor="middle" x="90" y="-122.3" font-family="Helvetica,sans-Serif" font-size="14.00">passbolt-webapp</text>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:passbolt -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>svc:passbolt-webapp->net:passbolt</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-107.87C174.64,-100.8 201.13,-92.82 223.16,-86.19"/>
|
||||
<polygon fill="black" stroke="black" points="224.37,-89.48 232.94,-83.24 222.35,-82.77 224.37,-89.48"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:traefik -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>svc:passbolt-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M157.85,-136.93C166.26,-140.81 174,-146.02 180,-153 189.88,-164.49 250.79,-625.33 266.53,-745.56"/>
|
||||
<polygon fill="black" stroke="black" points="263.1,-746.33 267.87,-755.8 270.04,-745.43 263.1,-746.33"/>
|
||||
</g>
|
||||
<!-- svc:pihole-exporter -->
|
||||
<g id="node23" class="node">
|
||||
<title>svc:pihole-exporter</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="151.5,-1386 28.5,-1386 28.5,-1350 151.5,-1350 151.5,-1386"/>
|
||||
<text text-anchor="middle" x="90" y="-1364.3" font-family="Helvetica,sans-Serif" font-size="14.00">pihole-exporter</text>
|
||||
</g>
|
||||
<!-- svc:pihole-exporter->net:monitor -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>svc:pihole-exporter->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M151.62,-1355.78C161.68,-1352.07 171.57,-1347.25 180,-1341 218.01,-1312.82 244.97,-1263.56 259.03,-1232.98"/>
|
||||
<polygon fill="black" stroke="black" points="262.27,-1234.31 263.14,-1223.76 255.87,-1231.46 262.27,-1234.31"/>
|
||||
</g>
|
||||
<!-- svc:portainer -->
|
||||
<g id="node24" class="node">
|
||||
<title>svc:portainer</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="130,-900 50,-900 50,-864 130,-864 130,-900"/>
|
||||
<text text-anchor="middle" x="90" y="-878.3" font-family="Helvetica,sans-Serif" font-size="14.00">portainer</text>
|
||||
</g>
|
||||
<!-- svc:portainer->net:traefik -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>svc:portainer->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M130.17,-874.06C146.25,-869.82 164.67,-863.64 180,-855 206.25,-840.22 231.35,-816.65 248.38,-798.74"/>
|
||||
<polygon fill="black" stroke="black" points="251.37,-800.67 255.62,-790.97 246.24,-795.9 251.37,-800.67"/>
|
||||
</g>
|
||||
<!-- svc:prometheus -->
|
||||
<g id="node25" class="node">
|
||||
<title>svc:prometheus</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="140,-1008 40,-1008 40,-972 140,-972 140,-1008"/>
|
||||
<text text-anchor="middle" x="90" y="-986.3" font-family="Helvetica,sans-Serif" font-size="14.00">prometheus</text>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:monitor -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>svc:prometheus->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M140.37,-997.26C154.39,-1001.24 168.86,-1007.42 180,-1017 229.98,-1059.95 254.42,-1136.8 264.44,-1178.04"/>
|
||||
<polygon fill="black" stroke="black" points="261.07,-1179 266.73,-1187.95 267.88,-1177.42 261.07,-1179"/>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:traefik -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>svc:prometheus->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M140.37,-982.74C154.39,-978.76 168.86,-972.58 180,-963 229.98,-920.05 254.42,-843.2 264.44,-801.96"/>
|
||||
<polygon fill="black" stroke="black" points="267.88,-802.58 266.73,-792.05 261.07,-801 267.88,-802.58"/>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp -->
|
||||
<g id="node26" class="node">
|
||||
<title>svc:searxng-webapp</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="156,-846 24,-846 24,-810 156,-810 156,-846"/>
|
||||
<text text-anchor="middle" x="90" y="-824.3" font-family="Helvetica,sans-Serif" font-size="14.00">searxng-webapp</text>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp->net:traefik -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>svc:searxng-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M151.18,-809.87C176.26,-802.32 204.79,-793.72 227.63,-786.84"/>
|
||||
<polygon fill="black" stroke="black" points="228.81,-790.14 237.37,-783.9 226.79,-783.44 228.81,-790.14"/>
|
||||
</g>
|
||||
<!-- svc:telegraf -->
|
||||
<g id="node27" class="node">
|
||||
<title>svc:telegraf</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="125.5,-1332 54.5,-1332 54.5,-1296 125.5,-1296 125.5,-1332"/>
|
||||
<text text-anchor="middle" x="90" y="-1310.3" font-family="Helvetica,sans-Serif" font-size="14.00">telegraf</text>
|
||||
</g>
|
||||
<!-- svc:telegraf->net:monitor -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>svc:telegraf->net:monitor</title>
|
||||
<path fill="none" stroke="black" d="M125.8,-1307.18C142.85,-1302.93 163.26,-1296.43 180,-1287 206.25,-1272.22 231.35,-1248.65 248.38,-1230.74"/>
|
||||
<polygon fill="black" stroke="black" points="251.37,-1232.67 255.62,-1222.97 246.24,-1227.9 251.37,-1232.67"/>
|
||||
</g>
|
||||
<!-- svc:traefik -->
|
||||
<g id="node28" class="node">
|
||||
<title>svc:traefik</title>
|
||||
<polygon fill="#dfefff" stroke="black" points="121,-792 59,-792 59,-756 121,-756 121,-792"/>
|
||||
<text text-anchor="middle" x="90" y="-770.3" font-family="Helvetica,sans-Serif" font-size="14.00">traefik</text>
|
||||
</g>
|
||||
<!-- svc:traefik->net:traefik -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>svc:traefik->net:traefik</title>
|
||||
<path fill="none" stroke="black" d="M121,-774C148.16,-774 188.69,-774 220.66,-774"/>
|
||||
<polygon fill="black" stroke="black" points="220.71,-777.5 230.71,-774 220.71,-770.5 220.71,-777.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,611 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: DockerTraefikDynu Pages: 1 -->
|
||||
<svg width="1113pt" height="1536pt"
|
||||
viewBox="0.00 0.00 1113.39 1536.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1532)">
|
||||
<title>DockerTraefikDynu</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1532 1109.39,-1532 1109.39,4 -4,4"/>
|
||||
<g id="clust2" class="cluster">
|
||||
<title>cluster_networks</title>
|
||||
<path fill="none" stroke="#d1d5db" stroke-dasharray="5,2" d="M922,-650C922,-650 1093.39,-650 1093.39,-650 1099.39,-650 1105.39,-656 1105.39,-662 1105.39,-662 1105.39,-1117 1105.39,-1117 1105.39,-1123 1099.39,-1129 1093.39,-1129 1093.39,-1129 922,-1129 922,-1129 916,-1129 910,-1123 910,-1117 910,-1117 910,-662 910,-662 910,-656 916,-650 922,-650"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-1113.8" font-family="Helvetica,sans-Serif" font-size="14.00">Docker backend networks</text>
|
||||
</g>
|
||||
<!-- dynu -->
|
||||
<g id="node1" class="node">
|
||||
<title>dynu</title>
|
||||
<path fill="#fde68a" stroke="black" d="M374,-789C374,-789 282,-789 282,-789 276,-789 270,-783 270,-777 270,-777 270,-765 270,-765 270,-759 276,-753 282,-753 282,-753 374,-753 374,-753 380,-753 386,-759 386,-765 386,-765 386,-777 386,-777 386,-783 380,-789 374,-789"/>
|
||||
<text text-anchor="middle" x="328" y="-768.2" font-family="Helvetica,sans-Serif" font-size="11.00">Dynu / Public DNS</text>
|
||||
</g>
|
||||
<!-- svc:traefik -->
|
||||
<g id="node2" class="node">
|
||||
<title>svc:traefik</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M559,-789C559,-789 513,-789 513,-789 507,-789 501,-783 501,-777 501,-777 501,-765 501,-765 501,-759 507,-753 513,-753 513,-753 559,-753 559,-753 565,-753 571,-759 571,-765 571,-765 571,-777 571,-777 571,-783 565,-789 559,-789"/>
|
||||
<text text-anchor="middle" x="536" y="-774.2" font-family="Helvetica,sans-Serif" font-size="11.00">traefik</text>
|
||||
<text text-anchor="middle" x="536" y="-762.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- dynu->svc:traefik -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>dynu->svc:traefik</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.6" d="M386.11,-771C419.13,-771 460.06,-771 490.64,-771"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.6" points="491,-774.5 501,-771 491,-767.5 491,-774.5"/>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:traefik -->
|
||||
<g id="edge30" class="edge">
|
||||
<title>svc:traefik->svc:traefik</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M511.86,-789.35C485.91,-817.82 493.95,-854 536,-854 574.1,-854 584.29,-824.29 566.55,-797.52"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="569.07,-795.06 560.14,-789.35 563.55,-799.38 569.07,-795.06"/>
|
||||
</g>
|
||||
<!-- svc:authelia -->
|
||||
<g id="node3" class="node">
|
||||
<title>svc:authelia</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M763,-789C763,-789 726,-789 726,-789 720,-789 714,-783 714,-777 714,-777 714,-765 714,-765 714,-759 720,-753 726,-753 726,-753 763,-753 763,-753 769,-753 775,-759 775,-765 775,-765 775,-777 775,-777 775,-783 769,-789 763,-789"/>
|
||||
<text text-anchor="middle" x="744.5" y="-774.2" font-family="Helvetica,sans-Serif" font-size="11.00">authelia</text>
|
||||
<text text-anchor="middle" x="744.5" y="-762.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:authelia -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>svc:traefik->svc:authelia</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M571.1,-771C607.43,-771 664.91,-771 703.39,-771"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="703.71,-774.5 713.71,-771 703.71,-767.5 703.71,-774.5"/>
|
||||
</g>
|
||||
<!-- svc:gitea -->
|
||||
<g id="node5" class="node">
|
||||
<title>svc:gitea</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M759.5,-688C759.5,-688 729.5,-688 729.5,-688 723.5,-688 717.5,-682 717.5,-676 717.5,-676 717.5,-656 717.5,-656 717.5,-650 723.5,-644 729.5,-644 729.5,-644 759.5,-644 759.5,-644 765.5,-644 771.5,-650 771.5,-656 771.5,-656 771.5,-676 771.5,-676 771.5,-682 765.5,-688 759.5,-688"/>
|
||||
<text text-anchor="middle" x="744.5" y="-675.2" font-family="Helvetica,sans-Serif" font-size="11.00">gitea</text>
|
||||
<text text-anchor="middle" x="744.5" y="-663.2" font-family="Helvetica,sans-Serif" font-size="11.00">:3000</text>
|
||||
<text text-anchor="middle" x="744.5" y="-651.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:gitea -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>svc:traefik->svc:gitea</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M571.1,-753.66C608.91,-734.44 669.61,-703.57 707.98,-684.06"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="709.89,-687.01 717.22,-679.36 706.72,-680.77 709.89,-687.01"/>
|
||||
</g>
|
||||
<!-- svc:gotify -->
|
||||
<g id="node7" class="node">
|
||||
<title>svc:gotify</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M759.5,-579C759.5,-579 729.5,-579 729.5,-579 723.5,-579 717.5,-573 717.5,-567 717.5,-567 717.5,-555 717.5,-555 717.5,-549 723.5,-543 729.5,-543 729.5,-543 759.5,-543 759.5,-543 765.5,-543 771.5,-549 771.5,-555 771.5,-555 771.5,-567 771.5,-567 771.5,-573 765.5,-579 759.5,-579"/>
|
||||
<text text-anchor="middle" x="744.5" y="-564.2" font-family="Helvetica,sans-Serif" font-size="11.00">gotify</text>
|
||||
<text text-anchor="middle" x="744.5" y="-552.2" font-family="Helvetica,sans-Serif" font-size="11.00">:80</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:gotify -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>svc:traefik->svc:gotify</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M553.8,-752.96C592.77,-711.11 686,-611 686,-611 686,-611 700.43,-598.45 714.83,-585.93"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="717.49,-588.25 722.74,-579.05 712.9,-582.97 717.49,-588.25"/>
|
||||
</g>
|
||||
<!-- svc:grafana -->
|
||||
<g id="node9" class="node">
|
||||
<title>svc:grafana</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M762,-999C762,-999 727,-999 727,-999 721,-999 715,-993 715,-987 715,-987 715,-975 715,-975 715,-969 721,-963 727,-963 727,-963 762,-963 762,-963 768,-963 774,-969 774,-975 774,-975 774,-987 774,-987 774,-993 768,-999 762,-999"/>
|
||||
<text text-anchor="middle" x="744.5" y="-984.2" font-family="Helvetica,sans-Serif" font-size="11.00">grafana</text>
|
||||
<text text-anchor="middle" x="744.5" y="-972.2" font-family="Helvetica,sans-Serif" font-size="11.00">:3000</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:grafana -->
|
||||
<g id="edge8" class="edge">
|
||||
<title>svc:traefik->svc:grafana</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M553.8,-789.04C592.77,-830.89 686,-931 686,-931 686,-931 700.43,-943.55 714.83,-956.07"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="712.9,-959.03 722.74,-962.95 717.49,-953.75 712.9,-959.03"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb -->
|
||||
<g id="node11" class="node">
|
||||
<title>svc:grampsweb</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M772.5,-1528C772.5,-1528 716.5,-1528 716.5,-1528 710.5,-1528 704.5,-1522 704.5,-1516 704.5,-1516 704.5,-1504 704.5,-1504 704.5,-1498 710.5,-1492 716.5,-1492 716.5,-1492 772.5,-1492 772.5,-1492 778.5,-1492 784.5,-1498 784.5,-1504 784.5,-1504 784.5,-1516 784.5,-1516 784.5,-1522 778.5,-1528 772.5,-1528"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1507.2" font-family="Helvetica,sans-Serif" font-size="11.00">grampsweb</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:grampsweb -->
|
||||
<g id="edge10" class="edge">
|
||||
<title>svc:traefik->svc:grampsweb</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M540.91,-789.07C564.42,-897.78 686,-1460 686,-1460 686,-1460 700.43,-1472.55 714.83,-1485.07"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="712.9,-1488.03 722.74,-1491.95 717.49,-1482.75 712.9,-1488.03"/>
|
||||
</g>
|
||||
<!-- svc:influxdb -->
|
||||
<g id="node13" class="node">
|
||||
<title>svc:influxdb</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M767.5,-898C767.5,-898 721.5,-898 721.5,-898 715.5,-898 709.5,-892 709.5,-886 709.5,-886 709.5,-866 709.5,-866 709.5,-860 715.5,-854 721.5,-854 721.5,-854 767.5,-854 767.5,-854 773.5,-854 779.5,-860 779.5,-866 779.5,-866 779.5,-886 779.5,-886 779.5,-892 773.5,-898 767.5,-898"/>
|
||||
<text text-anchor="middle" x="744.5" y="-885.2" font-family="Helvetica,sans-Serif" font-size="11.00">influxdb</text>
|
||||
<text text-anchor="middle" x="744.5" y="-873.2" font-family="Helvetica,sans-Serif" font-size="11.00">:8086</text>
|
||||
<text text-anchor="middle" x="744.5" y="-861.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:influxdb -->
|
||||
<g id="edge12" class="edge">
|
||||
<title>svc:traefik->svc:influxdb</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M571.1,-788.34C606.37,-806.27 661.57,-834.34 699.97,-853.87"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="698.85,-857.22 709.35,-858.64 702.02,-850.98 698.85,-857.22"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma -->
|
||||
<g id="node15" class="node">
|
||||
<title>svc:monitor-kuma</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M778.5,-1427C778.5,-1427 710.5,-1427 710.5,-1427 704.5,-1427 698.5,-1421 698.5,-1415 698.5,-1415 698.5,-1403 698.5,-1403 698.5,-1397 704.5,-1391 710.5,-1391 710.5,-1391 778.5,-1391 778.5,-1391 784.5,-1391 790.5,-1397 790.5,-1403 790.5,-1403 790.5,-1415 790.5,-1415 790.5,-1421 784.5,-1427 778.5,-1427"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1412.2" font-family="Helvetica,sans-Serif" font-size="11.00">monitor-kuma</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1400.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:monitor-kuma -->
|
||||
<g id="edge14" class="edge">
|
||||
<title>svc:traefik->svc:monitor-kuma</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M541.62,-789.24C566.77,-888.49 686,-1359 686,-1359 686,-1359 700.43,-1371.55 714.83,-1384.07"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="712.9,-1387.03 722.74,-1390.95 717.49,-1381.75 712.9,-1387.03"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge -->
|
||||
<g id="node17" class="node">
|
||||
<title>svc:mtls-bridge</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M772,-1326C772,-1326 717,-1326 717,-1326 711,-1326 705,-1320 705,-1314 705,-1314 705,-1294 705,-1294 705,-1288 711,-1282 717,-1282 717,-1282 772,-1282 772,-1282 778,-1282 784,-1288 784,-1294 784,-1294 784,-1314 784,-1314 784,-1320 778,-1326 772,-1326"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1313.2" font-family="Helvetica,sans-Serif" font-size="11.00">mtls-bridge</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1301.2" font-family="Helvetica,sans-Serif" font-size="11.00">:8080</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1289.2" font-family="Helvetica,sans-Serif" font-size="11.00">[mTLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:mtls-bridge -->
|
||||
<g id="edge16" class="edge">
|
||||
<title>svc:traefik->svc:mtls-bridge</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M542.66,-789.19C569.88,-876.69 686,-1250 686,-1250 686,-1250 698.77,-1262 712.28,-1274.68"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="710.1,-1277.43 719.78,-1281.72 714.89,-1272.33 710.1,-1277.43"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp -->
|
||||
<g id="node19" class="node">
|
||||
<title>svc:nextcloud-webapp</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M791,-137C791,-137 698,-137 698,-137 692,-137 686,-131 686,-125 686,-125 686,-113 686,-113 686,-107 692,-101 698,-101 698,-101 791,-101 791,-101 797,-101 803,-107 803,-113 803,-113 803,-125 803,-125 803,-131 797,-137 791,-137"/>
|
||||
<text text-anchor="middle" x="744.5" y="-116.2" font-family="Helvetica,sans-Serif" font-size="11.00">nextcloud-webapp</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:nextcloud-webapp -->
|
||||
<g id="edge18" class="edge">
|
||||
<title>svc:traefik->svc:nextcloud-webapp</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M541.62,-752.85C566.77,-654.11 686,-186 686,-186 686,-186 704.96,-163.91 721.1,-145.1"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="723.93,-147.18 727.79,-137.31 718.62,-142.62 723.93,-147.18"/>
|
||||
</g>
|
||||
<!-- svc:node-red -->
|
||||
<g id="node21" class="node">
|
||||
<title>svc:node-red</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M767.5,-1217C767.5,-1217 721.5,-1217 721.5,-1217 715.5,-1217 709.5,-1211 709.5,-1205 709.5,-1205 709.5,-1185 709.5,-1185 709.5,-1179 715.5,-1173 721.5,-1173 721.5,-1173 767.5,-1173 767.5,-1173 773.5,-1173 779.5,-1179 779.5,-1185 779.5,-1185 779.5,-1205 779.5,-1205 779.5,-1211 773.5,-1217 767.5,-1217"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1204.2" font-family="Helvetica,sans-Serif" font-size="11.00">node-red</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1192.2" font-family="Helvetica,sans-Serif" font-size="11.00">:1880</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1180.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:node-red -->
|
||||
<g id="edge20" class="edge">
|
||||
<title>svc:traefik->svc:node-red</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M544.29,-789.1C574.2,-863.38 686,-1141 686,-1141 686,-1141 698.77,-1153 712.28,-1165.68"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="710.1,-1168.43 719.78,-1172.72 714.89,-1163.33 710.1,-1168.43"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp -->
|
||||
<g id="node23" class="node">
|
||||
<title>svc:passbolt-webapp</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M787.5,-36C787.5,-36 701.5,-36 701.5,-36 695.5,-36 689.5,-30 689.5,-24 689.5,-24 689.5,-12 689.5,-12 689.5,-6 695.5,0 701.5,0 701.5,0 787.5,0 787.5,0 793.5,0 799.5,-6 799.5,-12 799.5,-12 799.5,-24 799.5,-24 799.5,-30 793.5,-36 787.5,-36"/>
|
||||
<text text-anchor="middle" x="744.5" y="-15.2" font-family="Helvetica,sans-Serif" font-size="11.00">passbolt-webapp</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:passbolt-webapp -->
|
||||
<g id="edge22" class="edge">
|
||||
<title>svc:traefik->svc:passbolt-webapp</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M540.83,-752.92C564.15,-642.88 686,-68 686,-68 686,-68 700.43,-55.45 714.83,-42.93"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="717.49,-45.25 722.74,-36.05 712.9,-39.97 717.49,-45.25"/>
|
||||
</g>
|
||||
<!-- svc:portainer -->
|
||||
<g id="node25" class="node">
|
||||
<title>svc:portainer</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M766,-478C766,-478 723,-478 723,-478 717,-478 711,-472 711,-466 711,-466 711,-446 711,-446 711,-440 717,-434 723,-434 723,-434 766,-434 766,-434 772,-434 778,-440 778,-446 778,-446 778,-466 778,-466 778,-472 772,-478 766,-478"/>
|
||||
<text text-anchor="middle" x="744.5" y="-465.2" font-family="Helvetica,sans-Serif" font-size="11.00">portainer</text>
|
||||
<text text-anchor="middle" x="744.5" y="-453.2" font-family="Helvetica,sans-Serif" font-size="11.00">:9000</text>
|
||||
<text text-anchor="middle" x="744.5" y="-441.2" font-family="Helvetica,sans-Serif" font-size="11.00">[TLS]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:portainer -->
|
||||
<g id="edge24" class="edge">
|
||||
<title>svc:traefik->svc:portainer</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M547.48,-752.65C581.39,-693.24 686,-510 686,-510 686,-510 698.77,-498 712.28,-485.32"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="714.89,-487.67 719.78,-478.28 710.1,-482.57 714.89,-487.67"/>
|
||||
</g>
|
||||
<!-- svc:prometheus -->
|
||||
<g id="node27" class="node">
|
||||
<title>svc:prometheus</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M774,-1108C774,-1108 715,-1108 715,-1108 709,-1108 703,-1102 703,-1096 703,-1096 703,-1076 703,-1076 703,-1070 709,-1064 715,-1064 715,-1064 774,-1064 774,-1064 780,-1064 786,-1070 786,-1076 786,-1076 786,-1096 786,-1096 786,-1102 780,-1108 774,-1108"/>
|
||||
<text text-anchor="middle" x="744.5" y="-1095.2" font-family="Helvetica,sans-Serif" font-size="11.00">prometheus</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1083.2" font-family="Helvetica,sans-Serif" font-size="11.00">:9090</text>
|
||||
<text text-anchor="middle" x="744.5" y="-1071.2" font-family="Helvetica,sans-Serif" font-size="11.00">[authelia]</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:prometheus -->
|
||||
<g id="edge26" class="edge">
|
||||
<title>svc:traefik->svc:prometheus</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M547.48,-789.35C581.39,-848.76 686,-1032 686,-1032 686,-1032 698.77,-1044 712.28,-1056.68"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="710.1,-1059.43 719.78,-1063.72 714.89,-1054.33 710.1,-1059.43"/>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp -->
|
||||
<g id="node29" class="node">
|
||||
<title>svc:searxng-webapp</title>
|
||||
<path fill="#dcfce7" stroke="black" d="M786,-369C786,-369 703,-369 703,-369 697,-369 691,-363 691,-357 691,-357 691,-345 691,-345 691,-339 697,-333 703,-333 703,-333 786,-333 786,-333 792,-333 798,-339 798,-345 798,-345 798,-357 798,-357 798,-363 792,-369 786,-369"/>
|
||||
<text text-anchor="middle" x="744.5" y="-348.2" font-family="Helvetica,sans-Serif" font-size="11.00">searxng-webapp</text>
|
||||
</g>
|
||||
<!-- svc:traefik->svc:searxng-webapp -->
|
||||
<g id="edge28" class="edge">
|
||||
<title>svc:traefik->svc:searxng-webapp</title>
|
||||
<path fill="none" stroke="#334155" stroke-width="1.4" d="M544.29,-752.9C574.2,-678.62 686,-401 686,-401 686,-401 700.43,-388.45 714.83,-375.93"/>
|
||||
<polygon fill="#334155" stroke="#334155" stroke-width="1.4" points="717.49,-378.25 722.74,-369.05 712.9,-372.97 717.49,-378.25"/>
|
||||
</g>
|
||||
<!-- net:traefik -->
|
||||
<g id="node36" class="node">
|
||||
<title>net:traefik</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-878" rx="31.04" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-875.2" font-family="Helvetica,sans-Serif" font-size="11.00">traefik</text>
|
||||
</g>
|
||||
<!-- svc:traefik->net:traefik -->
|
||||
<g id="edge55" class="edge">
|
||||
<title>svc:traefik->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M542.75,-752.82C570.13,-666.27 686,-300 686,-300 686,-300 803,-300 803,-300 803,-300 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- svc:authelia->net:traefik -->
|
||||
<g id="edge32" class="edge">
|
||||
<title>svc:authelia->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M775.21,-783.17C824.86,-803.51 924.25,-844.23 975.12,-865.06"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="974.26,-867.36 981.67,-867.75 976.12,-862.83 974.26,-867.36"/>
|
||||
</g>
|
||||
<!-- dns:auth.<domain> -->
|
||||
<g id="node4" class="node">
|
||||
<title>dns:auth.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="123.5,-1496 25.5,-1496 25.5,-1460 129.5,-1460 129.5,-1490 123.5,-1496"/>
|
||||
<polyline fill="none" stroke="black" points="123.5,-1496 123.5,-1490 "/>
|
||||
<polyline fill="none" stroke="black" points="129.5,-1490 123.5,-1490 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1475.2" font-family="Helvetica,sans-Serif" font-size="11.00">auth.<domain></text>
|
||||
</g>
|
||||
<!-- dns:auth.<domain>->dynu -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>dns:auth.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1459.95C128.03,-1445.63 155,-1428 155,-1428 155,-1428 286.65,-925.11 319.6,-799.28"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="323.07,-799.82 322.22,-789.26 316.3,-798.05 323.07,-799.82"/>
|
||||
</g>
|
||||
<!-- svc:gitea->net:traefik -->
|
||||
<g id="edge33" class="edge">
|
||||
<title>svc:gitea->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M768.56,-688.05C784.55,-703.36 803,-721 803,-721 803,-721 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:gitea.<domain> -->
|
||||
<g id="node6" class="node">
|
||||
<title>dns:gitea.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="125,-1395 24,-1395 24,-1359 131,-1359 131,-1389 125,-1395"/>
|
||||
<polyline fill="none" stroke="black" points="125,-1395 125,-1389 "/>
|
||||
<polyline fill="none" stroke="black" points="131,-1389 125,-1389 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1374.2" font-family="Helvetica,sans-Serif" font-size="11.00">gitea.<domain></text>
|
||||
</g>
|
||||
<!-- dns:gitea.<domain>->dynu -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>dns:gitea.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1358.95C128.03,-1344.63 155,-1327 155,-1327 155,-1327 283.58,-911.36 318.4,-798.82"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="321.76,-799.77 321.38,-789.18 315.08,-797.7 321.76,-799.77"/>
|
||||
</g>
|
||||
<!-- svc:gotify->net:traefik -->
|
||||
<g id="edge34" class="edge">
|
||||
<title>svc:gotify->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-579.05C782.73,-593.37 803,-611 803,-611 803,-611 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:gotify.<domain> -->
|
||||
<g id="node8" class="node">
|
||||
<title>dns:gotify.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="126,-1294 23,-1294 23,-1258 132,-1258 132,-1288 126,-1294"/>
|
||||
<polyline fill="none" stroke="black" points="126,-1294 126,-1288 "/>
|
||||
<polyline fill="none" stroke="black" points="132,-1288 126,-1288 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1273.2" font-family="Helvetica,sans-Serif" font-size="11.00">gotify.<domain></text>
|
||||
</g>
|
||||
<!-- dns:gotify.<domain>->dynu -->
|
||||
<g id="edge7" class="edge">
|
||||
<title>dns:gotify.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1257.95C128.03,-1243.63 155,-1226 155,-1226 155,-1226 279.21,-897.41 316.53,-798.71"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="319.89,-799.71 320.15,-789.12 313.34,-797.23 319.89,-799.71"/>
|
||||
</g>
|
||||
<!-- net:monitor -->
|
||||
<g id="node33" class="node">
|
||||
<title>net:monitor</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-979" rx="35.46" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-976.2" font-family="Helvetica,sans-Serif" font-size="11.00">monitor</text>
|
||||
</g>
|
||||
<!-- svc:grafana->net:monitor -->
|
||||
<g id="edge35" class="edge">
|
||||
<title>svc:grafana->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M774.2,-980.78C820.4,-980.43 911.47,-979.73 964.89,-979.32"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="965.19,-981.77 972.17,-979.26 965.15,-976.87 965.19,-981.77"/>
|
||||
</g>
|
||||
<!-- svc:grafana->net:traefik -->
|
||||
<g id="edge36" class="edge">
|
||||
<title>svc:grafana->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M774.2,-969.68C823.41,-950.28 923.53,-910.8 974.83,-890.57"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="975.82,-892.81 981.44,-887.96 974.03,-888.25 975.82,-892.81"/>
|
||||
</g>
|
||||
<!-- dns:grafana.<domain> -->
|
||||
<g id="node10" class="node">
|
||||
<title>dns:grafana.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="132,-1193 17,-1193 17,-1157 138,-1157 138,-1187 132,-1193"/>
|
||||
<polyline fill="none" stroke="black" points="132,-1193 132,-1187 "/>
|
||||
<polyline fill="none" stroke="black" points="138,-1187 132,-1187 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1172.2" font-family="Helvetica,sans-Serif" font-size="11.00">grafana.<domain></text>
|
||||
</g>
|
||||
<!-- dns:grafana.<domain>->dynu -->
|
||||
<g id="edge9" class="edge">
|
||||
<title>dns:grafana.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1156.95C128.03,-1142.63 155,-1125 155,-1125 155,-1125 273.61,-880.89 313.84,-798.09"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="317.01,-799.57 318.23,-789.04 310.72,-796.51 317.01,-799.57"/>
|
||||
</g>
|
||||
<!-- net:gramps -->
|
||||
<g id="node32" class="node">
|
||||
<title>net:gramps</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-1080" rx="34.76" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-1077.2" font-family="Helvetica,sans-Serif" font-size="11.00">gramps</text>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:gramps -->
|
||||
<g id="edge37" class="edge">
|
||||
<title>svc:grampsweb->net:gramps</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1491.95C782.73,-1477.63 803,-1460 803,-1460 803,-1460 949.23,-1187.21 993.89,-1103.88"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="996.27,-1104.64 997.41,-1097.32 991.95,-1102.33 996.27,-1104.64"/>
|
||||
</g>
|
||||
<!-- svc:grampsweb->net:traefik -->
|
||||
<g id="edge38" class="edge">
|
||||
<title>svc:grampsweb->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1491.95C782.73,-1477.63 803,-1460 803,-1460 803,-1460 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:familytree.<domain> -->
|
||||
<g id="node12" class="node">
|
||||
<title>dns:familytree.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="139,-1092 10,-1092 10,-1056 145,-1056 145,-1086 139,-1092"/>
|
||||
<polyline fill="none" stroke="black" points="139,-1092 139,-1086 "/>
|
||||
<polyline fill="none" stroke="black" points="145,-1086 139,-1086 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-1071.2" font-family="Helvetica,sans-Serif" font-size="11.00">familytree.<domain></text>
|
||||
</g>
|
||||
<!-- dns:familytree.<domain>->dynu -->
|
||||
<g id="edge11" class="edge">
|
||||
<title>dns:familytree.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-1055.95C128.03,-1041.63 155,-1024 155,-1024 155,-1024 264.64,-862.73 308.84,-797.71"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="311.9,-799.43 314.63,-789.2 306.11,-795.5 311.9,-799.43"/>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:monitor -->
|
||||
<g id="edge39" class="edge">
|
||||
<title>svc:influxdb->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M779.65,-889.47C829.7,-909.2 922.46,-945.78 972.53,-965.53"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="971.89,-967.91 979.3,-968.2 973.69,-963.35 971.89,-967.91"/>
|
||||
</g>
|
||||
<!-- svc:influxdb->net:traefik -->
|
||||
<g id="edge40" class="edge">
|
||||
<title>svc:influxdb->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M779.65,-876.26C828.54,-876.64 918.2,-877.32 968.99,-877.71"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="969.17,-880.16 976.19,-877.77 969.21,-875.26 969.17,-880.16"/>
|
||||
</g>
|
||||
<!-- dns:influxdb.<domain> -->
|
||||
<g id="node14" class="node">
|
||||
<title>dns:influxdb.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="132.5,-991 16.5,-991 16.5,-955 138.5,-955 138.5,-985 132.5,-991"/>
|
||||
<polyline fill="none" stroke="black" points="132.5,-991 132.5,-985 "/>
|
||||
<polyline fill="none" stroke="black" points="138.5,-985 132.5,-985 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-970.2" font-family="Helvetica,sans-Serif" font-size="11.00">influxdb.<domain></text>
|
||||
</g>
|
||||
<!-- dns:influxdb.<domain>->dynu -->
|
||||
<g id="edge13" class="edge">
|
||||
<title>dns:influxdb.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-954.95C128.03,-940.63 155,-923 155,-923 155,-923 250.14,-838.92 298.91,-795.83"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="301.42,-798.28 306.59,-789.03 296.78,-793.03 301.42,-798.28"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:monitor -->
|
||||
<g id="edge41" class="edge">
|
||||
<title>svc:monitor-kuma->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1390.95C782.73,-1376.63 803,-1359 803,-1359 803,-1359 910,-1030 910,-1030 910,-1030 947.73,-1010.1 976.05,-995.16"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="977.25,-997.3 982.29,-991.87 974.96,-992.97 977.25,-997.3"/>
|
||||
</g>
|
||||
<!-- svc:monitor-kuma->net:traefik -->
|
||||
<g id="edge42" class="edge">
|
||||
<title>svc:monitor-kuma->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-1390.95C782.73,-1376.63 803,-1359 803,-1359 803,-1359 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:monitor-kuma.<domain> -->
|
||||
<g id="node16" class="node">
|
||||
<title>dns:monitor-kuma.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="149,-890 0,-890 0,-854 155,-854 155,-884 149,-890"/>
|
||||
<polyline fill="none" stroke="black" points="149,-890 149,-884 "/>
|
||||
<polyline fill="none" stroke="black" points="155,-884 149,-884 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-869.2" font-family="Helvetica,sans-Serif" font-size="11.00">monitor-kuma.<domain></text>
|
||||
</g>
|
||||
<!-- dns:monitor-kuma.<domain>->dynu -->
|
||||
<g id="edge15" class="edge">
|
||||
<title>dns:monitor-kuma.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M122.93,-853.94C165.02,-836.83 228.32,-811.11 273.25,-792.85"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="274.59,-796.08 282.54,-789.07 271.96,-789.59 274.59,-796.08"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:monitor -->
|
||||
<g id="edge43" class="edge">
|
||||
<title>svc:mtls-bridge->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1281.72C785.06,-1266.85 803,-1250 803,-1250 803,-1250 910,-1030 910,-1030 910,-1030 947.73,-1010.1 976.05,-995.16"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="977.25,-997.3 982.29,-991.87 974.96,-992.97 977.25,-997.3"/>
|
||||
</g>
|
||||
<!-- svc:mtls-bridge->net:traefik -->
|
||||
<g id="edge44" class="edge">
|
||||
<title>svc:mtls-bridge->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1281.72C785.06,-1266.85 803,-1250 803,-1250 803,-1250 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:mtls-bridge.<domain> -->
|
||||
<g id="node18" class="node">
|
||||
<title>dns:mtls-bridge.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="142,-789 7,-789 7,-753 148,-753 148,-783 142,-789"/>
|
||||
<polyline fill="none" stroke="black" points="142,-789 142,-783 "/>
|
||||
<polyline fill="none" stroke="black" points="148,-783 142,-783 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-768.2" font-family="Helvetica,sans-Serif" font-size="11.00">mtls-bridge.<domain></text>
|
||||
</g>
|
||||
<!-- dns:mtls-bridge.<domain>->dynu -->
|
||||
<g id="edge17" class="edge">
|
||||
<title>dns:mtls-bridge.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M148.05,-771C182.94,-771 225.03,-771 259.61,-771"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="259.62,-774.5 269.62,-771 259.62,-767.5 259.62,-774.5"/>
|
||||
</g>
|
||||
<!-- net:nextcloud -->
|
||||
<g id="node34" class="node">
|
||||
<title>net:nextcloud</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-777" rx="42.89" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-774.2" font-family="Helvetica,sans-Serif" font-size="11.00">nextcloud</text>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:nextcloud -->
|
||||
<g id="edge45" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:nextcloud</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M761.21,-137.31C778.24,-157.15 803,-186 803,-186 803,-186 910,-727 910,-727 910,-727 945.48,-745.35 973.46,-759.81"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="972.67,-762.17 980.02,-763.2 974.92,-757.81 972.67,-762.17"/>
|
||||
</g>
|
||||
<!-- svc:nextcloud-webapp->net:traefik -->
|
||||
<g id="edge46" class="edge">
|
||||
<title>svc:nextcloud-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M761.21,-137.31C778.24,-157.15 803,-186 803,-186 803,-186 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:nextcloud.<domain> -->
|
||||
<g id="node20" class="node">
|
||||
<title>dns:nextcloud.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="138,-688 11,-688 11,-652 144,-652 144,-682 138,-688"/>
|
||||
<polyline fill="none" stroke="black" points="138,-688 138,-682 "/>
|
||||
<polyline fill="none" stroke="black" points="144,-682 138,-682 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-667.2" font-family="Helvetica,sans-Serif" font-size="11.00">nextcloud.<domain></text>
|
||||
</g>
|
||||
<!-- dns:nextcloud.<domain>->dynu -->
|
||||
<g id="edge19" class="edge">
|
||||
<title>dns:nextcloud.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M122.93,-688.06C165.02,-705.17 228.32,-730.89 273.25,-749.15"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="271.96,-752.41 282.54,-752.93 274.59,-745.92 271.96,-752.41"/>
|
||||
</g>
|
||||
<!-- svc:node-red->net:monitor -->
|
||||
<g id="edge47" class="edge">
|
||||
<title>svc:node-red->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1172.72C785.06,-1157.85 803,-1141 803,-1141 803,-1141 910,-1030 910,-1030 910,-1030 947.73,-1010.1 976.05,-995.16"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="977.25,-997.3 982.29,-991.87 974.96,-992.97 977.25,-997.3"/>
|
||||
</g>
|
||||
<!-- svc:node-red->net:traefik -->
|
||||
<g id="edge48" class="edge">
|
||||
<title>svc:node-red->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1172.72C785.06,-1157.85 803,-1141 803,-1141 803,-1141 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:node-red.<domain> -->
|
||||
<g id="node22" class="node">
|
||||
<title>dns:node-red.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="135.5,-587 13.5,-587 13.5,-551 141.5,-551 141.5,-581 135.5,-587"/>
|
||||
<polyline fill="none" stroke="black" points="135.5,-587 135.5,-581 "/>
|
||||
<polyline fill="none" stroke="black" points="141.5,-581 135.5,-581 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-566.2" font-family="Helvetica,sans-Serif" font-size="11.00">node-red.<domain></text>
|
||||
</g>
|
||||
<!-- dns:node-red.<domain>->dynu -->
|
||||
<g id="edge21" class="edge">
|
||||
<title>dns:node-red.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-587.05C128.03,-601.37 155,-619 155,-619 155,-619 250.14,-703.08 298.91,-746.17"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="296.78,-748.97 306.59,-752.97 301.42,-743.72 296.78,-748.97"/>
|
||||
</g>
|
||||
<!-- net:passbolt -->
|
||||
<g id="node35" class="node">
|
||||
<title>net:passbolt</title>
|
||||
<ellipse fill="#f8fafc" stroke="black" cx="1007.69" cy="-676" rx="37.77" ry="18"/>
|
||||
<text text-anchor="middle" x="1007.69" y="-673.2" font-family="Helvetica,sans-Serif" font-size="11.00">passbolt</text>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:passbolt -->
|
||||
<g id="edge49" class="edge">
|
||||
<title>svc:passbolt-webapp->net:passbolt</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-36.05C782.73,-50.37 803,-68 803,-68 803,-68 960.4,-537.83 998.48,-651.47"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="996.16,-652.27 1000.71,-658.13 1000.81,-650.72 996.16,-652.27"/>
|
||||
</g>
|
||||
<!-- svc:passbolt-webapp->net:traefik -->
|
||||
<g id="edge50" class="edge">
|
||||
<title>svc:passbolt-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-36.05C782.73,-50.37 803,-68 803,-68 803,-68 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:passbolt.<domain> -->
|
||||
<g id="node24" class="node">
|
||||
<title>dns:passbolt.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="134,-486 15,-486 15,-450 140,-450 140,-480 134,-486"/>
|
||||
<polyline fill="none" stroke="black" points="134,-486 134,-480 "/>
|
||||
<polyline fill="none" stroke="black" points="140,-480 134,-480 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-465.2" font-family="Helvetica,sans-Serif" font-size="11.00">passbolt.<domain></text>
|
||||
</g>
|
||||
<!-- dns:passbolt.<domain>->dynu -->
|
||||
<g id="edge23" class="edge">
|
||||
<title>dns:passbolt.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-486.05C128.03,-500.37 155,-518 155,-518 155,-518 264.64,-679.27 308.84,-744.29"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="306.11,-746.5 314.63,-752.8 311.9,-742.57 306.11,-746.5"/>
|
||||
</g>
|
||||
<!-- svc:portainer->net:traefik -->
|
||||
<g id="edge51" class="edge">
|
||||
<title>svc:portainer->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-478.28C785.06,-493.15 803,-510 803,-510 803,-510 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:portainer.<domain> -->
|
||||
<g id="node26" class="node">
|
||||
<title>dns:portainer.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="135.5,-385 13.5,-385 13.5,-349 141.5,-349 141.5,-379 135.5,-385"/>
|
||||
<polyline fill="none" stroke="black" points="135.5,-385 135.5,-379 "/>
|
||||
<polyline fill="none" stroke="black" points="141.5,-379 135.5,-379 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-364.2" font-family="Helvetica,sans-Serif" font-size="11.00">portainer.<domain></text>
|
||||
</g>
|
||||
<!-- dns:portainer.<domain>->dynu -->
|
||||
<g id="edge25" class="edge">
|
||||
<title>dns:portainer.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-385.05C128.03,-399.37 155,-417 155,-417 155,-417 273.61,-661.11 313.84,-743.91"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="310.72,-745.49 318.23,-752.96 317.01,-742.43 310.72,-745.49"/>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:monitor -->
|
||||
<g id="edge52" class="edge">
|
||||
<title>svc:prometheus->net:monitor</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M786.03,-1069.4C837.48,-1048.32 925.31,-1012.34 973.18,-992.73"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="974.11,-995 979.66,-990.07 972.26,-990.46 974.11,-995"/>
|
||||
</g>
|
||||
<!-- svc:prometheus->net:traefik -->
|
||||
<g id="edge53" class="edge">
|
||||
<title>svc:prometheus->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M769.22,-1063.72C785.06,-1048.85 803,-1032 803,-1032 803,-1032 910,-929 910,-929 910,-929 949.05,-908.4 977.52,-893.39"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="978.73,-895.52 983.78,-890.09 976.45,-891.18 978.73,-895.52"/>
|
||||
</g>
|
||||
<!-- dns:prometheus.<domain> -->
|
||||
<g id="node28" class="node">
|
||||
<title>dns:prometheus.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="144,-284 5,-284 5,-248 150,-248 150,-278 144,-284"/>
|
||||
<polyline fill="none" stroke="black" points="144,-284 144,-278 "/>
|
||||
<polyline fill="none" stroke="black" points="150,-278 144,-278 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-263.2" font-family="Helvetica,sans-Serif" font-size="11.00">prometheus.<domain></text>
|
||||
</g>
|
||||
<!-- dns:prometheus.<domain>->dynu -->
|
||||
<g id="edge27" class="edge">
|
||||
<title>dns:prometheus.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-284.05C128.03,-298.37 155,-316 155,-316 155,-316 279.21,-644.59 316.53,-743.29"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="313.34,-744.77 320.15,-752.88 319.89,-742.29 313.34,-744.77"/>
|
||||
</g>
|
||||
<!-- svc:searxng-webapp->net:traefik -->
|
||||
<g id="edge54" class="edge">
|
||||
<title>svc:searxng-webapp->net:traefik</title>
|
||||
<path fill="none" stroke="#94a3b8" stroke-dasharray="5,2" d="M766.26,-369.05C782.73,-383.37 803,-401 803,-401 803,-401 910,-828 910,-828 910,-828 949.05,-848.19 977.52,-862.92"/>
|
||||
<polygon fill="#94a3b8" stroke="#94a3b8" points="976.44,-865.11 983.78,-866.15 978.69,-860.76 976.44,-865.11"/>
|
||||
</g>
|
||||
<!-- dns:searxng.<domain> -->
|
||||
<g id="node30" class="node">
|
||||
<title>dns:searxng.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="133,-183 16,-183 16,-147 139,-147 139,-177 133,-183"/>
|
||||
<polyline fill="none" stroke="black" points="133,-183 133,-177 "/>
|
||||
<polyline fill="none" stroke="black" points="139,-177 133,-177 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-162.2" font-family="Helvetica,sans-Serif" font-size="11.00">searxng.<domain></text>
|
||||
</g>
|
||||
<!-- dns:searxng.<domain>->dynu -->
|
||||
<g id="edge29" class="edge">
|
||||
<title>dns:searxng.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-183.05C128.03,-197.37 155,-215 155,-215 155,-215 283.58,-630.64 318.4,-743.18"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="315.08,-744.3 321.38,-752.82 321.76,-742.23 315.08,-744.3"/>
|
||||
</g>
|
||||
<!-- dns:traefik.<domain> -->
|
||||
<g id="node31" class="node">
|
||||
<title>dns:traefik.<domain></title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="128.5,-82 20.5,-82 20.5,-46 134.5,-46 134.5,-76 128.5,-82"/>
|
||||
<polyline fill="none" stroke="black" points="128.5,-82 128.5,-76 "/>
|
||||
<polyline fill="none" stroke="black" points="134.5,-76 128.5,-76 "/>
|
||||
<text text-anchor="middle" x="77.5" y="-61.2" font-family="Helvetica,sans-Serif" font-size="11.00">traefik.<domain></text>
|
||||
</g>
|
||||
<!-- dns:traefik.<domain>->dynu -->
|
||||
<g id="edge31" class="edge">
|
||||
<title>dns:traefik.<domain>->dynu</title>
|
||||
<path fill="none" stroke="#334155" d="M106.12,-82.05C128.03,-96.37 155,-114 155,-114 155,-114 286.65,-616.89 319.6,-742.72"/>
|
||||
<polygon fill="#334155" stroke="#334155" points="316.3,-743.95 322.22,-752.74 323.07,-742.18 316.3,-743.95"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,21 @@
|
||||
# Public Infrastructure Summary
|
||||
|
||||
This documentation is generated from the infrastructure repository. Sensitive values are redacted.
|
||||
|
||||
> Generated docs are sanitised/redacted before publishing to GitHub Pages.
|
||||
|
||||
## Infrastructure diagrams
|
||||
|
||||
### Physical / virtual topology
|
||||
|
||||

|
||||
|
||||
### Docker, Traefik and Dynu routing
|
||||
|
||||

|
||||
|
||||
## Documents
|
||||
|
||||
- [Diagrams](diagrams.md)
|
||||
- [Compose Inventory](compose-inventory.md)
|
||||
- [Traefik Routes](traefik-routes.md)
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.43.0 (0)
|
||||
-->
|
||||
<!-- Title: PhysicalTopology Pages: 1 -->
|
||||
<svg width="514pt" height="61pt"
|
||||
viewBox="0.00 0.00 514.00 61.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 57)">
|
||||
<title>PhysicalTopology</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-57 510,-57 510,4 -4,4"/>
|
||||
<!-- placeholder:inventory -->
|
||||
<g id="node1" class="node">
|
||||
<title>placeholder:inventory</title>
|
||||
<polygon fill="#fef3c7" stroke="black" points="500,-53 0,-53 0,0 506,0 506,-47 500,-53"/>
|
||||
<polyline fill="none" stroke="black" points="500,-53 500,-47 "/>
|
||||
<polyline fill="none" stroke="black" points="506,-47 500,-47 "/>
|
||||
<text text-anchor="middle" x="253" y="-37.8" font-family="Times,serif" font-size="14.00">Host inventory JSON not found.</text>
|
||||
<text text-anchor="middle" x="253" y="-22.8" font-family="Times,serif" font-size="14.00">Generate terraform inventory and rerun scripts/docs/generate-all.sh</text>
|
||||
<text text-anchor="middle" x="253" y="-7.8" font-family="Times,serif" font-size="14.00">(--host-inventory <path>).</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,23 @@
|
||||
.md-content img, article img {
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.diagram-wrap {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin: 1rem 0 2rem;
|
||||
}
|
||||
|
||||
.diagram-wrap img {
|
||||
max-width: none;
|
||||
width: 1400px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.diagram-wrap img {
|
||||
width: 1200px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Traefik Routes
|
||||
|
||||
| Service | Router | Rule | Entrypoints | TLS | Middlewares | Target Port |
|
||||
|---|---|---|---|---|---|---|
|
||||
| authelia | authelia | Host(`auth.<domain>`) | websecure | true | | |
|
||||
| error-pages | error-pages-router | HostRegexp(`{host:.+}`) | web | | error-pages-middleware | |
|
||||
| gitea | gitea | Host(`gitea.<domain>`) | websecure | true | | 3000 |
|
||||
| gotify | gotify | Host(`gotify.<domain>`) | websecure | | | 80 |
|
||||
| grafana | grafana | Host(`grafana.<domain>`) | websecure | | | 3000 |
|
||||
| grampsweb | gramps | Host(`familytree.<domain>`) | websecure | | | 5000 |
|
||||
| influxdb | influxdb | Host(`influxdb.<domain>`) | websecure | | authelia | 8086 |
|
||||
| monitor-kuma | monitor | Host(`monitor-kuma.<domain>`) | websecure | true | | 3001 |
|
||||
| mtls-bridge | mtls-bridge | Host(`mtls-bridge.<domain>`) | websecure | | mtls-bridge-auth,mtls-bridge-cors | 8080 |
|
||||
| mtls-bridge | mtls-bridge-preflight | Host(`mtls-bridge.<domain>`) && Method(`OPTIONS`) | websecure | | mtls-bridge-cors | |
|
||||
| nextcloud-webapp | nextcloud | Host(`nextcloud.<domain>`) | websecure | | nextcloud-dav, nextcloud-webfinger | |
|
||||
| node-red | node-red | Host(`node-red.<domain>`) | websecure | | authelia | 1880 |
|
||||
| passbolt-webapp | passbolt | Host(`passbolt.<domain>`) | websecure | | | |
|
||||
| portainer | portainer | Host(`portainer.<domain>`) | websecure | true | | 9000 |
|
||||
| prometheus | prometheus | Host(`prometheus.<domain>`) | websecure | | authelia | 9090 |
|
||||
| searxng-webapp | searxng | Host(`searxng.<domain>`) | websecure | | | 8080 |
|
||||
| traefik | traefik | Host(`traefik.<domain>`) | websecure | | authelia | |
|
||||
@@ -33,7 +33,7 @@ This page explains where to find authoritative files quickly.
|
||||
|
||||
## Fast path for future Codex runs
|
||||
|
||||
1. Read [README.md](../README.md).
|
||||
1. Read [README.md](https://github.com/beatz174-bit/docker/blob/main/README.md).
|
||||
2. Read [docs/source-of-truth.md](source-of-truth.md).
|
||||
3. Read [docs/docker-environment.md](docker-environment.md).
|
||||
4. Read [docs/terraform-workflows.md](terraform-workflows.md).
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
This page explains how secret material is organized in this repository and where to find both human-readable and machine-readable references.
|
||||
|
||||
For machine-readable inventory metadata, use [`../secrets/inventory.json`](../secrets/inventory.json).
|
||||
For machine-readable inventory metadata, use [`secrets/inventory.json`](https://github.com/beatz174-bit/docker/blob/main/secrets/inventory.json).
|
||||
|
||||
## Scope and authority
|
||||
|
||||
- Canonical example template: [`../secrets/.env.secrets.example`](../secrets/.env.secrets.example)
|
||||
- Canonical example template: [`secrets/.env.secrets.example`](https://github.com/beatz174-bit/docker/blob/main/secrets/.env.secrets.example)
|
||||
- Runtime-loaded secret env file (local, non-committed): `../secrets/stack-secrets.env`
|
||||
- Dynu DNS inventory env file (local, non-committed): `../secrets/dynu.env`
|
||||
- Docker secret files (local, non-committed): `../secrets/*.txt`
|
||||
@@ -30,7 +30,7 @@ Treat the example template as the canonical shape for expected environment varia
|
||||
|
||||
## Machine-readable inventory
|
||||
|
||||
- Primary automation source: [`../secrets/inventory.json`](../secrets/inventory.json)
|
||||
- Primary automation source: [`secrets/inventory.json`](https://github.com/beatz174-bit/docker/blob/main/secrets/inventory.json)
|
||||
- Human guidance source: this page
|
||||
|
||||
Automation should parse `secrets/inventory.json` directly rather than scraping Markdown tables.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Public Showcase
|
||||
|
||||
This environment showcases practical infrastructure operations with a documentation-first approach.
|
||||
|
||||
Highlights:
|
||||
|
||||
- Container orchestration with Docker Compose.
|
||||
- Reverse proxy routing and TLS-focused service exposure patterns.
|
||||
- Monitoring and alerting configuration via version-controlled rules.
|
||||
- Documentation automation through CI.
|
||||
- Generated architecture diagrams to aid operational understanding.
|
||||
- Sanitized public documentation for safe sharing.
|
||||
|
||||
This page intentionally avoids private hostnames, internal-only URLs, credentials, and secret values.
|
||||
@@ -30,6 +30,10 @@ Use Terraform when documenting/reconciling existing:
|
||||
|
||||
Do **not** treat Terraform as a full replacement for Compose operations in this repo.
|
||||
|
||||
- Dynu public DNS records remain authoritative at Dynu.
|
||||
- Terraform Dynu configuration mirrors/reconciles Dynu DNS state for documentation and controlled drift management.
|
||||
- Imported Dynu Terraform state reflects actual provider-side DNS state at import time.
|
||||
|
||||
|
||||
### Ansible bootstrap decisions
|
||||
|
||||
|
||||
@@ -43,6 +43,21 @@ Use for existing Proxmox VMs and metadata reconciliation.
|
||||
5. Keep lifecycle ignore rules narrow and explicit.
|
||||
6. Iterate per VM until plan stabilizes.
|
||||
|
||||
|
||||
## Dynu DNS workflow
|
||||
|
||||
Directory: `infrastructure/terraform/dynu/`
|
||||
|
||||
Use for existing Dynu domains and DNS records.
|
||||
|
||||
1. Add or confirm the documentation catalog entry for one hostname.
|
||||
2. Confirm the provider resource type and import ID format.
|
||||
3. Import one existing domain or DNS record at a time.
|
||||
4. Inspect state with `terraform state show`.
|
||||
5. Reconcile only stable, meaningful attributes into hand-maintained `.tf`.
|
||||
6. Keep record IDs, dynamic DNS targets, and provider-computed values out unless intentionally required.
|
||||
7. Re-run plan until intended scope is clean.
|
||||
|
||||
## Physical host metadata workflow
|
||||
|
||||
Physical host metadata currently lives in Proxmox Terraform locals/outputs and is used as documentation inventory context.
|
||||
@@ -75,4 +90,4 @@ Treat generated files as:
|
||||
|
||||
- [docs/source-of-truth.md](source-of-truth.md)
|
||||
- [docs/infrastructure-inventory.md](infrastructure-inventory.md)
|
||||
- [infrastructure/terraform/README.md](../infrastructure/terraform/README.md)
|
||||
- [infrastructure/terraform/README.md](https://github.com/beatz174-bit/docker/blob/main/infrastructure/terraform/README.md)
|
||||
|
||||
@@ -10,17 +10,21 @@ It does **not** replace Docker Compose as runtime deployment authority.
|
||||
- Physical host metadata represented in Terraform locals/outputs.
|
||||
- Select Docker container mirror resources for documentation-oriented tracking.
|
||||
- Outputs that can support documentation and later downstream tooling.
|
||||
- Dynu DNS domain/record import and documentation inventory.
|
||||
|
||||
## What Terraform is not used for (today)
|
||||
|
||||
- Replacing `services-up.sh` / Compose for day-to-day app runtime orchestration.
|
||||
- Broad, immediate greenfield provisioning of the whole stack.
|
||||
- Casual `apply` operations across all infrastructure.
|
||||
- Replacing Dynu as DNS authority.
|
||||
- Blindly recreating production DNS records without import/reconciliation.
|
||||
|
||||
## Directory map
|
||||
|
||||
- `proxmox/` — imported/reconciled VM resources and host metadata outputs.
|
||||
- `docker/` — selective Docker container import/mirror resources.
|
||||
- `dynu/` — Dynu DNS brownfield import/reconciliation and DNS documentation outputs.
|
||||
- `bootstrap/` — backend/provider bootstrap scaffolding.
|
||||
- `modules/` — placeholder module directories for future stable abstractions.
|
||||
- `scripts/reconcile_from_plan.sh` — helper to convert generated plan config into reviewable draft files.
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/beatz174-bit/dynu" {
|
||||
version = "0.3.0"
|
||||
hashes = [
|
||||
"h1:yftAEp/lcPmbVRV8YenFaMJElUkt3j79TSt3OcQnwk4=",
|
||||
"zh:1f7344737dff5b12155e8308b1cb55b0cc6f83a4a5776eb1cc2273bc84bb8fa2",
|
||||
"zh:212662c4f5b979401f282f7f9856480bad86d9e488110bebc589a4eeb892ff02",
|
||||
"zh:2ed5294fc7db7639c41f99a9d7bcfff6585f1f372eb23cd8229adfe219cba63a",
|
||||
"zh:30fc9df00120f309ae969ae107e4dc4a0b04517f2c07a78934a2c479995f77b8",
|
||||
"zh:3126369f6dc86e8083ec12a2643ea87a13543ed12631b40375b7b9563da41474",
|
||||
"zh:3c5775a6763608253e2698b85dcee42eafd6ca8e08a8851866123de403a331b0",
|
||||
"zh:50bedffbee48505604d05181172018143e54c68249761a749fb7c115eec4ce04",
|
||||
"zh:528549a2763dd2fbf3ffe047fa19c0524eb08addd777c9c0350800394ff16235",
|
||||
"zh:99049e25d7d3fb26e2a94d6e609c8989efcee1af1568a77110a70bce2c01f1ef",
|
||||
"zh:9a6490b67aca08b135e5ba7092fc315e67177be1f280f6c0d06b9c64a0892d3a",
|
||||
"zh:bfaae08fb5a0b10184a7b6e8382038d0a0b9936ea13efa1462c35cee902b0c14",
|
||||
"zh:c0dbe59b9bfcbd42f3da1732615669a579275acc654c75127612d86f319b38b3",
|
||||
"zh:f809ab383cca0a5f83072981c64208cbd7fa67e986a86ee02dd2c82333221e32",
|
||||
"zh:f815fa2f8681477f159eb9a32b78f8988065e5040b7feea42604bac469c2c4eb",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
# Dynu Terraform Layer (Brownfield DNS Reconciliation)
|
||||
|
||||
This Terraform root is for **Dynu DNS brownfield reconciliation**. The intended pattern is:
|
||||
|
||||
1. Import the existing root domain object.
|
||||
2. Read inventory through `data.dynu_dns_records.root`.
|
||||
3. Generate reviewable `dynu_dns_record` resources and import commands.
|
||||
4. Import every existing DNS record into matching Terraform resources.
|
||||
5. Use `terraform plan` as the reconciliation check before any apply.
|
||||
|
||||
## Provider behavior to keep in mind
|
||||
|
||||
- Source: `beatz174-bit/dynu`
|
||||
- `dynu_domain` import requires a **numeric Dynu domain ID**.
|
||||
- Importing `dynu_domain` imports only the root domain object.
|
||||
- It **does not** import DNS records/subdomains.
|
||||
- `dynu_dns_record` imports require `<domain_id>/<record_id>`.
|
||||
|
||||
## Variables
|
||||
|
||||
- `dynu_root_domain` (default: `lan.ddnsgeek.com`)
|
||||
- `dynu_api_key` (sensitive)
|
||||
- `dynu_username` / `dynu_password` (optional)
|
||||
|
||||
## Safe validation commands
|
||||
|
||||
```bash
|
||||
cd infrastructure/terraform/dynu
|
||||
terraform fmt -check -recursive
|
||||
terraform init -backend=false -input=false
|
||||
terraform validate
|
||||
python3 -m py_compile scripts/generate-brownfield-records.py
|
||||
```
|
||||
|
||||
## Brownfield workflow
|
||||
|
||||
```bash
|
||||
cd infrastructure/terraform/dynu
|
||||
|
||||
terraform init
|
||||
terraform import dynu_domain.lan_ddnsgeek_com '<numeric-dynu-domain-id>'
|
||||
|
||||
terraform apply -refresh-only
|
||||
terraform output -json dynu_dns_records > /tmp/dynu-records.json
|
||||
|
||||
python3 scripts/generate-brownfield-records.py --dry-run
|
||||
python3 scripts/generate-brownfield-records.py --overwrite
|
||||
|
||||
# Review generated/dynu_dns_records.generated.tf
|
||||
# Review generated/import-dynu-dns-records.sh
|
||||
|
||||
bash generated/import-dynu-dns-records.sh
|
||||
|
||||
terraform plan
|
||||
```
|
||||
|
||||
## What each component means
|
||||
|
||||
- `data.dynu_dns_records.root`: read-only live inventory from Dynu.
|
||||
- `generated/dynu_dns_records.generated.tf`: generated management-intent resources; includes `prevent_destroy = true` on each record.
|
||||
- `generated/import-dynu-dns-records.sh`: imports each discovered record to its generated `dynu_dns_record` address using `<domain_id>/<record_id>`.
|
||||
- `terraform plan` after imports: reconciliation checkpoint. Any create/update/delete must be reviewed manually before apply.
|
||||
|
||||
## Generated artifacts
|
||||
|
||||
The helper script writes these files under `generated/`:
|
||||
|
||||
- `generated/dynu_dns_records_inventory.json`
|
||||
- `generated/dynu_dns_records.generated.tf`
|
||||
- `generated/import-dynu-dns-records.sh`
|
||||
|
||||
These are generated outputs meant for operator review before use in production.
|
||||
|
||||
|
||||
### Generator output selection (interactive + automation)
|
||||
|
||||
The brownfield generator defaults to Terraform output `dynu_dns_records`:
|
||||
|
||||
```bash
|
||||
python3 scripts/generate-brownfield-records.py --dry-run
|
||||
```
|
||||
|
||||
If the default output is missing/unusable and stdin is interactive, the script shows a picker of available Terraform outputs and indicates which ones are usable for DNS imports.
|
||||
|
||||
```bash
|
||||
# Interactive mode: choose from available Terraform outputs
|
||||
python3 scripts/generate-brownfield-records.py --dry-run
|
||||
|
||||
# Non-interactive mode: specify output explicitly
|
||||
python3 scripts/generate-brownfield-records.py \
|
||||
--records-output dynu_dns_inventory \
|
||||
--dry-run
|
||||
|
||||
# Disable menu and fail fast
|
||||
python3 scripts/generate-brownfield-records.py \
|
||||
--no-interactive \
|
||||
--dry-run
|
||||
|
||||
# Use saved terraform output JSON and choose interactively
|
||||
terraform output -json > generated/terraform-output.json
|
||||
python3 scripts/generate-brownfield-records.py \
|
||||
--from-file generated/terraform-output.json \
|
||||
--dry-run
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The menu shows Terraform outputs currently stored in state.
|
||||
- If newly added outputs do not appear, run:
|
||||
|
||||
```bash
|
||||
terraform apply -refresh-only
|
||||
```
|
||||
|
||||
- The selected output must contain real Dynu provider record fields:
|
||||
- `id`
|
||||
- `domain_id`
|
||||
- `hostname`
|
||||
- `record_type`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Plan shows a large wall of `+` values under outputs
|
||||
|
||||
Cause:
|
||||
|
||||
Terraform is planning to save **new output values** to state (for example, live records from `data.dynu_dns_records.root`). This is not creating DNS records by itself.
|
||||
|
||||
How to verify:
|
||||
|
||||
- Output-only changes appear under `Changes to Outputs`.
|
||||
- Real DNS changes appear as `dynu_dns_record` resource create/update/delete actions.
|
||||
|
||||
Use:
|
||||
|
||||
```bash
|
||||
terraform apply -refresh-only
|
||||
```
|
||||
|
||||
to persist refreshed data source and output values only.
|
||||
|
||||
### Error: `There is no function named "regexreplace"`
|
||||
|
||||
Cause:
|
||||
|
||||
`regexreplace` is not a Terraform function. Resource-name slugification should not be implemented in Terraform HCL for this workflow.
|
||||
|
||||
Fix:
|
||||
|
||||
- Keep `inventory.tf` focused on reading live records via `data.dynu_dns_records.root`.
|
||||
- Keep Terraform outputs simple (for example, `<domain_id>/<record_id>` mappings).
|
||||
- Let `scripts/generate-brownfield-records.py` generate Terraform-safe resource names with Python `tf_name(record)`.
|
||||
|
||||
### Error: `'"'"'dynu_dns_records'"'"'`
|
||||
|
||||
Cause:
|
||||
|
||||
The helper script reads `terraform output -json` and expects an output named `dynu_dns_records`.
|
||||
|
||||
Fix:
|
||||
|
||||
```bash
|
||||
cd infrastructure/terraform/dynu
|
||||
terraform init
|
||||
terraform apply -refresh-only
|
||||
terraform output -json | jq 'keys'
|
||||
```
|
||||
|
||||
Confirm `dynu_dns_records` appears in the key list.
|
||||
|
||||
If it does not, check that the Terraform config contains:
|
||||
|
||||
```hcl
|
||||
data "dynu_dns_records" "root" {
|
||||
hostname = var.dynu_root_domain
|
||||
}
|
||||
|
||||
output "dynu_dns_records" {
|
||||
value = data.dynu_dns_records.root.records
|
||||
}
|
||||
```
|
||||
|
||||
Then rerun:
|
||||
|
||||
```bash
|
||||
python3 scripts/generate-brownfield-records.py --dry-run
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
data "dynu_domain" "lan" {
|
||||
hostname = "lan.ddnsgeek.com"
|
||||
}
|
||||
|
||||
output "dynu_domain_id" {
|
||||
value = data.dynu_domain.lan.domain.id
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
locals {
|
||||
dynu_domain = var.dynu_root_domain
|
||||
}
|
||||
|
||||
# Import-first resource skeleton for the production Dynu zone.
|
||||
# `name` is required by provider schema and can be reconciled after import.
|
||||
resource "dynu_domain" "lan_ddnsgeek_com" {
|
||||
name = local.dynu_domain
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,518 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
# GENERATED FILE - REVIEW BEFORE USE
|
||||
#
|
||||
# Generated from Dynu brownfield DNS inventory.
|
||||
# Do not blindly apply this file to production DNS.
|
||||
# Import records into Terraform state before allowing Terraform to manage them.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
resource "dynu_dns_record" "auth_lan_ddnsgeek_com_a_18483099" {
|
||||
hostname = "auth.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "auth"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "auth_lan_ddnsgeek_com_a_19646048" {
|
||||
hostname = "auth.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "auth"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "edge_lan_ddnsgeek_com_a_10453241" {
|
||||
hostname = "edge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "edge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "edge_lan_ddnsgeek_com_a_19646062" {
|
||||
hostname = "edge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "edge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "familytree_lan_ddnsgeek_com_a_17017685" {
|
||||
hostname = "familytree.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "familytree"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "familytree_lan_ddnsgeek_com_a_19646056" {
|
||||
hostname = "familytree.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "familytree"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gitea_lan_ddnsgeek_com_a_14682463" {
|
||||
hostname = "gitea.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "gitea"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gitea_lan_ddnsgeek_com_a_19646063" {
|
||||
hostname = "gitea.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "gitea"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gotify_lan_ddnsgeek_com_a_17439061" {
|
||||
hostname = "gotify.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "gotify"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gotify_lan_ddnsgeek_com_a_19646047" {
|
||||
hostname = "gotify.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "gotify"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "grafana_lan_ddnsgeek_com_a_18113762" {
|
||||
hostname = "grafana.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "grafana"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "grafana_lan_ddnsgeek_com_a_19646050" {
|
||||
hostname = "grafana.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "grafana"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "influxdb_lan_ddnsgeek_com_a_18562198" {
|
||||
hostname = "influxdb.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "influxdb"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "influxdb_lan_ddnsgeek_com_a_19646059" {
|
||||
hostname = "influxdb.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "influxdb"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "kuma_lan_ddnsgeek_com_a_17454978" {
|
||||
hostname = "kuma.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 90
|
||||
enabled = true
|
||||
content = "120.155.99.146"
|
||||
node_name = "kuma"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "lan_ddnsgeek_com_soa_8299670" {
|
||||
hostname = "lan.ddnsgeek.com"
|
||||
record_type = "SOA"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "ns1.dynu.com. administrator.dynu.com. 0 3600 900 604800 300"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "monitor_kuma_lan_ddnsgeek_com_a_17462342" {
|
||||
hostname = "monitor-kuma.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "monitor-kuma"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "monitor_kuma_lan_ddnsgeek_com_a_19646051" {
|
||||
hostname = "monitor-kuma.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "monitor-kuma"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "mtls_bridge_lan_ddnsgeek_com_a_19232643" {
|
||||
hostname = "mtls-bridge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "mtls-bridge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "mtls_bridge_lan_ddnsgeek_com_a_19646058" {
|
||||
hostname = "mtls-bridge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "mtls-bridge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "nextcloud_lan_ddnsgeek_com_a_10453260" {
|
||||
hostname = "nextcloud.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "nextcloud"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "nextcloud_lan_ddnsgeek_com_a_19646057" {
|
||||
hostname = "nextcloud.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "nextcloud"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "node_red_lan_ddnsgeek_com_a_19041230" {
|
||||
hostname = "node-red.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "node-red"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "node_red_lan_ddnsgeek_com_a_19646053" {
|
||||
hostname = "node-red.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "node-red"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "passbolt_lan_ddnsgeek_com_a_10453262" {
|
||||
hostname = "passbolt.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "passbolt"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "passbolt_lan_ddnsgeek_com_a_19646049" {
|
||||
hostname = "passbolt.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "passbolt"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "portainer_lan_ddnsgeek_com_a_17458810" {
|
||||
hostname = "portainer.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "portainer"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "portainer_lan_ddnsgeek_com_a_19646046" {
|
||||
hostname = "portainer.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "portainer"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "prometheus_lan_ddnsgeek_com_a_18483311" {
|
||||
hostname = "prometheus.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "prometheus"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "prometheus_lan_ddnsgeek_com_a_19646061" {
|
||||
hostname = "prometheus.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "prometheus"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "searxng_lan_ddnsgeek_com_a_10453263" {
|
||||
hostname = "searxng.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "searxng"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "searxng_lan_ddnsgeek_com_a_19646055" {
|
||||
hostname = "searxng.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "searxng"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "shifts_lan_ddnsgeek_com_a_15901565" {
|
||||
hostname = "shifts.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "shifts"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "shifts_lan_ddnsgeek_com_a_19646052" {
|
||||
hostname = "shifts.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "shifts"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "stockfill_lan_ddnsgeek_com_a_17081867" {
|
||||
hostname = "stockfill.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "stockfill"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "stockfill_lan_ddnsgeek_com_a_19646060" {
|
||||
hostname = "stockfill.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "stockfill"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "traefik_lan_ddnsgeek_com_a_10453240" {
|
||||
hostname = "traefik.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "traefik"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "traefik_lan_ddnsgeek_com_a_19646054" {
|
||||
hostname = "traefik.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "traefik"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
# GENERATED FILE - REVIEW BEFORE USE
|
||||
#
|
||||
# Generated from Dynu brownfield DNS inventory.
|
||||
# Do not blindly apply this file to production DNS.
|
||||
# Import records into Terraform state before allowing Terraform to manage them.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
resource "dynu_dns_record" "auth_lan_ddnsgeek_com_a_18483099" {
|
||||
hostname = "auth.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "auth"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "auth_lan_ddnsgeek_com_a_19646048" {
|
||||
hostname = "auth.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "auth"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "edge_lan_ddnsgeek_com_a_10453241" {
|
||||
hostname = "edge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "edge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "edge_lan_ddnsgeek_com_a_19646062" {
|
||||
hostname = "edge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "edge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "familytree_lan_ddnsgeek_com_a_17017685" {
|
||||
hostname = "familytree.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "familytree"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "familytree_lan_ddnsgeek_com_a_19646056" {
|
||||
hostname = "familytree.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "familytree"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gitea_lan_ddnsgeek_com_a_14682463" {
|
||||
hostname = "gitea.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "gitea"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gitea_lan_ddnsgeek_com_a_19646063" {
|
||||
hostname = "gitea.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "gitea"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gotify_lan_ddnsgeek_com_a_17439061" {
|
||||
hostname = "gotify.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "gotify"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "gotify_lan_ddnsgeek_com_a_19646047" {
|
||||
hostname = "gotify.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "gotify"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "grafana_lan_ddnsgeek_com_a_18113762" {
|
||||
hostname = "grafana.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "grafana"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "grafana_lan_ddnsgeek_com_a_19646050" {
|
||||
hostname = "grafana.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "grafana"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "influxdb_lan_ddnsgeek_com_a_18562198" {
|
||||
hostname = "influxdb.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "influxdb"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "influxdb_lan_ddnsgeek_com_a_19646059" {
|
||||
hostname = "influxdb.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "influxdb"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "kuma_lan_ddnsgeek_com_a_17454978" {
|
||||
hostname = "kuma.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 60
|
||||
enabled = true
|
||||
content = "120.155.99.146"
|
||||
node_name = "kuma"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "lan_ddnsgeek_com_soa_8299670" {
|
||||
hostname = "lan.ddnsgeek.com"
|
||||
record_type = "SOA"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "ns1.dynu.com. administrator.dynu.com. 0 3600 900 604800 300"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "monitor_kuma_lan_ddnsgeek_com_a_17462342" {
|
||||
hostname = "monitor-kuma.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "monitor-kuma"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "monitor_kuma_lan_ddnsgeek_com_a_19646051" {
|
||||
hostname = "monitor-kuma.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "monitor-kuma"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "mtls_bridge_lan_ddnsgeek_com_a_19232643" {
|
||||
hostname = "mtls-bridge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "mtls-bridge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "mtls_bridge_lan_ddnsgeek_com_a_19646058" {
|
||||
hostname = "mtls-bridge.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "mtls-bridge"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "nextcloud_lan_ddnsgeek_com_a_10453260" {
|
||||
hostname = "nextcloud.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "nextcloud"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "nextcloud_lan_ddnsgeek_com_a_19646057" {
|
||||
hostname = "nextcloud.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "nextcloud"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "node_red_lan_ddnsgeek_com_a_19041230" {
|
||||
hostname = "node-red.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "node-red"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "node_red_lan_ddnsgeek_com_a_19646053" {
|
||||
hostname = "node-red.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "node-red"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "passbolt_lan_ddnsgeek_com_a_10453262" {
|
||||
hostname = "passbolt.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "passbolt"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "passbolt_lan_ddnsgeek_com_a_19646049" {
|
||||
hostname = "passbolt.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "passbolt"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "portainer_lan_ddnsgeek_com_a_17458810" {
|
||||
hostname = "portainer.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "portainer"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "portainer_lan_ddnsgeek_com_a_19646046" {
|
||||
hostname = "portainer.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "portainer"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "prometheus_lan_ddnsgeek_com_a_18483311" {
|
||||
hostname = "prometheus.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "prometheus"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "prometheus_lan_ddnsgeek_com_a_19646061" {
|
||||
hostname = "prometheus.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "prometheus"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "searxng_lan_ddnsgeek_com_a_10453263" {
|
||||
hostname = "searxng.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "searxng"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "searxng_lan_ddnsgeek_com_a_19646055" {
|
||||
hostname = "searxng.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "searxng"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "shifts_lan_ddnsgeek_com_a_15901565" {
|
||||
hostname = "shifts.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "shifts"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "shifts_lan_ddnsgeek_com_a_19646052" {
|
||||
hostname = "shifts.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "shifts"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "stockfill_lan_ddnsgeek_com_a_17081867" {
|
||||
hostname = "stockfill.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "stockfill"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "stockfill_lan_ddnsgeek_com_a_19646060" {
|
||||
hostname = "stockfill.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "stockfill"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "traefik_lan_ddnsgeek_com_a_10453240" {
|
||||
hostname = "traefik.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
dynamic = true
|
||||
node_name = "traefik"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "traefik_lan_ddnsgeek_com_a_19646054" {
|
||||
hostname = "traefik.lan.ddnsgeek.com"
|
||||
record_type = "A"
|
||||
ttl = 120
|
||||
enabled = true
|
||||
content = "167.179.167.166"
|
||||
group = "home"
|
||||
node_name = "traefik"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
[
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "auth.lan.ddnsgeek.com",
|
||||
"id": 18483099,
|
||||
"node_name": "auth",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-02-17T12:59:58.803"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "auth.lan.ddnsgeek.com",
|
||||
"id": 19646048,
|
||||
"node_name": "auth",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.693"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "edge.lan.ddnsgeek.com",
|
||||
"id": 10453241,
|
||||
"node_name": "edge",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2024-03-18T01:27:26"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "edge.lan.ddnsgeek.com",
|
||||
"id": 19646062,
|
||||
"node_name": "edge",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:45.237"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "familytree.lan.ddnsgeek.com",
|
||||
"id": 17017685,
|
||||
"node_name": "familytree",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-11-17T04:43:07.953"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "familytree.lan.ddnsgeek.com",
|
||||
"id": 19646056,
|
||||
"node_name": "familytree",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:44.25"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "gitea.lan.ddnsgeek.com",
|
||||
"id": 14682463,
|
||||
"node_name": "gitea",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-07-16T03:15:50.38"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "gitea.lan.ddnsgeek.com",
|
||||
"id": 19646063,
|
||||
"node_name": "gitea",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:45.343"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "gotify.lan.ddnsgeek.com",
|
||||
"id": 17439061,
|
||||
"node_name": "gotify",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-12-16T09:35:47.307"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "gotify.lan.ddnsgeek.com",
|
||||
"id": 19646047,
|
||||
"node_name": "gotify",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.693"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "grafana.lan.ddnsgeek.com",
|
||||
"id": 18113762,
|
||||
"node_name": "grafana",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-01-28T04:38:25.92"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "grafana.lan.ddnsgeek.com",
|
||||
"id": 19646050,
|
||||
"node_name": "grafana",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.72"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "influxdb.lan.ddnsgeek.com",
|
||||
"id": 18562198,
|
||||
"node_name": "influxdb",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-02-22T16:46:26.85"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "influxdb.lan.ddnsgeek.com",
|
||||
"id": 19646059,
|
||||
"node_name": "influxdb",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:44.85"
|
||||
},
|
||||
{
|
||||
"content": "120.155.99.146",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "kuma.lan.ddnsgeek.com",
|
||||
"id": 17454978,
|
||||
"node_name": "kuma",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 60,
|
||||
"updated_on": "2026-04-21T04:50:04.81"
|
||||
},
|
||||
{
|
||||
"content": "ns1.dynu.com. administrator.dynu.com. 0 3600 900 604800 300",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "lan.ddnsgeek.com",
|
||||
"id": 8299670,
|
||||
"node_name": null,
|
||||
"record_type": "SOA",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2022-03-15T10:08:15"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "monitor-kuma.lan.ddnsgeek.com",
|
||||
"id": 17462342,
|
||||
"node_name": "monitor-kuma",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-12-17T14:47:25"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "monitor-kuma.lan.ddnsgeek.com",
|
||||
"id": 19646051,
|
||||
"node_name": "monitor-kuma",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.727"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "mtls-bridge.lan.ddnsgeek.com",
|
||||
"id": 19232643,
|
||||
"node_name": "mtls-bridge",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-04-13T04:20:00.11"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "mtls-bridge.lan.ddnsgeek.com",
|
||||
"id": 19646058,
|
||||
"node_name": "mtls-bridge",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:44.737"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "nextcloud.lan.ddnsgeek.com",
|
||||
"id": 10453260,
|
||||
"node_name": "nextcloud",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2024-03-18T01:40:00"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "nextcloud.lan.ddnsgeek.com",
|
||||
"id": 19646057,
|
||||
"node_name": "nextcloud",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:44.53"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "node-red.lan.ddnsgeek.com",
|
||||
"id": 19041230,
|
||||
"node_name": "node-red",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-03-30T04:51:39.68"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "node-red.lan.ddnsgeek.com",
|
||||
"id": 19646053,
|
||||
"node_name": "node-red",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.737"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "passbolt.lan.ddnsgeek.com",
|
||||
"id": 10453262,
|
||||
"node_name": "passbolt",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2024-03-18T01:40:18"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "passbolt.lan.ddnsgeek.com",
|
||||
"id": 19646049,
|
||||
"node_name": "passbolt",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.693"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "portainer.lan.ddnsgeek.com",
|
||||
"id": 17458810,
|
||||
"node_name": "portainer",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-12-17T10:23:40.077"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "portainer.lan.ddnsgeek.com",
|
||||
"id": 19646046,
|
||||
"node_name": "portainer",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.693"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "prometheus.lan.ddnsgeek.com",
|
||||
"id": 18483311,
|
||||
"node_name": "prometheus",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-02-17T13:17:00.55"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "prometheus.lan.ddnsgeek.com",
|
||||
"id": 19646061,
|
||||
"node_name": "prometheus",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:45.033"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "searxng.lan.ddnsgeek.com",
|
||||
"id": 10453263,
|
||||
"node_name": "searxng",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2024-03-18T01:40:34"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "searxng.lan.ddnsgeek.com",
|
||||
"id": 19646055,
|
||||
"node_name": "searxng",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:38.797"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "shifts.lan.ddnsgeek.com",
|
||||
"id": 15901565,
|
||||
"node_name": "shifts",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-09-30T04:25:20.65"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "shifts.lan.ddnsgeek.com",
|
||||
"id": 19646052,
|
||||
"node_name": "shifts",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.727"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "stockfill.lan.ddnsgeek.com",
|
||||
"id": 17081867,
|
||||
"node_name": "stockfill",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2025-11-21T06:49:33.47"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "stockfill.lan.ddnsgeek.com",
|
||||
"id": 19646060,
|
||||
"node_name": "stockfill",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:44.987"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "traefik.lan.ddnsgeek.com",
|
||||
"id": 10453240,
|
||||
"node_name": "traefik",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2024-03-18T01:27:13"
|
||||
},
|
||||
{
|
||||
"content": "167.179.167.166",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "traefik.lan.ddnsgeek.com",
|
||||
"id": 19646054,
|
||||
"node_name": "traefik",
|
||||
"record_type": "A",
|
||||
"state": true,
|
||||
"ttl": 120,
|
||||
"updated_on": "2026-05-12T21:11:37.737"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env bash
|
||||
# ---------------------------------------------------------------------------
|
||||
# GENERATED FILE - REVIEW BEFORE USE
|
||||
#
|
||||
# Imports existing Dynu DNS records into Terraform state.
|
||||
# Does not apply changes.
|
||||
# ---------------------------------------------------------------------------
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TF_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${TF_ROOT}"
|
||||
|
||||
# Re-running imports will fail for resources already in state.
|
||||
# This script skips imports when state already contains the resource address.
|
||||
|
||||
if terraform state show 'dynu_dns_record.auth_lan_ddnsgeek_com_a_18483099' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.auth_lan_ddnsgeek_com_a_18483099'
|
||||
else
|
||||
terraform import 'dynu_dns_record.auth_lan_ddnsgeek_com_a_18483099' '9695470/18483099'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.auth_lan_ddnsgeek_com_a_19646048' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.auth_lan_ddnsgeek_com_a_19646048'
|
||||
else
|
||||
terraform import 'dynu_dns_record.auth_lan_ddnsgeek_com_a_19646048' '9695470/19646048'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.edge_lan_ddnsgeek_com_a_10453241' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.edge_lan_ddnsgeek_com_a_10453241'
|
||||
else
|
||||
terraform import 'dynu_dns_record.edge_lan_ddnsgeek_com_a_10453241' '9695470/10453241'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.edge_lan_ddnsgeek_com_a_19646062' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.edge_lan_ddnsgeek_com_a_19646062'
|
||||
else
|
||||
terraform import 'dynu_dns_record.edge_lan_ddnsgeek_com_a_19646062' '9695470/19646062'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.familytree_lan_ddnsgeek_com_a_17017685' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.familytree_lan_ddnsgeek_com_a_17017685'
|
||||
else
|
||||
terraform import 'dynu_dns_record.familytree_lan_ddnsgeek_com_a_17017685' '9695470/17017685'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.familytree_lan_ddnsgeek_com_a_19646056' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.familytree_lan_ddnsgeek_com_a_19646056'
|
||||
else
|
||||
terraform import 'dynu_dns_record.familytree_lan_ddnsgeek_com_a_19646056' '9695470/19646056'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.gitea_lan_ddnsgeek_com_a_14682463' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.gitea_lan_ddnsgeek_com_a_14682463'
|
||||
else
|
||||
terraform import 'dynu_dns_record.gitea_lan_ddnsgeek_com_a_14682463' '9695470/14682463'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.gitea_lan_ddnsgeek_com_a_19646063' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.gitea_lan_ddnsgeek_com_a_19646063'
|
||||
else
|
||||
terraform import 'dynu_dns_record.gitea_lan_ddnsgeek_com_a_19646063' '9695470/19646063'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.gotify_lan_ddnsgeek_com_a_17439061' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.gotify_lan_ddnsgeek_com_a_17439061'
|
||||
else
|
||||
terraform import 'dynu_dns_record.gotify_lan_ddnsgeek_com_a_17439061' '9695470/17439061'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.gotify_lan_ddnsgeek_com_a_19646047' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.gotify_lan_ddnsgeek_com_a_19646047'
|
||||
else
|
||||
terraform import 'dynu_dns_record.gotify_lan_ddnsgeek_com_a_19646047' '9695470/19646047'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.grafana_lan_ddnsgeek_com_a_18113762' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.grafana_lan_ddnsgeek_com_a_18113762'
|
||||
else
|
||||
terraform import 'dynu_dns_record.grafana_lan_ddnsgeek_com_a_18113762' '9695470/18113762'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.grafana_lan_ddnsgeek_com_a_19646050' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.grafana_lan_ddnsgeek_com_a_19646050'
|
||||
else
|
||||
terraform import 'dynu_dns_record.grafana_lan_ddnsgeek_com_a_19646050' '9695470/19646050'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.influxdb_lan_ddnsgeek_com_a_18562198' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.influxdb_lan_ddnsgeek_com_a_18562198'
|
||||
else
|
||||
terraform import 'dynu_dns_record.influxdb_lan_ddnsgeek_com_a_18562198' '9695470/18562198'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.influxdb_lan_ddnsgeek_com_a_19646059' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.influxdb_lan_ddnsgeek_com_a_19646059'
|
||||
else
|
||||
terraform import 'dynu_dns_record.influxdb_lan_ddnsgeek_com_a_19646059' '9695470/19646059'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.kuma_lan_ddnsgeek_com_a_17454978' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.kuma_lan_ddnsgeek_com_a_17454978'
|
||||
else
|
||||
terraform import 'dynu_dns_record.kuma_lan_ddnsgeek_com_a_17454978' '9695470/17454978'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.lan_ddnsgeek_com_soa_8299670' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.lan_ddnsgeek_com_soa_8299670'
|
||||
else
|
||||
terraform import 'dynu_dns_record.lan_ddnsgeek_com_soa_8299670' '9695470/8299670'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.monitor_kuma_lan_ddnsgeek_com_a_17462342' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.monitor_kuma_lan_ddnsgeek_com_a_17462342'
|
||||
else
|
||||
terraform import 'dynu_dns_record.monitor_kuma_lan_ddnsgeek_com_a_17462342' '9695470/17462342'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.monitor_kuma_lan_ddnsgeek_com_a_19646051' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.monitor_kuma_lan_ddnsgeek_com_a_19646051'
|
||||
else
|
||||
terraform import 'dynu_dns_record.monitor_kuma_lan_ddnsgeek_com_a_19646051' '9695470/19646051'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.mtls_bridge_lan_ddnsgeek_com_a_19232643' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.mtls_bridge_lan_ddnsgeek_com_a_19232643'
|
||||
else
|
||||
terraform import 'dynu_dns_record.mtls_bridge_lan_ddnsgeek_com_a_19232643' '9695470/19232643'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.mtls_bridge_lan_ddnsgeek_com_a_19646058' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.mtls_bridge_lan_ddnsgeek_com_a_19646058'
|
||||
else
|
||||
terraform import 'dynu_dns_record.mtls_bridge_lan_ddnsgeek_com_a_19646058' '9695470/19646058'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.nextcloud_lan_ddnsgeek_com_a_10453260' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.nextcloud_lan_ddnsgeek_com_a_10453260'
|
||||
else
|
||||
terraform import 'dynu_dns_record.nextcloud_lan_ddnsgeek_com_a_10453260' '9695470/10453260'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.nextcloud_lan_ddnsgeek_com_a_19646057' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.nextcloud_lan_ddnsgeek_com_a_19646057'
|
||||
else
|
||||
terraform import 'dynu_dns_record.nextcloud_lan_ddnsgeek_com_a_19646057' '9695470/19646057'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.node_red_lan_ddnsgeek_com_a_19041230' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.node_red_lan_ddnsgeek_com_a_19041230'
|
||||
else
|
||||
terraform import 'dynu_dns_record.node_red_lan_ddnsgeek_com_a_19041230' '9695470/19041230'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.node_red_lan_ddnsgeek_com_a_19646053' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.node_red_lan_ddnsgeek_com_a_19646053'
|
||||
else
|
||||
terraform import 'dynu_dns_record.node_red_lan_ddnsgeek_com_a_19646053' '9695470/19646053'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.passbolt_lan_ddnsgeek_com_a_10453262' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.passbolt_lan_ddnsgeek_com_a_10453262'
|
||||
else
|
||||
terraform import 'dynu_dns_record.passbolt_lan_ddnsgeek_com_a_10453262' '9695470/10453262'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.passbolt_lan_ddnsgeek_com_a_19646049' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.passbolt_lan_ddnsgeek_com_a_19646049'
|
||||
else
|
||||
terraform import 'dynu_dns_record.passbolt_lan_ddnsgeek_com_a_19646049' '9695470/19646049'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.portainer_lan_ddnsgeek_com_a_17458810' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.portainer_lan_ddnsgeek_com_a_17458810'
|
||||
else
|
||||
terraform import 'dynu_dns_record.portainer_lan_ddnsgeek_com_a_17458810' '9695470/17458810'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.portainer_lan_ddnsgeek_com_a_19646046' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.portainer_lan_ddnsgeek_com_a_19646046'
|
||||
else
|
||||
terraform import 'dynu_dns_record.portainer_lan_ddnsgeek_com_a_19646046' '9695470/19646046'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.prometheus_lan_ddnsgeek_com_a_18483311' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.prometheus_lan_ddnsgeek_com_a_18483311'
|
||||
else
|
||||
terraform import 'dynu_dns_record.prometheus_lan_ddnsgeek_com_a_18483311' '9695470/18483311'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.prometheus_lan_ddnsgeek_com_a_19646061' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.prometheus_lan_ddnsgeek_com_a_19646061'
|
||||
else
|
||||
terraform import 'dynu_dns_record.prometheus_lan_ddnsgeek_com_a_19646061' '9695470/19646061'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.searxng_lan_ddnsgeek_com_a_10453263' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.searxng_lan_ddnsgeek_com_a_10453263'
|
||||
else
|
||||
terraform import 'dynu_dns_record.searxng_lan_ddnsgeek_com_a_10453263' '9695470/10453263'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.searxng_lan_ddnsgeek_com_a_19646055' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.searxng_lan_ddnsgeek_com_a_19646055'
|
||||
else
|
||||
terraform import 'dynu_dns_record.searxng_lan_ddnsgeek_com_a_19646055' '9695470/19646055'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.shifts_lan_ddnsgeek_com_a_15901565' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.shifts_lan_ddnsgeek_com_a_15901565'
|
||||
else
|
||||
terraform import 'dynu_dns_record.shifts_lan_ddnsgeek_com_a_15901565' '9695470/15901565'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.shifts_lan_ddnsgeek_com_a_19646052' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.shifts_lan_ddnsgeek_com_a_19646052'
|
||||
else
|
||||
terraform import 'dynu_dns_record.shifts_lan_ddnsgeek_com_a_19646052' '9695470/19646052'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.stockfill_lan_ddnsgeek_com_a_17081867' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.stockfill_lan_ddnsgeek_com_a_17081867'
|
||||
else
|
||||
terraform import 'dynu_dns_record.stockfill_lan_ddnsgeek_com_a_17081867' '9695470/17081867'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.stockfill_lan_ddnsgeek_com_a_19646060' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.stockfill_lan_ddnsgeek_com_a_19646060'
|
||||
else
|
||||
terraform import 'dynu_dns_record.stockfill_lan_ddnsgeek_com_a_19646060' '9695470/19646060'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.traefik_lan_ddnsgeek_com_a_10453240' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.traefik_lan_ddnsgeek_com_a_10453240'
|
||||
else
|
||||
terraform import 'dynu_dns_record.traefik_lan_ddnsgeek_com_a_10453240' '9695470/10453240'
|
||||
fi
|
||||
|
||||
if terraform state show 'dynu_dns_record.traefik_lan_ddnsgeek_com_a_19646054' >/dev/null 2>&1; then
|
||||
echo 'Skipping already imported: dynu_dns_record.traefik_lan_ddnsgeek_com_a_19646054'
|
||||
else
|
||||
terraform import 'dynu_dns_record.traefik_lan_ddnsgeek_com_a_19646054' '9695470/19646054'
|
||||
fi
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copy this file to imports.tf and adjust IDs after confirming the
|
||||
# published provider docs for import ID formats.
|
||||
# For dynu_domain, import ID is commonly the root domain name.
|
||||
|
||||
import {
|
||||
to = dynu_domain.lan_ddnsgeek_com
|
||||
id = var.dynu_root_domain
|
||||
}
|
||||
|
||||
# DNS record imports are intentionally examples only because the provider
|
||||
# requires explicit record_type/hostname in config before import.
|
||||
#
|
||||
# import {
|
||||
# to = dynu_dns_record.grafana_lan_ddnsgeek_com
|
||||
# id = var.dynu_record_import_id
|
||||
# }
|
||||
@@ -0,0 +1,3 @@
|
||||
data "dynu_dns_records" "root" {
|
||||
hostname = var.dynu_root_domain
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
output "dynu_domain" {
|
||||
description = "Primary Dynu domain represented by this Terraform root."
|
||||
value = local.dynu_domain
|
||||
}
|
||||
|
||||
output "dynu_dns_records_catalog" {
|
||||
description = "Documentation catalog of expected Dynu DNS records discovered from repo service exposure."
|
||||
value = local.dynu_dns_records_catalog
|
||||
}
|
||||
|
||||
output "dynu_dns_inventory" {
|
||||
description = "Documentation-friendly Dynu DNS inventory for export and merge into broader infrastructure docs."
|
||||
value = {
|
||||
provider = "dynu"
|
||||
domain = local.dynu_domain
|
||||
record_count = length(local.dynu_dns_records_catalog)
|
||||
records = local.dynu_dns_records_catalog
|
||||
}
|
||||
}
|
||||
|
||||
output "dynu_root_domain_id" {
|
||||
description = "Dynu numeric domain ID resolved from dynu_root_domain."
|
||||
value = data.dynu_dns_records.root.domain_id
|
||||
}
|
||||
|
||||
output "dynu_root_domain_name" {
|
||||
description = "Dynu root domain name resolved from dynu_root_domain."
|
||||
value = data.dynu_dns_records.root.domain_name
|
||||
}
|
||||
|
||||
output "dynu_dns_records" {
|
||||
description = "Full read-only DNS record inventory returned by Dynu."
|
||||
value = data.dynu_dns_records.root.records
|
||||
}
|
||||
|
||||
output "dynu_dns_hostnames" {
|
||||
description = "Sorted hostname list discovered for dynu_root_domain."
|
||||
value = sort(distinct([for record in data.dynu_dns_records.root.records : record.hostname]))
|
||||
}
|
||||
|
||||
output "dynu_dns_record_import_ids" {
|
||||
description = "Map of Dynu DNS record identity to provider import IDs in domain_id/record_id format."
|
||||
value = {
|
||||
for record in data.dynu_dns_records.root.records :
|
||||
format("%s/%s/%s", record.hostname, record.record_type, record.id) => format("%s/%s", record.domain_id, record.id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
provider "dynu" {
|
||||
# Keep auth local-only; do not commit credentials.
|
||||
api_key = var.dynu_api_key
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
locals {
|
||||
dynu_dns_records_catalog_base = {
|
||||
auth = {
|
||||
hostname = "auth"
|
||||
service = "authelia"
|
||||
source = "core/authelia/docker-compose.yml"
|
||||
purpose = "Authentication portal"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
gitea = {
|
||||
hostname = "gitea"
|
||||
service = "gitea"
|
||||
source = "apps/gitea/docker-compose.yml"
|
||||
purpose = "Gitea service endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
gotify = {
|
||||
hostname = "gotify"
|
||||
service = "gotify"
|
||||
source = "monitoring/gotify/docker-compose.yml"
|
||||
purpose = "Gotify notifications"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
grafana = {
|
||||
hostname = "grafana"
|
||||
service = "grafana"
|
||||
source = "monitoring/grafana/docker-compose.yml"
|
||||
purpose = "Grafana monitoring UI"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
familytree = {
|
||||
hostname = "familytree"
|
||||
service = "gramps"
|
||||
source = "apps/gramps/docker-compose.yml"
|
||||
purpose = "Family tree application"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
influxdb = {
|
||||
hostname = "influxdb"
|
||||
service = "influxdb"
|
||||
source = "monitoring/influxdb/docker-compose.yml"
|
||||
purpose = "InfluxDB metrics endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
monitor_kuma = {
|
||||
hostname = "monitor-kuma"
|
||||
service = "uptime-kuma"
|
||||
source = "monitoring/uptime-kuma/docker-compose.yml"
|
||||
purpose = "Uptime Kuma monitoring UI"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
mtls_bridge = {
|
||||
hostname = "mtls-bridge"
|
||||
service = "mtls-bridge"
|
||||
source = "monitoring/mtls-bridge/docker-compose.yml"
|
||||
purpose = "mTLS bridge API"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
nextcloud = {
|
||||
hostname = "nextcloud"
|
||||
service = "nextcloud-webapp"
|
||||
source = "apps/nextcloud/docker-compose.yml"
|
||||
purpose = "Nextcloud service endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
node_red = {
|
||||
hostname = "node-red"
|
||||
service = "node-red"
|
||||
source = "monitoring/node-red/docker-compose.yml"
|
||||
purpose = "Node-RED automation UI/API"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
passbolt = {
|
||||
hostname = "passbolt"
|
||||
service = "passbolt-webapp"
|
||||
source = "apps/passbolt/docker-compose.yml"
|
||||
purpose = "Passbolt password management"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
portainer = {
|
||||
hostname = "portainer"
|
||||
service = "portainer"
|
||||
source = "monitoring/portainer/docker-compose.yml"
|
||||
purpose = "Portainer admin endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
prometheus = {
|
||||
hostname = "prometheus"
|
||||
service = "prometheus"
|
||||
source = "monitoring/prometheus/docker-compose.yml"
|
||||
purpose = "Prometheus metrics endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
searxng = {
|
||||
hostname = "searxng"
|
||||
service = "searxng"
|
||||
source = "apps/searxng/docker-compose.yml"
|
||||
purpose = "SearXNG search endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
traefik = {
|
||||
hostname = "traefik"
|
||||
service = "traefik"
|
||||
source = "core/traefik/docker-compose.yml"
|
||||
purpose = "Traefik dashboard/API endpoint"
|
||||
record_type = null
|
||||
ttl = null
|
||||
target = null
|
||||
proxied = null
|
||||
}
|
||||
}
|
||||
|
||||
dynu_dns_records_catalog = {
|
||||
for key, record in local.dynu_dns_records_catalog_base :
|
||||
key => merge(record, {
|
||||
fqdn = format("%s.%s", record.hostname, local.dynu_domain)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate Terraform dynu_dns_record resources/import commands from Dynu inventory outputs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_PATH = Path(__file__).resolve()
|
||||
TF_ROOT = SCRIPT_PATH.parents[1]
|
||||
GENERATED_DIR = TF_ROOT / "generated"
|
||||
TF_FILE = GENERATED_DIR / "dynu_dns_records.generated.tf"
|
||||
IMPORT_SCRIPT = GENERATED_DIR / "import-dynu-dns-records.sh"
|
||||
INVENTORY_FILE = GENERATED_DIR / "dynu_dns_records_inventory.json"
|
||||
DEFAULT_RECORDS_OUTPUT = "dynu_dns_records"
|
||||
REQUIRED_RECORD_FIELDS = ("id", "domain_id", "hostname", "record_type")
|
||||
|
||||
HEADER_TF = """# ---------------------------------------------------------------------------
|
||||
# GENERATED FILE - REVIEW BEFORE USE
|
||||
#
|
||||
# Generated from Dynu brownfield DNS inventory.
|
||||
# Do not blindly apply this file to production DNS.
|
||||
# Import records into Terraform state before allowing Terraform to manage them.
|
||||
# ---------------------------------------------------------------------------
|
||||
"""
|
||||
|
||||
HEADER_SH = """#!/usr/bin/env bash
|
||||
# ---------------------------------------------------------------------------
|
||||
# GENERATED FILE - REVIEW BEFORE USE
|
||||
#
|
||||
# Imports existing Dynu DNS records into Terraform state.
|
||||
# Does not apply changes.
|
||||
# ---------------------------------------------------------------------------
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TF_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${TF_ROOT}"
|
||||
|
||||
# Re-running imports will fail for resources already in state.
|
||||
# This script skips imports when state already contains the resource address.
|
||||
"""
|
||||
|
||||
OPTIONAL_FIELDS = ["group", "host", "priority", "weight", "port", "flags", "tag", "value", "node_name"]
|
||||
|
||||
|
||||
def run_terraform_output() -> dict:
|
||||
if not (TF_ROOT / ".terraform").exists():
|
||||
raise RuntimeError("Terraform is not initialized in infrastructure/terraform/dynu. Run: terraform init")
|
||||
|
||||
cmd = ["terraform", "output", "-json"]
|
||||
proc = subprocess.run(cmd, cwd=TF_ROOT, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"Failed to run {' '.join(cmd)}:\n{proc.stderr.strip()}")
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def type_shape_name(value: object) -> str:
|
||||
if isinstance(value, list):
|
||||
return "list"
|
||||
if isinstance(value, dict):
|
||||
return "object"
|
||||
return type(value).__name__
|
||||
|
||||
|
||||
def extract_records(payload: object, output_name: str) -> list[dict]:
|
||||
source = payload
|
||||
if isinstance(payload, list):
|
||||
source = payload
|
||||
elif isinstance(payload, dict):
|
||||
if isinstance(payload.get("value"), list):
|
||||
source = payload["value"]
|
||||
elif isinstance(payload.get("records"), list):
|
||||
source = payload["records"]
|
||||
elif isinstance(payload.get("value"), dict) and isinstance(payload["value"].get("records"), list):
|
||||
source = payload["value"]["records"]
|
||||
elif output_name in payload and isinstance(payload[output_name], dict):
|
||||
output_wrapper = payload[output_name]
|
||||
if isinstance(output_wrapper.get("value"), list):
|
||||
source = output_wrapper["value"]
|
||||
elif isinstance(output_wrapper.get("value"), dict) and isinstance(output_wrapper["value"].get("records"), list):
|
||||
source = output_wrapper["value"]["records"]
|
||||
elif isinstance(output_wrapper.get("records"), list):
|
||||
source = output_wrapper["records"]
|
||||
else:
|
||||
raise RuntimeError(f"Output '{output_name}' does not contain a records list.")
|
||||
else:
|
||||
raise RuntimeError(f"Output '{output_name}' not found and no records list discovered.")
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported JSON payload type: {type(payload).__name__}")
|
||||
|
||||
if not isinstance(source, list):
|
||||
raise RuntimeError(f"Output '{output_name}' did not resolve to a list of records.")
|
||||
return source
|
||||
|
||||
|
||||
def validate_records(records: list[dict], output_name: str) -> None:
|
||||
for i, record in enumerate(records):
|
||||
if not isinstance(record, dict):
|
||||
raise RuntimeError(f"Selected output '{output_name}' has non-object record at index {i}: {type(record).__name__}.")
|
||||
missing = [field for field in REQUIRED_RECORD_FIELDS if field not in record]
|
||||
if missing:
|
||||
missing_text = ", ".join(missing)
|
||||
raise RuntimeError(
|
||||
f"Selected output '{output_name}' contains records, but they are not importable Dynu provider records. "
|
||||
f"Record #{i} is missing required fields: {missing_text}. "
|
||||
"Choose an output sourced from data.dynu_dns_records.root, such as dynu_dns_records or dynu_dns_inventory."
|
||||
)
|
||||
|
||||
|
||||
def describe_output(output_name: str, output_wrapper: object, full_outputs: dict) -> dict:
|
||||
details = {
|
||||
"name": output_name,
|
||||
"usable": False,
|
||||
"shape": type_shape_name(output_wrapper),
|
||||
"record_count": "none",
|
||||
"error": "no records list found",
|
||||
}
|
||||
if isinstance(output_wrapper, dict) and "value" in output_wrapper:
|
||||
details["shape"] = type_shape_name(output_wrapper.get("value"))
|
||||
|
||||
try:
|
||||
records = extract_records(full_outputs, output_name)
|
||||
except RuntimeError as exc:
|
||||
details["error"] = str(exc)
|
||||
return details
|
||||
|
||||
details["record_count"] = len(records)
|
||||
try:
|
||||
validate_records(records, output_name)
|
||||
except RuntimeError as exc:
|
||||
details["error"] = str(exc)
|
||||
if isinstance(output_wrapper, dict) and isinstance(output_wrapper.get("value"), dict) and isinstance(output_wrapper["value"].get("records"), list):
|
||||
details["shape"] = "object with records list"
|
||||
elif isinstance(output_wrapper, dict) and isinstance(output_wrapper.get("value"), list):
|
||||
details["shape"] = "list"
|
||||
return details
|
||||
|
||||
details["usable"] = True
|
||||
details["error"] = None
|
||||
if isinstance(output_wrapper, dict) and isinstance(output_wrapper.get("value"), dict) and isinstance(output_wrapper["value"].get("records"), list):
|
||||
details["shape"] = "object with records list"
|
||||
elif isinstance(output_wrapper, dict) and isinstance(output_wrapper.get("value"), list):
|
||||
details["shape"] = "list"
|
||||
return details
|
||||
|
||||
|
||||
def choose_output_interactively(outputs: dict, descriptions: list[dict]) -> str | None:
|
||||
print("\nAvailable Terraform outputs:\n")
|
||||
indexed = {str(i): item for i, item in enumerate(descriptions, 1)}
|
||||
by_name = {item["name"]: item for item in descriptions}
|
||||
|
||||
for i, item in enumerate(descriptions, 1):
|
||||
print(f" {i}) {item['name']}")
|
||||
print(f" usable: {'yes' if item['usable'] else 'no'}")
|
||||
print(f" shape: {item['shape']}")
|
||||
print(f" record count: {item['record_count']}")
|
||||
if item["error"]:
|
||||
print(f" reason: {item['error']}")
|
||||
print()
|
||||
|
||||
attempts = 0
|
||||
while attempts < 3:
|
||||
attempts += 1
|
||||
try:
|
||||
selection = input(f"Choose an output to use for DNS records [1-{len(descriptions)}], or press Enter to cancel: ").strip()
|
||||
except KeyboardInterrupt:
|
||||
print("\nSelection cancelled.")
|
||||
return None
|
||||
|
||||
if selection == "":
|
||||
print("Selection cancelled.")
|
||||
return None
|
||||
|
||||
candidate = indexed.get(selection) or by_name.get(selection)
|
||||
if candidate is None:
|
||||
print("Invalid selection. Enter a number from the list or an exact output name.")
|
||||
continue
|
||||
|
||||
if not candidate["usable"]:
|
||||
print(f"Output '{candidate['name']}' is not usable: {candidate['error']}")
|
||||
continue
|
||||
|
||||
return candidate["name"]
|
||||
|
||||
raise RuntimeError("Too many invalid selections. Exiting without writing files.")
|
||||
|
||||
|
||||
def tf_name(record: dict) -> str:
|
||||
base = f"{record.get('hostname', '')}_{record.get('record_type', '')}_{record.get('id', '')}".lower()
|
||||
base = base.replace("*", "wildcard")
|
||||
base = re.sub(r"[^a-z0-9_]+", "_", base)
|
||||
base = re.sub(r"_+", "_", base).strip("_")
|
||||
if not base or not re.match(r"^[a-z]", base):
|
||||
base = f"record_{base}" if base else "record"
|
||||
if not base.endswith(str(record.get("id", ""))):
|
||||
base = f"{base}_{record.get('id', '')}"
|
||||
return base
|
||||
|
||||
|
||||
def hcl_value(value):
|
||||
if isinstance(value, bool):
|
||||
return "true" if value else "false"
|
||||
if isinstance(value, (int, float)):
|
||||
return str(value)
|
||||
return json.dumps(value)
|
||||
|
||||
|
||||
def generate_resources(records: list[dict]) -> str:
|
||||
chunks = [HEADER_TF.rstrip(), ""]
|
||||
for rec in records:
|
||||
name = tf_name(rec)
|
||||
lines = [f'resource "dynu_dns_record" "{name}" {{']
|
||||
lines.append(f" hostname = {hcl_value(rec.get('hostname'))}")
|
||||
lines.append(f" record_type = {hcl_value(rec.get('record_type'))}")
|
||||
if rec.get("ttl") is not None:
|
||||
lines.append(f" ttl = {hcl_value(rec.get('ttl'))}")
|
||||
enabled = rec.get("enabled")
|
||||
if enabled is None:
|
||||
enabled = rec.get("state")
|
||||
if enabled is not None:
|
||||
lines.append(f" enabled = {hcl_value(enabled)}")
|
||||
|
||||
content = rec.get("content")
|
||||
rtype = str(rec.get("record_type", "")).upper()
|
||||
if content in (None, "") and rtype in {"A", "AAAA"}:
|
||||
lines.append(" dynamic = true")
|
||||
elif content not in (None, ""):
|
||||
lines.append(f" content = {hcl_value(content)}")
|
||||
|
||||
for field in OPTIONAL_FIELDS:
|
||||
value = rec.get(field)
|
||||
if value not in (None, ""):
|
||||
lines.append(f" {field.ljust(11)}= {hcl_value(value)}")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
" lifecycle {",
|
||||
" prevent_destroy = true",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
])
|
||||
chunks.extend(lines)
|
||||
return "\n".join(chunks).rstrip() + "\n"
|
||||
|
||||
|
||||
def generate_import_script(records: list[dict]) -> str:
|
||||
lines = [HEADER_SH.rstrip(), ""]
|
||||
for rec in records:
|
||||
name = tf_name(rec)
|
||||
import_id = f"{rec['domain_id']}/{rec['id']}"
|
||||
addr = f"dynu_dns_record.{name}"
|
||||
lines.append(f"if terraform state show '{addr}' >/dev/null 2>&1; then")
|
||||
lines.append(f" echo 'Skipping already imported: {addr}'")
|
||||
lines.append("else")
|
||||
lines.append(f" terraform import '{addr}' '{import_id}'")
|
||||
lines.append("fi")
|
||||
lines.append("")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def write_file(path: Path, content: str, dry_run: bool, overwrite: bool) -> None:
|
||||
if path.exists() and not overwrite:
|
||||
raise RuntimeError(f"Refusing to overwrite existing file: {path}. Re-run with --overwrite.")
|
||||
if dry_run:
|
||||
print(f"[dry-run] Would write {path}")
|
||||
return
|
||||
path.write_text(content, encoding="utf-8")
|
||||
print(f"Wrote {path}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print intended output paths without writing files.")
|
||||
parser.add_argument("--overwrite", "--force", action="store_true", dest="overwrite", help="Overwrite existing generated files.")
|
||||
parser.add_argument("--from-file", type=Path, help="Load inventory JSON from a file instead of calling terraform output.")
|
||||
parser.add_argument(
|
||||
"--records-output",
|
||||
default=None,
|
||||
help=(
|
||||
"Terraform output name containing Dynu DNS records. "
|
||||
f"Defaults to {DEFAULT_RECORDS_OUTPUT}; if missing in an interactive terminal, "
|
||||
"the script prompts you to choose from available outputs."
|
||||
),
|
||||
)
|
||||
parser.add_argument("--no-interactive", action="store_true", help="Disable interactive output selection.")
|
||||
args = parser.parse_args()
|
||||
|
||||
records_output_explicit = args.records_output is not None
|
||||
records_output = args.records_output or DEFAULT_RECORDS_OUTPUT
|
||||
|
||||
try:
|
||||
payload = json.loads(args.from_file.read_text(encoding="utf-8")) if args.from_file else run_terraform_output()
|
||||
|
||||
selected_output = records_output
|
||||
descriptions: list[dict] = []
|
||||
if isinstance(payload, dict):
|
||||
descriptions = [describe_output(name, payload[name], payload) for name in sorted(payload)]
|
||||
|
||||
try:
|
||||
records = extract_records(payload, selected_output)
|
||||
validate_records(records, selected_output)
|
||||
except RuntimeError as exc:
|
||||
is_interactive = sys.stdin.isatty() and not args.no_interactive
|
||||
should_prompt = isinstance(payload, dict) and not records_output_explicit and is_interactive
|
||||
if should_prompt:
|
||||
print(f"Terraform output '{selected_output}' was not found or is unusable.\n")
|
||||
chosen = choose_output_interactively(payload, descriptions)
|
||||
if chosen is None:
|
||||
print("Exiting without writing files.")
|
||||
return 1
|
||||
selected_output = chosen
|
||||
records = extract_records(payload, selected_output)
|
||||
validate_records(records, selected_output)
|
||||
else:
|
||||
if isinstance(payload, dict):
|
||||
available = ", ".join(sorted(payload.keys())) or "(none)"
|
||||
if records_output_explicit:
|
||||
raise RuntimeError(
|
||||
f"Missing or unusable Terraform output '{selected_output}'. "
|
||||
f"Available outputs: {available}. Details: {exc}"
|
||||
)
|
||||
raise RuntimeError(
|
||||
f"Missing or unusable Terraform output '{selected_output}'. "
|
||||
f"Available outputs: {available}.\n\n"
|
||||
"Run interactively to choose an output, or pass one explicitly, for example:\n\n"
|
||||
" python3 scripts/generate-brownfield-records.py --records-output dynu_dns_inventory --dry-run"
|
||||
)
|
||||
raise
|
||||
|
||||
GENERATED_DIR.mkdir(parents=True, exist_ok=True)
|
||||
write_file(INVENTORY_FILE, json.dumps(records, indent=2, sort_keys=True) + "\n", args.dry_run, args.overwrite)
|
||||
write_file(TF_FILE, generate_resources(records), args.dry_run, args.overwrite)
|
||||
write_file(IMPORT_SCRIPT, generate_import_script(records), args.dry_run, args.overwrite)
|
||||
if not args.dry_run:
|
||||
IMPORT_SCRIPT.chmod(0o755)
|
||||
return 0
|
||||
except Exception as exc: # noqa: BLE001
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,7 @@
|
||||
# Local-only credentials. Do not commit real values.
|
||||
dynu_api_key = "replace-with-dynu-api-key"
|
||||
dynu_username = null
|
||||
dynu_password = null
|
||||
|
||||
dynu_root_domain = "lan.ddnsgeek.com"
|
||||
dynu_record_import_id = "REPLACE_WITH_DYNU_RECORD_IMPORT_ID"
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
variable "dynu_root_domain" {
|
||||
description = "Dynu root domain name to reconcile/import (for example: lan.ddnsgeek.com)."
|
||||
type = string
|
||||
default = "lan.ddnsgeek.com"
|
||||
}
|
||||
|
||||
variable "dynu_api_key" {
|
||||
description = "Dynu API key/token used by the Dynu Terraform provider."
|
||||
type = string
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "dynu_username" {
|
||||
description = "Optional Dynu username, only if required by the provider."
|
||||
type = string
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "dynu_password" {
|
||||
description = "Optional Dynu password, only if required by the provider."
|
||||
type = string
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "dynu_record_import_id" {
|
||||
description = "Placeholder import ID for a single dynu_dns_record during one-at-a-time reconciliation."
|
||||
type = string
|
||||
default = "REPLACE_WITH_DYNU_RECORD_IMPORT_ID"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.6.0"
|
||||
|
||||
required_providers {
|
||||
dynu = {
|
||||
source = "beatz174-bit/dynu"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,16 @@ output "physical_hosts" {
|
||||
value = local.physical_hosts
|
||||
}
|
||||
|
||||
output "virtual_hosts" {
|
||||
description = "Virtual host/VM inventory used for documentation"
|
||||
value = local.virtual_hosts
|
||||
}
|
||||
|
||||
|
||||
output "infrastructure_inventory" {
|
||||
description = "Combined infrastructure inventory"
|
||||
value = {
|
||||
physical_hosts = local.physical_hosts
|
||||
virtual_hosts = local.virtual_hosts
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,59 @@ locals {
|
||||
notes = "Raspberry Pi host"
|
||||
}
|
||||
}
|
||||
|
||||
# Virtual host inventory for documentation output. This is intentionally
|
||||
# concise and shaped for docs tooling (not a full provider object dump).
|
||||
virtual_hosts = {
|
||||
docker = {
|
||||
name = "docker"
|
||||
type = "virtual"
|
||||
role = "docker-host"
|
||||
proxmox_node = "pve"
|
||||
vm_id = 103
|
||||
management_ip = ""
|
||||
os_family = "linux"
|
||||
notes = "Primary Docker VM"
|
||||
}
|
||||
server_nixos = {
|
||||
name = "server-nixos"
|
||||
type = "virtual"
|
||||
role = "nixos-server"
|
||||
proxmox_node = "pve"
|
||||
vm_id = 104
|
||||
management_ip = ""
|
||||
os_family = "nixos"
|
||||
notes = "General-purpose NixOS VM"
|
||||
}
|
||||
nix_cache = {
|
||||
name = "nix-cache"
|
||||
type = "virtual"
|
||||
role = "cache"
|
||||
proxmox_node = "pve"
|
||||
vm_id = 105
|
||||
management_ip = ""
|
||||
os_family = "linux"
|
||||
notes = "Nix binary cache VM"
|
||||
}
|
||||
pbs = {
|
||||
name = "pbs"
|
||||
type = "virtual"
|
||||
role = "backup"
|
||||
proxmox_node = "pve"
|
||||
vm_id = 106
|
||||
management_ip = ""
|
||||
os_family = "linux"
|
||||
notes = "Proxmox Backup Server VM"
|
||||
}
|
||||
pihole = {
|
||||
name = "pihole"
|
||||
type = "virtual"
|
||||
role = "dns"
|
||||
proxmox_node = "pve"
|
||||
vm_id = 108
|
||||
management_ip = ""
|
||||
os_family = "linux"
|
||||
notes = "DNS filtering VM"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
site_name: Public Infrastructure Documentation
|
||||
site_description: Public-facing infrastructure documentation
|
||||
repo_url: https://github.com/beatz174-bit/docker
|
||||
site_url: https://beatz174-bit.github.io/docker/
|
||||
docs_dir: docs/public
|
||||
site_dir: site-public
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Diagrams: diagrams.md
|
||||
- Compose Inventory: compose-inventory.md
|
||||
- Traefik Routes: traefik-routes.md
|
||||
|
||||
theme:
|
||||
name: mkdocs
|
||||
extra_css:
|
||||
- stylesheets/extra.css
|
||||
|
||||
validation:
|
||||
nav:
|
||||
omitted_files: ignore
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
site_name: Infrastructure Documentation
|
||||
site_description: Generated and maintained infrastructure documentation
|
||||
repo_url: https://github.com/beatz174-bit/docker
|
||||
docs_dir: docs
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Docker Environment: docker.md
|
||||
- Networking: networking.md
|
||||
- Monitoring: monitoring.md
|
||||
- Automation: automation.md
|
||||
- Operations: operations.md
|
||||
- Public Showcase: showcase.md
|
||||
- Generated:
|
||||
- Compose Inventory: generated/compose-inventory.md
|
||||
- Traefik Routes: generated/traefik-routes.md
|
||||
|
||||
theme:
|
||||
name: mkdocs
|
||||
|
||||
validation:
|
||||
nav:
|
||||
omitted_files: ignore
|
||||
exclude_docs: |
|
||||
README.md
|
||||
@@ -1,3 +1,28 @@
|
||||
{
|
||||
"dockerUpdateAttempts": {}
|
||||
"dockerUpdateAttempts": {
|
||||
"nextcloud-redis|redis:latest|docker": {
|
||||
"time": 1778483142172,
|
||||
"status": "test_failed",
|
||||
"failedAt": 1778483206801,
|
||||
"notified": true
|
||||
},
|
||||
"telegraf|telegraf:latest|raspi": {
|
||||
"time": 1778569512188,
|
||||
"status": "pull_failed",
|
||||
"failedAt": 1778569512904,
|
||||
"notified": true
|
||||
},
|
||||
"traefik|traefik:3|raspi": {
|
||||
"time": 1778569512188,
|
||||
"status": "pull_failed",
|
||||
"failedAt": 1778569512880,
|
||||
"notified": true
|
||||
},
|
||||
"searxng-webapp|searxng/searxng:latest|docker": {
|
||||
"time": 1778613012267,
|
||||
"status": "success",
|
||||
"completedAt": 1778613022513,
|
||||
"notified": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -442,3 +442,79 @@
|
||||
{"ts":"2026-04-17T07:05:12.795Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-17T07:05:12.795Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-17T19:11:29.490Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-21T07:05:42.645Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-21T07:05:42.646Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-21T07:05:59.089Z","flow":"docker-updates","event":"completed","container":"gitea","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-21T07:06:45.971Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-21T07:06:45.971Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-21T07:06:56.112Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-21T07:06:56.112Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-22T07:05:43.226Z","flow":"docker-updates","event":"completed","container":"gramps-web","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-22T07:05:43.226Z","flow":"docker-updates","event":"completed","container":"gramps-web","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-22T07:05:43.237Z","flow":"docker-updates","event":"completed","container":"gramps-web-celery","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-22T07:05:43.237Z","flow":"docker-updates","event":"completed","container":"gramps-web-celery","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-22T07:06:55.068Z","flow":"docker-updates","event":"completed","container":"influxdb","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-22T07:06:55.069Z","flow":"docker-updates","event":"completed","container":"influxdb","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-22T11:50:51.776Z","flow":"docker-updates","event":"completed","container":"gitea","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-24T07:06:46.595Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-24T07:06:46.596Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-25T07:06:01.141Z","flow":"docker-updates","event":"completed","container":"gitea","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":1,"code":0,"error":""}
|
||||
{"ts":"2026-04-25T07:06:05.952Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-25T08:21:45.451Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-25T08:21:45.451Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-26T07:05:42.647Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-26T07:05:42.647Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-26T07:06:59.195Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-26T19:10:52.147Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-26T19:10:52.183Z","flow":"docker-updates","event":"completed","container":"gitea","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-27T07:05:42.810Z","flow":"docker-updates","event":"completed","container":"gramps-web","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-27T07:05:42.811Z","flow":"docker-updates","event":"completed","container":"gramps-web","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-27T07:05:42.820Z","flow":"docker-updates","event":"completed","container":"gramps-web-celery","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-27T07:05:42.820Z","flow":"docker-updates","event":"completed","container":"gramps-web-celery","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-27T19:11:52.376Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-29T07:06:01.058Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-30T07:05:42.792Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-30T07:05:42.793Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-04-30T07:07:03.429Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-04-30T07:55:52.210Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-02T07:06:02.030Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-03T19:11:00.985Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-04T07:05:43.437Z","flow":"docker-updates","event":"completed","container":"gramps-web-celery","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-04T07:05:43.437Z","flow":"docker-updates","event":"completed","container":"gramps-web-celery","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-04T07:05:43.478Z","flow":"docker-updates","event":"completed","container":"gramps-web","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-04T07:05:43.478Z","flow":"docker-updates","event":"completed","container":"gramps-web","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-04T07:06:49.671Z","flow":"docker-updates","event":"completed","container":"pihole-exporter","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-05T07:06:53.883Z","flow":"docker-updates","event":"completed","container":"influxdb","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-05T07:06:53.883Z","flow":"docker-updates","event":"completed","container":"influxdb","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-05T07:15:53.751Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-05T19:11:46.870Z","flow":"docker-updates","event":"completed","container":"pihole-exporter","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-06T07:05:43.370Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-06T07:05:43.371Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-06T07:06:52.049Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-06T07:06:52.050Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-06T07:07:04.711Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-06T19:21:00.548Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-07T07:05:43.486Z","flow":"docker-updates","event":"completed","container":"portainer-agent","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-07T07:05:43.486Z","flow":"docker-updates","event":"completed","container":"portainer-agent","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-07T07:06:00.453Z","flow":"docker-updates","event":"completed","container":"gramps-redis","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-07T07:06:54.399Z","flow":"docker-updates","event":"completed","container":"portainer","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-07T07:16:46.710Z","flow":"docker-updates","event":"completed","container":"pihole-exporter","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-07T19:11:59.538Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-08T07:25:53.059Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-08T19:10:53.346Z","flow":"docker-updates","event":"completed","container":"gramps-redis","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-08T19:11:48.007Z","flow":"docker-updates","event":"completed","container":"portainer","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":1,"code":0,"error":""}
|
||||
{"ts":"2026-05-08T19:21:46.912Z","flow":"docker-updates","event":"completed","container":"pihole-exporter","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-09T07:05:43.856Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-09T07:05:43.856Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-09T07:06:56.795Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-09T07:06:56.795Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-09T07:16:59.180Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-09T19:30:53.415Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-11T07:06:02.234Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-11T07:06:46.802Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"failed","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-11T07:06:46.802Z","flow":"docker-updates","event":"completed","container":"nextcloud-redis","project":"unknown","host":"docker","status":"locked","success":0,"failed":1,"duration_ms":0,"code":0,"error":""}
|
||||
{"ts":"2026-05-12T07:05:12.881Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-12T07:05:12.881Z","flow":"docker-updates","event":"completed","container":"traefik","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-12T07:05:12.904Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"raspi","status":"failed","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-12T07:05:12.904Z","flow":"docker-updates","event":"completed","container":"telegraf","project":"unknown","host":"raspi","status":"locked","success":0,"failed":1,"duration_ms":0,"code":1,"error":""}
|
||||
{"ts":"2026-05-12T19:10:22.519Z","flow":"docker-updates","event":"completed","container":"searxng-webapp","project":"unknown","host":"docker","status":"success","success":1,"failed":0,"duration_ms":0,"code":0,"error":""}
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
inv_dir="${repo_root}/infrastructure/terraform/proxmox"
|
||||
json_out="${repo_root}/data/terraform/proxmox-inventory.json"
|
||||
md_out="${repo_root}/docs/generated/host-topology.md"
|
||||
|
||||
mkdir -p "$(dirname "${json_out}")" "$(dirname "${md_out}")"
|
||||
|
||||
(
|
||||
cd "${inv_dir}"
|
||||
terraform output -json infrastructure_inventory > "${json_out}"
|
||||
)
|
||||
|
||||
python3 "${repo_root}/scripts/docs/generate_host_topology.py" \
|
||||
--input "${json_out}" \
|
||||
--output "${md_out}"
|
||||
|
||||
echo "Generated: ${md_out}"
|
||||
@@ -0,0 +1,4 @@
|
||||
PROJECT_ROOT=.
|
||||
TZ=UTC
|
||||
DOMAIN=example.internal
|
||||
SECRETS_ENV=scripts/docs/ci-secrets-placeholder.env
|
||||
@@ -0,0 +1,2 @@
|
||||
EXAMPLE_PASSWORD=placeholder
|
||||
EXAMPLE_TOKEN=placeholder
|
||||
Executable
+115
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
ALLOW_STALE_TERRAFORM=0
|
||||
SKIP_TERRAFORM=0
|
||||
SKIP_DNS=0
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/docs/generate-all.sh [--allow-stale-terraform] [--skip-terraform] [--skip-dns]
|
||||
|
||||
Options:
|
||||
--allow-stale-terraform Use existing Terraform JSON when terraform is unavailable.
|
||||
--skip-terraform Skip Terraform refresh and require existing JSON artifacts.
|
||||
--skip-dns Skip Dynu DNS Terraform refresh.
|
||||
USAGE
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--allow-stale-terraform) ALLOW_STALE_TERRAFORM=1 ;;
|
||||
--skip-terraform) SKIP_TERRAFORM=1 ;;
|
||||
--skip-dns) SKIP_DNS=1 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "ERROR: Unknown option: $1" >&2; usage; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
mkdir -p docs/generated docs/diagrams docs/public data/terraform
|
||||
[[ -d infrastructure/terraform/proxmox ]] && mkdir -p infrastructure/terraform/proxmox/generated
|
||||
[[ -d infrastructure/terraform/dynu ]] && mkdir -p infrastructure/terraform/dynu/generated
|
||||
|
||||
if ! scripts/docs/render-compose-config.sh; then
|
||||
echo "ERROR: Docker Compose config render failed." >&2
|
||||
echo "Check services-up.sh, compose files, and default-environment.env." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -s docs/generated/docker-compose.resolved.yml ]]; then
|
||||
echo "ERROR: Expected non-empty docs/generated/docker-compose.resolved.yml after compose render." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROXMOX_DIR="infrastructure/terraform/proxmox"
|
||||
PRIMARY_HOST_INV="data/terraform/proxmox-inventory.json"
|
||||
FALLBACK_HOST_INV="infrastructure/terraform/proxmox/generated/infrastructure_inventory.json"
|
||||
HOST_INVENTORY=""
|
||||
|
||||
if [[ -d "$PROXMOX_DIR" ]]; then
|
||||
if [[ "$SKIP_TERRAFORM" -eq 1 ]]; then
|
||||
echo "INFO: --skip-terraform set; using pre-generated Terraform JSON artifacts." >&2
|
||||
elif command -v terraform >/dev/null 2>&1; then
|
||||
scripts/docs/build_host_topology.sh
|
||||
elif [[ "$ALLOW_STALE_TERRAFORM" -eq 1 ]]; then
|
||||
echo "WARNING: terraform unavailable; using stale Terraform JSON due to --allow-stale-terraform." >&2
|
||||
else
|
||||
echo "ERROR: terraform is not available, but $PROXMOX_DIR exists and must be refreshed." >&2
|
||||
echo "Install terraform and rerun scripts/docs/generate-all.sh, or use --allow-stale-terraform/--skip-terraform intentionally." >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "ERROR: Required Terraform directory missing: $PROXMOX_DIR" >&2
|
||||
echo "Physical topology diagrams require host inventory from Terraform." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for p in "$PRIMARY_HOST_INV" "$FALLBACK_HOST_INV"; do
|
||||
if [[ -s "$p" ]]; then
|
||||
HOST_INVENTORY="$p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -z "$HOST_INVENTORY" ]]; then
|
||||
echo "ERROR: Host topology inventory is missing." >&2
|
||||
echo "Expected one of: $PRIMARY_HOST_INV or $FALLBACK_HOST_INV" >&2
|
||||
echo "Run scripts/docs/build_host_topology.sh from a machine with Terraform state access, then rerun scripts/docs/generate-all.sh." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DNS_INVENTORY="infrastructure/terraform/dynu/generated/dynu_dns_records_inventory.json"
|
||||
if [[ "$SKIP_DNS" -eq 0 && -d infrastructure/terraform/dynu ]]; then
|
||||
if [[ "$SKIP_TERRAFORM" -eq 1 ]]; then
|
||||
echo "INFO: Skipping Dynu Terraform refresh due to --skip-terraform." >&2
|
||||
elif command -v terraform >/dev/null 2>&1; then
|
||||
scripts/docs/generate_dynu_dns_inventory.sh
|
||||
elif [[ "$ALLOW_STALE_TERRAFORM" -eq 1 ]]; then
|
||||
echo "WARNING: terraform unavailable; using stale Dynu DNS inventory due to --allow-stale-terraform." >&2
|
||||
else
|
||||
echo "ERROR: terraform unavailable; cannot refresh Dynu DNS inventory while infrastructure/terraform/dynu exists." >&2
|
||||
echo "Install terraform and rerun, or use --allow-stale-terraform/--skip-terraform intentionally." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
python3 scripts/docs/generate-compose-inventory.py docs/generated/docker-compose.resolved.yml docs/generated/compose-inventory.md
|
||||
python3 scripts/docs/generate-traefik-routes.py docs/generated/docker-compose.resolved.yml docs/generated/traefik-routes.md
|
||||
python3 scripts/docs/generate-docs-index.py docs/generated/index.md
|
||||
|
||||
GEN_ARGS=(--compose docs/generated/docker-compose.resolved.yml --out-dir docs/diagrams --domain-display redacted-label --host-inventory "$HOST_INVENTORY")
|
||||
[[ -s "$DNS_INVENTORY" && "$SKIP_DNS" -eq 0 ]] && GEN_ARGS+=(--dns-inventory "$DNS_INVENTORY")
|
||||
python3 scripts/docs/generate-diagrams.py "${GEN_ARGS[@]}"
|
||||
python3 scripts/docs/sanitize-public-docs.py docs/generated docs/diagrams docs/public
|
||||
|
||||
[[ -s docs/public/physical-topology.svg ]] || { echo "ERROR: docs/public/physical-topology.svg missing or empty." >&2; exit 1; }
|
||||
if grep -Fq "Host inventory JSON not found" docs/public/physical-topology.svg || grep -Fq "Generate terraform inventory" docs/public/physical-topology.svg; then
|
||||
echo "ERROR: docs/public/physical-topology.svg contains placeholder/error text; host inventory refresh failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ -s docs/public/docker-traefik-dynu.svg ]] || { echo "ERROR: docs/public/docker-traefik-dynu.svg missing or empty." >&2; exit 1; }
|
||||
[[ -s docs/generated/docker-compose.resolved.yml ]] || { echo "ERROR: docs/generated/docker-compose.resolved.yml missing or empty." >&2; exit 1; }
|
||||
[[ -s docs/generated/host-topology.md ]] || { echo "ERROR: docs/generated/host-topology.md missing or empty." >&2; exit 1; }
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import sys, yaml
|
||||
|
||||
def md(v): return str(v).replace('|','\\|') if v is not None else ''
|
||||
|
||||
inp,out=sys.argv[1],sys.argv[2]
|
||||
with open(inp) as f: c=yaml.safe_load(f) or {}
|
||||
svcs=c.get('services',{}) or {}
|
||||
nets=c.get('networks',{}) or {}
|
||||
vols=c.get('volumes',{}) or {}
|
||||
raw=open(inp,'rb').read()
|
||||
fingerprint=hashlib.sha256(raw).hexdigest()[:12]
|
||||
lines=["# Docker Compose Inventory","",f"Source fingerprint: `{fingerprint}`","","## Summary","","| Item | Count |","|---|---:|",f"| Services | {len(svcs)} |",f"| Networks | {len(nets)} |",f"| Volumes | {len(vols)} |","","## Services","","| Service | Container | Image | Build | Profiles | Networks | Ports | Restart |","|---|---|---|---|---|---|---|---|"]
|
||||
for n,s in sorted(svcs.items()):
|
||||
build=s.get('build','')
|
||||
if isinstance(build,dict): build=build.get('context','')
|
||||
ports=', '.join(str(p) for p in s.get('ports',[]) )
|
||||
networks=', '.join((s.get('networks') or {}).keys() if isinstance(s.get('networks'),dict) else (s.get('networks') or []))
|
||||
profiles=', '.join(s.get('profiles',[]) or [])
|
||||
lines.append(f"| {md(n)} | {md(s.get('container_name',''))} | {md(s.get('image',''))} | {md(build)} | {md(profiles)} | {md(networks)} | {md(ports)} | {md(s.get('restart',''))} |")
|
||||
lines += ["","## Networks","","| Network | Driver | External |","|---|---|---|"]
|
||||
for n,v in sorted(nets.items()): lines.append(f"| {md(n)} | {md((v or {}).get('driver',''))} | {md((v or {}).get('external',False))} |")
|
||||
lines += ["","## Volumes","","| Volume | External |","|---|---|"]
|
||||
for n,v in sorted(vols.items()): lines.append(f"| {md(n)} | {md((v or {}).get('external',False))} |")
|
||||
open(out,'w').write('\n'.join(lines)+'\n')
|
||||
@@ -0,0 +1,360 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
|
||||
INTERNAL_DOMAIN_RE = re.compile(r"\b[a-zA-Z0-9.-]+\.lan\.ddnsgeek\.com\b")
|
||||
HOST_MATCH_RE = re.compile(r"Host\(([^)]*)\)")
|
||||
TICKED_HOST_RE = re.compile(r"`([^`]+)`")
|
||||
|
||||
|
||||
def require_dot() -> None:
|
||||
if not shutil.which("dot"):
|
||||
raise SystemExit(
|
||||
"Graphviz 'dot' not found in environment. Install graphviz before running docs generation."
|
||||
)
|
||||
|
||||
|
||||
def load_compose(path: Path) -> dict:
|
||||
with path.open() as handle:
|
||||
return yaml.safe_load(handle) or {}
|
||||
|
||||
|
||||
PARENT_KEYS = ("node", "node_name", "host", "physical_host", "hypervisor_host", "proxmox_node")
|
||||
|
||||
|
||||
def display_domain(value: str, mode: str) -> str:
|
||||
if mode == "full":
|
||||
return value
|
||||
if mode == "placeholder":
|
||||
return "<internal-domain>" if INTERNAL_DOMAIN_RE.search(value) else value
|
||||
if INTERNAL_DOMAIN_RE.search(value):
|
||||
label = re.sub(r"\.lan\.ddnsgeek\.com$", "", value)
|
||||
return f"{label}.<domain>"
|
||||
return value
|
||||
|
||||
|
||||
def parse_labels(service: dict) -> dict[str, str]:
|
||||
labels = service.get("labels") or {}
|
||||
if isinstance(labels, list):
|
||||
mapped = {}
|
||||
for item in labels:
|
||||
if "=" in str(item):
|
||||
k, v = str(item).split("=", 1)
|
||||
mapped[k] = v
|
||||
return mapped
|
||||
if isinstance(labels, dict):
|
||||
return {str(k): str(v) for k, v in labels.items()}
|
||||
return {}
|
||||
|
||||
|
||||
def extract_hosts(rule: str) -> list[str]:
|
||||
hosts: list[str] = []
|
||||
for m in HOST_MATCH_RE.findall(rule or ""):
|
||||
for host in TICKED_HOST_RE.findall(m):
|
||||
hosts.append(host.strip())
|
||||
return hosts
|
||||
|
||||
|
||||
def render_svg(dot_path: Path, svg_path: Path) -> None:
|
||||
subprocess.run(["dot", "-Tsvg", str(dot_path), "-o", str(svg_path)], check=True)
|
||||
|
||||
|
||||
def write_dot(path: Path, lines: list[str]) -> None:
|
||||
path.write_text("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def infer_host(service_name: str, service: dict) -> str:
|
||||
labels = parse_labels(service)
|
||||
for key in ("com.docker.compose.project", "infra.host", "host"):
|
||||
value = labels.get(key)
|
||||
if value:
|
||||
return value.lower()
|
||||
s = service_name.lower()
|
||||
if "raspi" in s or "pi" in s:
|
||||
return "raspberrypi"
|
||||
if "proxmox" in s:
|
||||
return "proxmox"
|
||||
return "docker"
|
||||
|
||||
|
||||
def categorize_service(service_name: str) -> str:
|
||||
s = service_name.lower()
|
||||
if any(k in s for k in ["traefik", "authelia", "oauth", "auth", "proxy", "nginx", "caddy"]):
|
||||
return "edge/proxy/auth"
|
||||
if any(k in s for k in ["prometheus", "grafana", "loki", "promtail", "alert", "node-exporter", "cadvisor"]):
|
||||
return "monitoring"
|
||||
if any(k in s for k in ["watchtower", "diun", "ansible", "cron", "runner", "backup"]):
|
||||
return "automation"
|
||||
if any(k in s for k in ["postgres", "mariadb", "mysql", "redis", "minio", "nfs", "storage", "db", "queue"]):
|
||||
return "storage/database/support"
|
||||
return "apps"
|
||||
|
||||
|
||||
def load_inventory(path: Path | None) -> dict:
|
||||
if not path or not path.exists():
|
||||
return {}
|
||||
payload = json.loads(path.read_text())
|
||||
return payload.get("value", payload) if isinstance(payload, dict) else {}
|
||||
|
||||
|
||||
def to_records(data) -> list[dict]:
|
||||
if not isinstance(data, dict):
|
||||
return []
|
||||
out = []
|
||||
for key, value in data.items():
|
||||
if isinstance(value, dict):
|
||||
rec = dict(value)
|
||||
rec.setdefault("_key", str(key))
|
||||
rec.setdefault("name", rec.get("hostname") or rec.get("vm_name") or str(key))
|
||||
out.append(rec)
|
||||
return out
|
||||
|
||||
|
||||
def parent_name(item: dict) -> str:
|
||||
for k in PARENT_KEYS:
|
||||
v = item.get(k)
|
||||
if v:
|
||||
return str(v)
|
||||
return ""
|
||||
|
||||
|
||||
def generate_physical_topology(compose: dict, inventory: dict, out_dot: Path, out_svg: Path) -> None:
|
||||
physical = to_records(inventory.get("physical_hosts", {}))
|
||||
virtual = to_records(inventory.get("virtual_hosts", {})) + to_records(inventory.get("vms", {}))
|
||||
if not physical and not virtual:
|
||||
lines = [
|
||||
"digraph PhysicalTopology {",
|
||||
" graph [rankdir=LR, fontname=\"Helvetica\", nodesep=1.0, ranksep=1.5];",
|
||||
' "placeholder:inventory" [shape=note, style="filled", fillcolor="#fef3c7", label="Host inventory JSON not found.\\nGenerate terraform inventory and rerun scripts/docs/generate-all.sh\\n(--host-inventory <path>)."];',
|
||||
]
|
||||
lines.append("}")
|
||||
write_dot(out_dot, lines)
|
||||
render_svg(out_dot, out_svg)
|
||||
return
|
||||
|
||||
lines = [
|
||||
"digraph PhysicalTopology {",
|
||||
" graph [rankdir=LR, compound=true, splines=polyline, nodesep=0.95, ranksep=1.7, ratio=compress, fontname=\"Helvetica\", fontsize=13, concentrate=true, newrank=true];",
|
||||
" node [fontname=\"Helvetica\", fontsize=12, style=\"rounded,filled\", fillcolor=\"#ffffff\"];",
|
||||
" edge [fontname=\"Helvetica\", fontsize=10, color=\"#64748b\"];",
|
||||
]
|
||||
phys_names = {str(p.get("name")): p for p in physical}
|
||||
children: dict[str, list[dict]] = {k: [] for k in phys_names}
|
||||
orphans: list[dict] = []
|
||||
for vm in virtual:
|
||||
parent = parent_name(vm)
|
||||
if parent in children:
|
||||
children[parent].append(vm)
|
||||
else:
|
||||
orphans.append(vm)
|
||||
for host, record in sorted(phys_names.items()):
|
||||
host_role = str(record.get("role", "") or "")
|
||||
cluster_label = f"{host}\\n{host_role}" if host_role else host
|
||||
lines.extend([
|
||||
f' subgraph "cluster_{host}" {{',
|
||||
f' label="{cluster_label}";',
|
||||
' style="rounded,filled";',
|
||||
' color="#60a5fa";',
|
||||
' fillcolor="#eff6ff";',
|
||||
f' "phys:{host}" [label="{host}", shape=box3d, fillcolor="#bfdbfe"];',
|
||||
])
|
||||
for vm in sorted(children.get(host, []), key=lambda x: str(x.get("name", "")).lower()):
|
||||
vm_name = str(vm.get("name"))
|
||||
vm_role = str(vm.get("role", "") or "virtual host")
|
||||
cluster_id = f"cluster_{host}_{re.sub(r'[^a-zA-Z0-9]+', '_', vm_name)}"
|
||||
lines.extend([
|
||||
f' subgraph "{cluster_id}" {{',
|
||||
f' label="{vm_name}";',
|
||||
' style="rounded,dashed";',
|
||||
' color="#bfdbfe";',
|
||||
' fillcolor="#f8fbff";',
|
||||
f' "vm:{vm_name}" [label="{vm_name}\\n{vm_role}", shape=component, fillcolor="#dcfce7"];',
|
||||
])
|
||||
if "docker" in vm_role.lower() or "docker" in vm_name.lower():
|
||||
lines.append(f' "role:{vm_name}" [label="Docker host", shape=box, fillcolor="#fef3c7"];')
|
||||
lines.append(f' "vm:{vm_name}" -> "role:{vm_name}" [style=dashed, label="runs"];')
|
||||
lines.append(' }')
|
||||
lines.append(f' "phys:{host}" -> "vm:{vm_name}" [label="hosts"];')
|
||||
lines.append(' }')
|
||||
if orphans:
|
||||
lines.extend([
|
||||
' subgraph "cluster_orphans" {',
|
||||
' label="Unmapped virtual hosts"; style="rounded,dashed"; color="#d1d5db";',
|
||||
])
|
||||
for vm in sorted(orphans, key=lambda x: str(x.get("name", "")).lower()):
|
||||
vm_name = str(vm.get("name"))
|
||||
lines.append(f' "vm:{vm_name}" [label="{vm_name}", shape=component, fillcolor="#fee2e2"];')
|
||||
lines.append(" }")
|
||||
|
||||
lines.extend([
|
||||
' subgraph "cluster_legend" {',
|
||||
' label="Legend"; style="rounded"; color="#d1d5db";',
|
||||
' "leg_host" [label="Physical host", shape=box3d, fillcolor="#eff6ff"];',
|
||||
' "leg_vm" [label="Virtual machine", shape=component, fillcolor="#dcfce7"];',
|
||||
' "leg_role" [label="Hosted role", shape=box, fillcolor="#fef3c7"];',
|
||||
' "leg_host" -> "leg_vm" [label="hosts"];',
|
||||
' "leg_vm" -> "leg_role" [style=dashed, label="runs"];',
|
||||
' }',
|
||||
"}",
|
||||
])
|
||||
write_dot(out_dot, lines)
|
||||
render_svg(out_dot, out_svg)
|
||||
|
||||
|
||||
def generate_docker_traefik_dynu(compose: dict, dns_inventory: dict, domain_mode: str, out_dot: Path, out_svg: Path) -> None:
|
||||
services = compose.get("services") or {}
|
||||
networks = compose.get("networks") or {}
|
||||
lines = [
|
||||
"digraph DockerTraefikDynu {",
|
||||
" graph [rankdir=LR, compound=true, splines=polyline, nodesep=0.9, ranksep=1.6, fontname=\"Helvetica\", concentrate=true, newrank=true];",
|
||||
" node [fontname=\"Helvetica\", fontsize=11, style=\"rounded,filled\"];",
|
||||
" edge [fontname=\"Helvetica\", fontsize=9, color=\"#334155\"];",
|
||||
' "dynu" [label="Dynu / Public DNS", shape=box, fillcolor="#fde68a"];',
|
||||
' "svc:traefik" [label="Traefik", shape=box, fillcolor="#bfdbfe"];',
|
||||
' "dynu" -> "svc:traefik" [penwidth=1.6];',
|
||||
]
|
||||
|
||||
routes: dict[str, dict] = {}
|
||||
dns_nodes: set[str] = set()
|
||||
|
||||
for svc_name, svc in sorted(services.items()):
|
||||
labels = parse_labels(svc)
|
||||
router_prefix = "traefik.http.routers."
|
||||
service_prefix = "traefik.http.services."
|
||||
lb_ports = {}
|
||||
for k, v in labels.items():
|
||||
if k.startswith(service_prefix) and k.endswith(".loadbalancer.server.port"):
|
||||
lb_ports[k[len(service_prefix):].split(".", 1)[0]] = v
|
||||
|
||||
routers = sorted({k[len(router_prefix):].split(".", 1)[0] for k in labels if k.startswith(router_prefix)})
|
||||
for router in routers:
|
||||
rule = labels.get(f"{router_prefix}{router}.rule", "")
|
||||
target = labels.get(f"{router_prefix}{router}.service", svc_name)
|
||||
middlewares = labels.get(f"{router_prefix}{router}.middlewares", "")
|
||||
tls = labels.get(f"{router_prefix}{router}.tls", "false")
|
||||
port = lb_ports.get(target, "")
|
||||
badges = []
|
||||
if str(tls).lower() in ("true", "1"):
|
||||
badges.append("TLS")
|
||||
mw_low = middlewares.lower()
|
||||
if "authelia" in mw_low:
|
||||
badges.append("authelia")
|
||||
if "mtls" in mw_low:
|
||||
badges.append("mTLS")
|
||||
hosts = [display_domain(h, domain_mode) for h in extract_hosts(rule)]
|
||||
if not hosts:
|
||||
continue
|
||||
info = routes.setdefault(svc_name, {"hosts": set(), "port": port, "badges": set()})
|
||||
info["hosts"].update(hosts)
|
||||
if port:
|
||||
info["port"] = port
|
||||
info["badges"].update(badges)
|
||||
|
||||
for svc_name, info in sorted(routes.items()):
|
||||
label = svc_name
|
||||
if info.get("port"):
|
||||
label += f"\n:{info['port']}"
|
||||
if info.get("badges"):
|
||||
label += "\n[" + ", ".join(sorted(info["badges"])) + "]"
|
||||
lines.append(f' "svc:{svc_name}" [label="{label}", shape=box, fillcolor="#dcfce7"];')
|
||||
lines.append(f' "svc:traefik" -> "svc:{svc_name}" [penwidth=1.4];')
|
||||
for host in sorted(info["hosts"]):
|
||||
dns_nodes.add(host)
|
||||
lines.append(f' "dns:{host}" [label="{host}", shape=note, fillcolor="#fef3c7"];')
|
||||
lines.append(f' "dns:{host}" -> "dynu";')
|
||||
for record in (dns_inventory.get("records", []) if isinstance(dns_inventory, dict) else []):
|
||||
host = record.get("hostname") or record.get("name")
|
||||
if not host:
|
||||
continue
|
||||
host_disp = display_domain(str(host), domain_mode)
|
||||
if host_disp in dns_nodes:
|
||||
continue
|
||||
dns_nodes.add(host_disp)
|
||||
lines.append(f' "dns:{host_disp}" [label="{host_disp}", shape=note, fillcolor="#fef3c7"];')
|
||||
lines.append(f' "dns:{host_disp}" -> "dynu" [style=dashed, color=\"#94a3b8\"];')
|
||||
|
||||
lines.append(' { rank=same; ' + '; '.join([f'"dns:{d}"' for d in sorted(dns_nodes)]) + '; }' if dns_nodes else '')
|
||||
|
||||
lines.append(' subgraph "cluster_networks" {')
|
||||
lines.append(' label="Docker backend networks"; style="rounded,dashed"; color="#d1d5db";')
|
||||
for net in sorted(networks.keys()):
|
||||
lines.append(f' "net:{net}" [label="{net}", shape=ellipse, fillcolor="#f8fafc"];')
|
||||
lines.append(' }')
|
||||
|
||||
for svc_name in sorted(routes.keys()):
|
||||
svc = services.get(svc_name, {})
|
||||
svc_nets = svc.get("networks") or []
|
||||
if isinstance(svc_nets, dict):
|
||||
svc_nets = svc_nets.keys()
|
||||
for net in svc_nets:
|
||||
lines.append(f' "svc:{svc_name}" -> "net:{net}" [style=dashed, color="#94a3b8", arrowsize=0.7];')
|
||||
|
||||
lines.append("}")
|
||||
write_dot(out_dot, lines)
|
||||
render_svg(out_dot, out_svg)
|
||||
|
||||
|
||||
|
||||
def generate_compose_topology(compose: dict, out_dot: Path, out_svg: Path) -> None:
|
||||
services = compose.get("services") or {}
|
||||
networks = compose.get("networks") or {}
|
||||
lines = [
|
||||
"digraph Compose {",
|
||||
" rankdir=LR;",
|
||||
' node [fontname="Helvetica"];',
|
||||
]
|
||||
for service in sorted(services):
|
||||
lines.append(f' "svc:{service}" [label="{service}", shape=box, style=filled, fillcolor="#dfefff"];')
|
||||
for net in sorted(networks):
|
||||
lines.append(f' "net:{net}" [label="{net}", shape=ellipse, style=filled, fillcolor="#f4f4f4"];')
|
||||
|
||||
for service, svc in sorted(services.items()):
|
||||
svc_nets = svc.get("networks") or []
|
||||
if isinstance(svc_nets, dict):
|
||||
svc_nets = svc_nets.keys()
|
||||
for net in svc_nets:
|
||||
lines.append(f' "svc:{service}" -> "net:{net}";')
|
||||
|
||||
lines.append("}")
|
||||
write_dot(out_dot, lines)
|
||||
render_svg(out_dot, out_svg)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("legacy", nargs="*")
|
||||
parser.add_argument("--compose")
|
||||
parser.add_argument("--out-dir")
|
||||
parser.add_argument("--host-inventory")
|
||||
parser.add_argument("--dns-inventory")
|
||||
parser.add_argument("--domain-display", choices=["full", "redacted-label", "placeholder"], default="redacted-label")
|
||||
args = parser.parse_args()
|
||||
|
||||
require_dot()
|
||||
|
||||
if args.compose and args.out_dir:
|
||||
compose_path = Path(args.compose)
|
||||
out_dir = Path(args.out_dir)
|
||||
elif len(args.legacy) == 3:
|
||||
compose_path = Path(args.legacy[0])
|
||||
out_dir = Path(args.legacy[1]).parent
|
||||
else:
|
||||
raise SystemExit("Usage: generate-diagrams.py --compose <compose.yml> --out-dir <dir>")
|
||||
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
compose = load_compose(compose_path)
|
||||
host_inventory = load_inventory(Path(args.host_inventory)) if args.host_inventory else {}
|
||||
dns_inventory = load_inventory(Path(args.dns_inventory)) if args.dns_inventory else {}
|
||||
|
||||
generate_docker_traefik_dynu(compose, dns_inventory, args.domain_display, out_dir / "docker-traefik-dynu.dot", out_dir / "docker-traefik-dynu.svg")
|
||||
generate_physical_topology(compose, host_inventory, out_dir / "physical-topology.dot", out_dir / "physical-topology.svg")
|
||||
generate_compose_topology(compose, out_dir / "docker-compose.dot", out_dir / "docker-compose.svg")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
out = Path(sys.argv[1])
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(
|
||||
"""# Generated Documentation
|
||||
|
||||
This directory contains documentation generated automatically from repository configuration.
|
||||
|
||||
## Files
|
||||
|
||||
- [Compose file list](compose-files.txt)
|
||||
- [Resolved Docker Compose config](docker-compose.resolved.yml)
|
||||
- [Compose inventory](compose-inventory.md)
|
||||
- [Traefik routes](traefik-routes.md)
|
||||
- [Docker Compose diagram](../diagrams/docker-compose.svg)
|
||||
"""
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys,yaml,glob
|
||||
out=sys.argv[1]
|
||||
patterns=["monitoring/prometheus/rules/**/*.yml","monitoring/prometheus/rules/**/*.yaml","**/prometheus/rules/**/*.yml","**/prometheus/rules/**/*.yaml"]
|
||||
files=sorted({f for p in patterns for f in glob.glob(p,recursive=True)})
|
||||
lines=["# Prometheus Rules","", "| File | Group | Alert | Expr | For | Labels | Annotations |","|---|---|---|---|---|---|---|"]
|
||||
if not files:
|
||||
open(out,'w').write("# Prometheus Rules\n\nNo Prometheus rule files were found.\n"); sys.exit(0)
|
||||
for fp in files:
|
||||
try:data=yaml.safe_load(open(fp)) or {}
|
||||
except Exception as e: raise SystemExit(f"Malformed YAML in {fp}: {e}")
|
||||
for g in data.get('groups',[]) or []:
|
||||
for r in g.get('rules',[]) or []:
|
||||
lines.append(f"| {fp} | {g.get('name','')} | {r.get('alert','')} | {str(r.get('expr','')).replace('|','\\|')} | {r.get('for','')} | {r.get('labels',{})} | {r.get('annotations',{})} |")
|
||||
open(out,'w').write('\n'.join(lines)+'\n')
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys,yaml,re
|
||||
inp,out=sys.argv[1],sys.argv[2]
|
||||
with open(inp) as f: c=yaml.safe_load(f) or {}
|
||||
rows=[]
|
||||
for sname,svc in (c.get('services') or {}).items():
|
||||
labels=svc.get('labels') or {}
|
||||
if isinstance(labels,list):
|
||||
d={}
|
||||
for l in labels:
|
||||
if '=' in str(l):k,v=str(l).split('=',1);d[k]=v
|
||||
labels=d
|
||||
routers={}
|
||||
for k,v in labels.items():
|
||||
m=re.match(r'traefik\.http\.routers\.([^.]+)\.(rule|entrypoints|tls|middlewares)$',k)
|
||||
if m: routers.setdefault(m.group(1),{})[m.group(2)]=v
|
||||
ports={}
|
||||
for k,v in labels.items():
|
||||
m=re.match(r'traefik\.http\.services\.([^.]+)\.loadbalancer\.server\.port$',k)
|
||||
if m: ports[m.group(1)]=v
|
||||
for r,rv in routers.items():
|
||||
rows.append((sname,r,rv.get('rule',''),rv.get('entrypoints',''),rv.get('tls',''),rv.get('middlewares',''),ports.get(r,'')))
|
||||
lines=["# Traefik Routes","", "| Service | Router | Rule | Entrypoints | TLS | Middlewares | Target Port |","|---|---|---|---|---|---|---|"]
|
||||
if not rows:
|
||||
lines=["# Traefik Routes","","No Traefik routes were detected."]
|
||||
else:
|
||||
for r in sorted(rows): lines.append('| '+' | '.join(str(x).replace('|','\\|') for x in r)+' |')
|
||||
open(out,'w').write('\n'.join(lines)+'\n')
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
dynu_dir="${repo_root}/infrastructure/terraform/dynu"
|
||||
out_json="${dynu_dir}/generated/dynu_dns_records_inventory.json"
|
||||
|
||||
if [[ ! -d "${dynu_dir}" ]]; then
|
||||
echo "ERROR: Dynu Terraform directory not found at ${dynu_dir}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v terraform >/dev/null 2>&1; then
|
||||
echo "ERROR: terraform is required to refresh Dynu DNS inventory." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "${out_json}")"
|
||||
|
||||
(
|
||||
cd "${dynu_dir}"
|
||||
if terraform output -json dynu_dns_inventory > "${out_json}"; then
|
||||
:
|
||||
elif terraform output -json dynu_dns_records_catalog > "${out_json}"; then
|
||||
:
|
||||
else
|
||||
echo "ERROR: Failed to export Dynu DNS inventory from Terraform outputs. Tried: dynu_dns_inventory, dynu_dns_records_catalog." >&2
|
||||
echo "Run 'cd infrastructure/terraform/dynu && terraform output' to inspect available outputs." >&2
|
||||
exit 1
|
||||
fi
|
||||
)
|
||||
|
||||
if [[ ! -s "${out_json}" ]]; then
|
||||
echo "ERROR: Dynu DNS inventory output file is empty: ${out_json}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generated: ${out_json}"
|
||||
Executable
+189
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate host topology Markdown/Mermaid from Terraform inventory JSON."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
PARENT_KEYS = [
|
||||
"node",
|
||||
"node_name",
|
||||
"host",
|
||||
"physical_host",
|
||||
"hypervisor_host",
|
||||
"proxmox_node",
|
||||
]
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate Markdown topology from terraform output JSON"
|
||||
)
|
||||
parser.add_argument("--input", required=True, help="Input JSON path")
|
||||
parser.add_argument("--output", required=True, help="Output Markdown path")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def load_inventory(raw: dict[str, Any]) -> dict[str, Any]:
|
||||
payload = raw.get("value", raw)
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError("Inventory payload must be an object")
|
||||
return payload
|
||||
|
||||
|
||||
def infer_name(key: str, item: dict[str, Any]) -> str:
|
||||
for candidate in ("name", "hostname", "vm_name", "id"):
|
||||
value = item.get(candidate)
|
||||
if value:
|
||||
return str(value)
|
||||
return key
|
||||
|
||||
|
||||
def to_records(host_map: Any) -> list[dict[str, Any]]:
|
||||
if isinstance(host_map, dict):
|
||||
records: list[dict[str, Any]] = []
|
||||
for key, val in host_map.items():
|
||||
if isinstance(val, dict):
|
||||
rec = dict(val)
|
||||
rec.setdefault("_key", str(key))
|
||||
rec.setdefault("name", infer_name(str(key), rec))
|
||||
records.append(rec)
|
||||
return sorted(records, key=lambda x: str(x.get("name", "")).lower())
|
||||
return []
|
||||
|
||||
|
||||
def escape_cell(value: Any) -> str:
|
||||
if value is None:
|
||||
return ""
|
||||
text = str(value).replace("|", "\\|").replace("\n", "<br>")
|
||||
return text
|
||||
|
||||
|
||||
def get_first(item: dict[str, Any], *keys: str) -> str:
|
||||
for key in keys:
|
||||
value = item.get(key)
|
||||
if value is not None and value != "":
|
||||
return str(value)
|
||||
return ""
|
||||
|
||||
|
||||
def detect_virtual_hosts(inv: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
virtual: list[dict[str, Any]] = []
|
||||
for key in ("virtual_hosts", "vms"):
|
||||
virtual.extend(to_records(inv.get(key, {})))
|
||||
return sorted(virtual, key=lambda x: str(x.get("name", "")).lower())
|
||||
|
||||
|
||||
def build_mermaid(physical: list[dict[str, Any]], virtual: list[dict[str, Any]]) -> list[str]:
|
||||
lines = ["```mermaid", "flowchart TD"]
|
||||
for p in physical:
|
||||
pname = get_first(p, "name", "hostname", "_key")
|
||||
pid = f"phys_{pname.lower().replace('-', '_').replace(' ', '_')}"
|
||||
lines.append(f' {pid}["{pname}\\nphysical"]')
|
||||
|
||||
for v in virtual:
|
||||
vname = get_first(v, "name", "hostname", "_key")
|
||||
vid = f"virt_{vname.lower().replace('-', '_').replace(' ', '_')}"
|
||||
parent = ""
|
||||
for k in PARENT_KEYS:
|
||||
parent = get_first(v, k)
|
||||
if parent:
|
||||
break
|
||||
|
||||
lines.append(f' {vid}["{vname}\\nvirtual"]')
|
||||
if parent:
|
||||
pid = f"phys_{parent.lower().replace('-', '_').replace(' ', '_')}"
|
||||
lines.append(f" {pid} --> {vid}")
|
||||
|
||||
lines.append("```")
|
||||
return lines
|
||||
|
||||
|
||||
def build_table(headers: list[str], rows: list[list[str]]) -> list[str]:
|
||||
out = ["| " + " | ".join(headers) + " |", "| " + " | ".join(["---"] * len(headers)) + " |"]
|
||||
for row in rows:
|
||||
out.append("| " + " | ".join(escape_cell(cell) for cell in row) + " |")
|
||||
return out
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
raw = json.loads(Path(args.input).read_text(encoding="utf-8"))
|
||||
inv = load_inventory(raw)
|
||||
|
||||
physical = to_records(inv.get("physical_hosts", {}))
|
||||
virtual = detect_virtual_hosts(inv)
|
||||
|
||||
now = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
||||
lines: list[str] = [
|
||||
"# Host Topology",
|
||||
"",
|
||||
f"> Generated by `scripts/docs/generate_host_topology.py` on {now}.",
|
||||
"",
|
||||
"## Topology Diagram",
|
||||
"",
|
||||
]
|
||||
lines.extend(build_mermaid(physical, virtual))
|
||||
lines.extend(["", "## Physical Hosts", ""])
|
||||
|
||||
physical_rows = [
|
||||
[
|
||||
get_first(p, "name", "hostname", "_key"),
|
||||
get_first(p, "type"),
|
||||
get_first(p, "role"),
|
||||
get_first(p, "management", "management_ip"),
|
||||
get_first(p, "os", "os_family"),
|
||||
get_first(p, "hypervisor"),
|
||||
get_first(p, "location"),
|
||||
get_first(p, "notes"),
|
||||
]
|
||||
for p in physical
|
||||
]
|
||||
lines.extend(
|
||||
build_table(
|
||||
["Name", "Type", "Role", "Management", "OS", "Hypervisor", "Location", "Notes"],
|
||||
physical_rows,
|
||||
)
|
||||
)
|
||||
|
||||
lines.extend(["", "## Virtual Hosts", ""])
|
||||
if virtual:
|
||||
virtual_rows = []
|
||||
for v in virtual:
|
||||
parent = ""
|
||||
for k in PARENT_KEYS:
|
||||
parent = get_first(v, k)
|
||||
if parent:
|
||||
break
|
||||
virtual_rows.append(
|
||||
[
|
||||
get_first(v, "name", "hostname", "_key"),
|
||||
get_first(v, "type"),
|
||||
get_first(v, "role"),
|
||||
parent,
|
||||
get_first(v, "management", "management_ip", "ip"),
|
||||
get_first(v, "os", "os_family"),
|
||||
get_first(v, "notes"),
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
build_table(
|
||||
["Name", "Type", "Role", "Parent/Node", "Management", "OS", "Notes"],
|
||||
virtual_rows,
|
||||
)
|
||||
)
|
||||
else:
|
||||
lines.append("No VM/virtual host data found in the current Terraform inventory output.")
|
||||
|
||||
out_path = Path(args.output)
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
out_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Discover compose files for docs tooling and CI without running containers.
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
declare -a files=()
|
||||
|
||||
if [ -f services-up.sh ]; then
|
||||
# Parse literal FILES array entries in services-up.sh (e.g. default-network.yml).
|
||||
while IFS= read -r line; do
|
||||
path=$(sed -E 's#.*\$PROJECT_ROOT/([^" ]+).*#\1#' <<<"$line")
|
||||
[ -f "$path" ] && files+=("$path")
|
||||
done < <(awk '/^FILES=\(/,/^\)/ {print}' services-up.sh | grep -E '\-f[[:space:]]+"\$PROJECT_ROOT/')
|
||||
|
||||
# Reuse the same compose roots used by services-up.sh to avoid archived compose files.
|
||||
if grep -q 'find "\$PROJECT_ROOT/apps" "\$PROJECT_ROOT/monitoring" "\$PROJECT_ROOT/core"' services-up.sh; then
|
||||
while IFS= read -r f; do files+=("$f"); done < <(
|
||||
find apps monitoring core -maxdepth 2 -type f \
|
||||
\( -name 'docker-compose.yml' -o -name 'docker-compose.yaml' -o -name 'compose.yml' -o -name 'compose.yaml' \) \
|
||||
| sed 's#^\./##' | sort
|
||||
)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
while IFS= read -r f; do files+=("$f"); done < <(
|
||||
find . -type f \
|
||||
\( -name 'docker-compose.yml' -o -name 'docker-compose.yaml' -o -name 'compose.yml' -o -name 'compose.yaml' \) \
|
||||
| sed 's#^\./##' | grep -v '^archive/' | sort
|
||||
)
|
||||
fi
|
||||
|
||||
printf '%s\n' "${files[@]}" | awk 'NF' | awk '!seen[$0]++'
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT"
|
||||
mkdir -p docs/generated
|
||||
mapfile -t COMPOSE_FILES < <(scripts/docs/list-compose-files.sh)
|
||||
if [ "${#COMPOSE_FILES[@]}" -eq 0 ]; then
|
||||
echo "No compose files found" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "${COMPOSE_FILES[@]}" > docs/generated/compose-files.txt
|
||||
|
||||
if [ ! -x ./services-up.sh ]; then
|
||||
echo "services-up.sh is missing or not executable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./services-up.sh --profile all config > docs/generated/docker-compose.resolved.yml
|
||||
|
||||
service_count="$(
|
||||
python3 - <<'PY'
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
data = yaml.safe_load(Path("docs/generated/docker-compose.resolved.yml").read_text()) or {}
|
||||
print(len(data.get("services") or {}))
|
||||
PY
|
||||
)"
|
||||
|
||||
if [ "$service_count" -eq 0 ]; then
|
||||
echo "ERROR: rendered compose config contains zero services; check --profile all / COMPOSE_PROFILES." >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
src_generated = Path(sys.argv[1])
|
||||
src_diagrams = Path(sys.argv[2])
|
||||
out_dir = Path(sys.argv[3])
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def sanitize_text(content: str) -> str:
|
||||
content = re.sub(r'\b([a-zA-Z0-9-]+)\.lan\.ddnsgeek\.com\b', r'\1.<domain>', content)
|
||||
content = re.sub(
|
||||
r'\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b',
|
||||
'<private-ip>',
|
||||
content,
|
||||
)
|
||||
content = re.sub(r'(?i)\b(password|token|api[_-]?key|secret)\s*[:=]\s*[^\s\n]+', r'\1=<redacted>', content)
|
||||
content = re.sub(r'(?m)^([A-Z0-9_]*(?:PASSWORD|TOKEN|API_KEY|SECRET)[A-Z0-9_]*)\s*[:=]\s*.*$', r'\1=<redacted>', content)
|
||||
return content
|
||||
|
||||
|
||||
for name in ['compose-inventory.md', 'traefik-routes.md']:
|
||||
src = src_generated / name
|
||||
if src.exists():
|
||||
(out_dir / name).write_text(sanitize_text(src.read_text(errors='ignore')))
|
||||
|
||||
for svg_name in ['docker-compose.svg', 'physical-topology.svg', 'docker-traefik-dynu.svg']:
|
||||
src = src_diagrams / svg_name
|
||||
if src.exists():
|
||||
(out_dir / svg_name).write_text(sanitize_text(src.read_text(errors='ignore')))
|
||||
|
||||
(out_dir / 'index.md').write_text(
|
||||
"""# Public Infrastructure Summary
|
||||
|
||||
This documentation is generated from the infrastructure repository. Sensitive values are redacted.
|
||||
|
||||
> Generated docs are sanitised/redacted before publishing to GitHub Pages.
|
||||
|
||||
## Infrastructure diagrams
|
||||
|
||||
### Physical / virtual topology
|
||||
|
||||

|
||||
|
||||
### Docker, Traefik and Dynu routing
|
||||
|
||||

|
||||
|
||||
## Documents
|
||||
|
||||
- [Diagrams](diagrams.md)
|
||||
- [Compose Inventory](compose-inventory.md)
|
||||
- [Traefik Routes](traefik-routes.md)
|
||||
"""
|
||||
)
|
||||
|
||||
(out_dir / 'diagrams.md').write_text(
|
||||
"""# Infrastructure diagrams
|
||||
|
||||
## Physical / virtual topology
|
||||
|
||||
This view groups containers by inferred host and service role (edge/proxy/auth, monitoring, automation, apps, and supporting storage/services).
|
||||
|
||||
<div class="diagram-wrap">
|
||||
<img src="physical-topology.svg" alt="Physical topology">
|
||||
</div>
|
||||
|
||||
## Docker, Traefik and Dynu routing
|
||||
|
||||
This view shows sanitised public DNS names flowing to Traefik, then to exposed Docker services, with backend Docker network membership shown as secondary context.
|
||||
|
||||
_Diagrams are generated from Compose data and Traefik labels._
|
||||
|
||||
<div class="diagram-wrap">
|
||||
<img src="docker-traefik-dynu.svg" alt="Docker Traefik Dynu">
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
mkdir -p docs/generated docs/diagrams docs/public
|
||||
|
||||
scripts/docs/generate-all.sh
|
||||
|
||||
if [ ! -d docs/public ] || [ -z "$(find docs/public -mindepth 1 -print -quit)" ]; then
|
||||
echo "ERROR: docs/public is missing or empty after generation." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Public docs generated in docs/public. Review changes before commit."
|
||||
@@ -26,6 +26,12 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--diagrams-dir", default="docs/diagrams", help="Diagram output directory.")
|
||||
parser.add_argument("--readme-file", default="README.md", help="README path for regeneration notes.")
|
||||
parser.add_argument("--architecture-file", default="docs/architecture.md", help="Architecture markdown path.")
|
||||
parser.add_argument(
|
||||
"--dynu-dns-inventory-file",
|
||||
default="infrastructure/terraform/dynu/generated/dynu_dns_records_inventory.json",
|
||||
help="Path to Dynu DNS brownfield inventory JSON.",
|
||||
)
|
||||
parser.add_argument("--skip-dynu-dns", action="store_true", help="Skip Dynu DNS inventory loading/rendering.")
|
||||
parser.add_argument("--network-file", default="docs/network.md", help="Network markdown path.")
|
||||
parser.add_argument("--coverage-file", default="docs/monitoring-coverage.md", help="Coverage markdown path.")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print changes instead of writing files.")
|
||||
@@ -41,6 +47,109 @@ def load_json(path: Path) -> dict[str, Any]:
|
||||
return data
|
||||
|
||||
|
||||
def load_optional_json(path: Path) -> Any | None:
|
||||
if not path.exists():
|
||||
return None
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
return json.load(handle)
|
||||
|
||||
|
||||
def normalize_dynu_dns_records(payload: Any) -> list[dict[str, Any]]:
|
||||
records_payload = payload
|
||||
if isinstance(payload, dict):
|
||||
if isinstance(payload.get("records"), list):
|
||||
records_payload = payload["records"]
|
||||
elif isinstance(payload.get("value"), list):
|
||||
records_payload = payload["value"]
|
||||
if not isinstance(records_payload, list):
|
||||
raise ValueError("Dynu DNS inventory must be a list or object with list field `records`/`value`.")
|
||||
|
||||
normalized: list[dict[str, Any]] = []
|
||||
for record in records_payload:
|
||||
if not isinstance(record, dict):
|
||||
continue
|
||||
normalized.append(
|
||||
{
|
||||
"id": record.get("id"),
|
||||
"domain_id": record.get("domain_id"),
|
||||
"domain_name": str(record.get("domain_name") or "unknown"),
|
||||
"hostname": str(record.get("hostname") or "unknown"),
|
||||
"node_name": str(record.get("node_name") or "unknown"),
|
||||
"record_type": str(record.get("record_type") or "unknown"),
|
||||
"content": record.get("content"),
|
||||
"state": record.get("state") if "state" in record else record.get("enabled"),
|
||||
"ttl": record.get("ttl"),
|
||||
"updated_on": record.get("updated_on"),
|
||||
}
|
||||
)
|
||||
normalized.sort(
|
||||
key=lambda r: (
|
||||
str(r.get("domain_name") or ""),
|
||||
str(r.get("hostname") or ""),
|
||||
str(r.get("record_type") or ""),
|
||||
str(r.get("id") or ""),
|
||||
)
|
||||
)
|
||||
return normalized
|
||||
|
||||
|
||||
def render_dynu_dns_architecture_section(records: list[dict[str, Any]], inventory_path: Path | None) -> str:
|
||||
domains = sorted({r["domain_name"] for r in records if r["domain_name"] != "unknown"})
|
||||
dynamic_count = 0
|
||||
static_count = 0
|
||||
disabled_count = 0
|
||||
rows: list[list[str]] = []
|
||||
for record in records:
|
||||
rtype = str(record.get("record_type") or "").upper()
|
||||
raw_content = record.get("content")
|
||||
content = "" if raw_content is None else str(raw_content).strip()
|
||||
is_dynamic = rtype in {"A", "AAAA"} and not content
|
||||
mode = "dynamic" if is_dynamic else ("static" if content else ("record" if rtype not in {"A", "AAAA"} else "static"))
|
||||
target = "dynamic" if is_dynamic else (content or ("unknown" if not content else content))
|
||||
if is_dynamic:
|
||||
dynamic_count += 1
|
||||
else:
|
||||
static_count += 1
|
||||
|
||||
enabled_value = record.get("state")
|
||||
enabled_text = "unknown" if enabled_value is None else str(bool(enabled_value)).lower()
|
||||
if enabled_value is False:
|
||||
disabled_count += 1
|
||||
|
||||
rows.append(
|
||||
[
|
||||
str(record.get("hostname") or "unknown"),
|
||||
rtype or "unknown",
|
||||
target,
|
||||
mode,
|
||||
str(record.get("ttl") if record.get("ttl") is not None else "unknown"),
|
||||
enabled_text,
|
||||
str(record.get("id") if record.get("id") is not None else "unknown"),
|
||||
str(record.get("updated_on") or "unknown"),
|
||||
]
|
||||
)
|
||||
|
||||
domain_text = ", ".join(domains) if domains else "unknown"
|
||||
lines = [
|
||||
"### Dynu DNS brownfield inventory",
|
||||
"",
|
||||
"Dynu DNS is managed as a brownfield reconciliation source. Terraform imports the root domain and individual DNS records into state, while generated configuration provides reviewable management intent.",
|
||||
"",
|
||||
f"- Inventory source: `{inventory_path}`" if inventory_path else "- Inventory source: `unknown`",
|
||||
f"- Records observed: `{len(records)}`",
|
||||
f"- Domains observed: `{domain_text}`",
|
||||
f"- Dynamic A/AAAA records: `{dynamic_count}`",
|
||||
f"- Static records: `{static_count}`",
|
||||
f"- Disabled records: `{disabled_count}`",
|
||||
"",
|
||||
"#### DNS records",
|
||||
"",
|
||||
markdown_table(["hostname", "type", "target/content", "mode", "ttl", "enabled", "record id", "updated"], rows or [["none", "", "", "", "", "", "", ""]]),
|
||||
"",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def merged_labels(target: dict[str, Any]) -> dict[str, str]:
|
||||
discovered = target.get("discovered_labels") or {}
|
||||
labels = target.get("labels") or {}
|
||||
@@ -244,12 +353,18 @@ def render_network_doc(inventory: dict[str, Any], targets: list[dict[str, Any]])
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def render_architecture_section(inventory: dict[str, Any], targets: list[dict[str, Any]]) -> str:
|
||||
def render_architecture_section(
|
||||
inventory: dict[str, Any],
|
||||
targets: list[dict[str, Any]],
|
||||
dynu_records: list[dict[str, Any]] | None = None,
|
||||
dynu_inventory_path: Path | None = None,
|
||||
dynu_inventory_missing: bool = False,
|
||||
) -> str:
|
||||
summaries = summarize_targets(targets, normalize_targets({"targets": inventory.get("unhealthy_targets") or []}))
|
||||
notes = inventory.get("notes") or []
|
||||
|
||||
lines = [
|
||||
"## Runtime visibility from Prometheus",
|
||||
"## Runtime and infrastructure inventory",
|
||||
"",
|
||||
GENERATED_BEGIN,
|
||||
"",
|
||||
@@ -267,12 +382,42 @@ def render_architecture_section(inventory: dict[str, Any], targets: list[dict[st
|
||||
[[job, str(data["active"]), str(data["unhealthy"])] for job, data in summaries["by_job"].items()] or [["none", "0", "0"]],
|
||||
),
|
||||
"",
|
||||
]
|
||||
if dynu_records is not None:
|
||||
lines.extend(["", render_dynu_dns_architecture_section(dynu_records, dynu_inventory_path).rstrip(), ""])
|
||||
elif dynu_inventory_missing:
|
||||
lines.extend(
|
||||
[
|
||||
"### Dynu DNS brownfield inventory",
|
||||
"",
|
||||
f"Dynu DNS inventory was not found at `{dynu_inventory_path}`.",
|
||||
"",
|
||||
"Generate it with:",
|
||||
"",
|
||||
"```bash",
|
||||
"cd infrastructure/terraform/dynu",
|
||||
"python3 scripts/generate-brownfield-records.py --overwrite",
|
||||
"```",
|
||||
"",
|
||||
]
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
"### Data sources",
|
||||
"",
|
||||
"- `docs/runtime/prometheus-inventory.json` (normalized runtime export)",
|
||||
]
|
||||
)
|
||||
if dynu_records is not None:
|
||||
lines.append(f"- `{dynu_inventory_path}` (Dynu DNS brownfield inventory)")
|
||||
elif dynu_inventory_missing:
|
||||
lines.append("- Dynu DNS inventory not available; run the Dynu brownfield generator.")
|
||||
lines.extend(
|
||||
[
|
||||
"- Prometheus scrape metadata (`targets` + label sets)",
|
||||
"- Existing repository architecture docs for declared topology",
|
||||
]
|
||||
)
|
||||
if notes:
|
||||
lines.extend(["", "### Notes from inventory", ""])
|
||||
for note in notes:
|
||||
@@ -284,14 +429,26 @@ def render_architecture_section(inventory: dict[str, Any], targets: list[dict[st
|
||||
def upsert_generated_section(path: Path, section_markdown: str, dry_run: bool, verbose: bool) -> None:
|
||||
existing = path.read_text(encoding="utf-8") if path.exists() else ""
|
||||
section_body = section_markdown
|
||||
legacy_heading = "## Runtime visibility from Prometheus"
|
||||
new_heading = "## Runtime and infrastructure inventory"
|
||||
|
||||
if GENERATED_BEGIN in existing and GENERATED_END in existing:
|
||||
# Migration: replace legacy heading that exists outside the generated markers.
|
||||
existing = re.sub(
|
||||
rf"^{re.escape(legacy_heading)}(?=\n)",
|
||||
new_heading,
|
||||
existing,
|
||||
count=1,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
pattern = re.compile(
|
||||
rf"{re.escape(GENERATED_BEGIN)}.*?{re.escape(GENERATED_END)}",
|
||||
re.DOTALL,
|
||||
)
|
||||
replacement = "\n".join(
|
||||
line for line in section_body.splitlines() if line.strip() not in {"## Runtime visibility from Prometheus"}
|
||||
line
|
||||
for line in section_body.splitlines()
|
||||
if line.strip() not in {legacy_heading, new_heading}
|
||||
)
|
||||
updated = pattern.sub(replacement.strip(), existing)
|
||||
else:
|
||||
@@ -384,6 +541,7 @@ def main() -> int:
|
||||
inventory_path = Path(args.inventory_file)
|
||||
docs_dir = Path(args.docs_dir)
|
||||
diagrams_dir = Path(args.diagrams_dir)
|
||||
dynu_inventory_path = Path(args.dynu_dns_inventory_file)
|
||||
|
||||
inventory = load_json(inventory_path)
|
||||
targets = normalize_targets(inventory)
|
||||
@@ -394,7 +552,25 @@ def main() -> int:
|
||||
|
||||
coverage_md = render_monitoring_coverage(inventory, targets)
|
||||
network_md = render_network_doc(inventory, targets)
|
||||
architecture_section = render_architecture_section(inventory, targets)
|
||||
dynu_records: list[dict[str, Any]] | None = None
|
||||
dynu_inventory_missing = False
|
||||
if not args.skip_dynu_dns:
|
||||
try:
|
||||
dynu_payload = load_optional_json(dynu_inventory_path)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError(f"Invalid JSON in Dynu DNS inventory file {dynu_inventory_path}: {exc}") from exc
|
||||
if dynu_payload is None:
|
||||
dynu_inventory_missing = True
|
||||
else:
|
||||
dynu_records = normalize_dynu_dns_records(dynu_payload)
|
||||
|
||||
architecture_section = render_architecture_section(
|
||||
inventory,
|
||||
targets,
|
||||
dynu_records=dynu_records,
|
||||
dynu_inventory_path=dynu_inventory_path,
|
||||
dynu_inventory_missing=dynu_inventory_missing,
|
||||
)
|
||||
monitoring_mmd = render_monitoring_mermaid(targets)
|
||||
architecture_mmd = render_architecture_mermaid(targets)
|
||||
|
||||
|
||||
+13
-13
@@ -1,13 +1,13 @@
|
||||
10:17:35 INFO: === Update started: 2026-04-21 10:17:35 ===
|
||||
10:17:35 WARNING: Skipping traefik (directory does not exist)
|
||||
10:17:35 WARNING: Skipping nextcloud (directory does not exist)
|
||||
10:17:35 WARNING: Skipping passbolt (directory does not exist)
|
||||
10:17:35 WARNING: Skipping searxng (directory does not exist)
|
||||
10:17:35 WARNING: Skipping gitea (directory does not exist)
|
||||
10:17:35 WARNING: Skipping gotify (directory does not exist)
|
||||
10:17:35 WARNING: Skipping grafana (directory does not exist)
|
||||
10:17:35 WARNING: Skipping gramps (directory does not exist)
|
||||
10:17:35 WARNING: Skipping portainer (directory does not exist)
|
||||
10:17:35 WARNING: Skipping prometheus (directory does not exist)
|
||||
10:17:35 WARNING: Skipping uptime-kuma (directory does not exist)
|
||||
10:17:35 INFO: Pruning unused containers, images, networks, and volumes...
|
||||
12:50:25 INFO: === Update started: 2026-05-12 12:50:25 ===
|
||||
12:50:25 WARNING: Skipping traefik (directory does not exist)
|
||||
12:50:25 WARNING: Skipping nextcloud (directory does not exist)
|
||||
12:50:25 WARNING: Skipping passbolt (directory does not exist)
|
||||
12:50:25 WARNING: Skipping searxng (directory does not exist)
|
||||
12:50:25 WARNING: Skipping gitea (directory does not exist)
|
||||
12:50:25 WARNING: Skipping gotify (directory does not exist)
|
||||
12:50:25 WARNING: Skipping grafana (directory does not exist)
|
||||
12:50:25 WARNING: Skipping gramps (directory does not exist)
|
||||
12:50:25 WARNING: Skipping portainer (directory does not exist)
|
||||
12:50:25 WARNING: Skipping prometheus (directory does not exist)
|
||||
12:50:25 WARNING: Skipping uptime-kuma (directory does not exist)
|
||||
12:50:25 INFO: Pruning unused containers, images, networks, and volumes...
|
||||
|
||||
Reference in New Issue
Block a user