diff --git a/README.md b/README.md index 4924a2d..ab70398 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,7 @@ Run a read-only health snapshot from the Debian server with: ./jeannie gitops-status ./jeannie cert-check ./jeannie release-snapshot +./jeannie map ``` It reports host memory/disk, systemd services, Docker Compose stacks, @@ -314,6 +315,10 @@ HTTP status, and DuckDNS-to-OCI edge IP drift from the canonical inventory. state directory with Git state, Kubernetes/Argo CD/Helm state, workload images, public URL status, and pointers to the latest Gitea/OpenTofu backups. +`map` prints a dependency map from the canonical inventory, covering public +entry, GitOps automation, cluster foundation, DNS, observability, and security +learning paths. `./jeannie map --dot` emits Graphviz DOT. + Focused doctor commands run narrower read-only checks and print the most likely next step: diff --git a/README.md.tmpl b/README.md.tmpl index 2e0d62e..942faa9 100644 --- a/README.md.tmpl +++ b/README.md.tmpl @@ -275,6 +275,7 @@ Run a read-only health snapshot from the Debian server with: ./{{ main_script }} gitops-status ./{{ main_script }} cert-check ./{{ main_script }} release-snapshot +./{{ main_script }} map ``` It reports host memory/disk, systemd services, Docker Compose stacks, @@ -314,6 +315,10 @@ HTTP status, and DuckDNS-to-OCI edge IP drift from the canonical inventory. state directory with Git state, Kubernetes/Argo CD/Helm state, workload images, public URL status, and pointers to the latest Gitea/OpenTofu backups. +`map` prints a dependency map from the canonical inventory, covering public +entry, GitOps automation, cluster foundation, DNS, observability, and security +learning paths. `./{{ main_script }} map --dot` emits Graphviz DOT. + Focused doctor commands run narrower read-only checks and print the most likely next step: diff --git a/docs/jeannie.1.md b/docs/jeannie.1.md index 532af6a..bc4c957 100644 --- a/docs/jeannie.1.md +++ b/docs/jeannie.1.md @@ -76,6 +76,10 @@ and DuckDNS-to-OCI edge IP drift from the canonical inventory. state, workload images, public URL status, and latest Gitea/OpenTofu backup pointers. +`map` +: Print a dependency map from the canonical inventory. Use `map --dot` for a +Graphviz DOT graph. + Prometheus alerts : High-signal alert rules are managed as the `apps/homelab-alerts` GitOps app. They cover node readiness, restart storms, unavailable deployments, storage diff --git a/jeannie b/jeannie index 630d77a..ccd2cda 100755 --- a/jeannie +++ b/jeannie @@ -5986,6 +5986,10 @@ release_snapshot() { "${REPO_ROOT}/scripts/release-snapshot" } +homelab_map() { + "${REPO_ROOT}/scripts/homelab-map" "${@:2}" +} + case "${1:-}" in up) up @@ -6020,6 +6024,9 @@ case "${1:-}" in release-snapshot) release_snapshot ;; + map) + homelab_map "$@" + ;; validate) validate_homelab ;; @@ -6164,7 +6171,7 @@ case "${1:-}" in echo "Log: ${JEANNIE_LOG_FILE}" ;; *) - echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|capacity|recover-plan|gitops-status|cert-check|release-snapshot|validate|access-audit|kubeconfig-readonly|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-restore|drill-gitea-restore|drill-pihole-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-runtime|security-attack-path|openwrt|nuke}" + echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|capacity|recover-plan|gitops-status|cert-check|release-snapshot|map [--dot]|validate|access-audit|kubeconfig-readonly|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-restore|drill-gitea-restore|drill-pihole-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-runtime|security-attack-path|openwrt|nuke}" exit 1 ;; esac diff --git a/scripts/homelab-map b/scripts/homelab-map new file mode 100755 index 0000000..1cf0e60 --- /dev/null +++ b/scripts/homelab-map @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +INVENTORY_FILE="${HOMELAB_INVENTORY_FILE:-${REPO_ROOT}/homelab.yml}" +FORMAT="${1:-text}" + +inventory() { + local path="$1" + local fallback="$2" + + if command -v ruby >/dev/null 2>&1 && [ -f "$INVENTORY_FILE" ]; then + value="$( + ruby -ryaml -e ' + data = YAML.load_file(ARGV[0]) + value = ARGV[1].split(".").reduce(data) { |memo, key| memo.respond_to?(:[]) ? memo[key] : nil } + puts value if value.is_a?(String) || value.is_a?(Integer) + ' "$INVENTORY_FILE" "$path" 2>/dev/null || true + )" + if [ -n "$value" ]; then + printf '%s\n' "$value" + return 0 + fi + fi + + printf '%s\n' "$fallback" +} + +domain="$(inventory domain.base lab2025.duckdns.org)" +debian="$(inventory hosts.debian.lan_ip 192.168.100.73)" +debian_ts="$(inventory hosts.debian.tailscale_ip 100.85.138.30)" +rpi="$(inventory hosts.rpi4.lan_ip 192.168.100.89)" +oci="$(inventory hosts.oci_edge.public_ip 132.145.170.74)" +oci_ts="$(inventory hosts.oci_edge.tailscale_ip 100.118.255.19)" +pimox="$(inventory hosts.opi5_pimox.lan_ip 192.168.100.80)" +traefik="$(inventory network.metallb.traefik_ip 192.168.100.240)" +registry="$(inventory services.local_registry.endpoint 192.168.100.73:30500)" +gitea_http="$(inventory services.gitea.http_port 3000)" +gitea_ssh="$(inventory services.gitea.ssh_port 32222)" +pihole_web="$(inventory services.rpi_dns.pihole_web_port 8081)" +kuma="$(inventory services.rpi_dns.uptime_kuma_port 3001)" + +if [ "$FORMAT" = "--dot" ] || [ "$FORMAT" = "dot" ]; then + cat < "OCI edge ${oci}"; + "OCI edge ${oci}" -> "Traefik ${traefik}"; + "OCI edge ${oci}" -> "Debian Tailscale ${debian_ts}"; + "Debian ${debian}" -> "Gitea :${gitea_http}/git"; + "Debian ${debian}" -> "Local registry ${registry}"; + "Debian ${debian}" -> "Kubernetes control plane"; + "Debian ${debian}" -> "Pimox ${pimox}"; + "Pimox ${pimox}" -> "Worker VMs"; + "Worker VMs" -> "Kubernetes apps"; + "Kubernetes apps" -> "Traefik ${traefik}"; + "Gitea :${gitea_http}/git" -> "Argo CD"; + "Argo CD" -> "Kubernetes apps"; + "Clients" -> "Pi-hole ${rpi}:53"; + "Pi-hole ${rpi}:53" -> "Unbound"; + "Pi-hole ${rpi}:53" -> "Public DNS fallback"; + "Uptime Kuma ${rpi}:${kuma}" -> "Public URLs"; + "Uptime Kuma ${rpi}:${kuma}" -> "LAN services"; +} +EOF + exit 0 +fi + +cat < ${domain} + -> OCI edge ${oci} / tailscale ${oci_ts} + -> Traefik LoadBalancer ${traefik} + -> Kubernetes ingress routes + -> website, demos, Heimdall, observability, Argo CD, arr routes + +Git and automation + Debian ${debian} + -> Gitea HTTP :${gitea_http}/git and SSH :${gitea_ssh} + -> Gitea Actions runner + -> local registry ${registry} + -> jeannie orchestration + Gitea repository + -> Argo CD applications + -> Kubernetes manifests under apps/ + -> edge/provisioning/cluster/platform Terraform stacks + +Cluster foundation + Debian ${debian} + -> kubeadm control plane + -> container registry + -> provisioning HTTP/TFTP + Orange Pi Pimox ${pimox} + -> worker VM template + -> worker VM clones + -> Kubernetes worker capacity + +DNS and always-on health + Clients and lab hosts + -> RPi4 Pi-hole ${rpi}:53 + -> Unbound resolver + -> public fallback DNS if Unbound is unhealthy + RPi4 + -> Pi-hole web ${rpi}:${pihole_web} + -> Uptime Kuma ${rpi}:${kuma} + -> monitors public URLs, LAN services, DNS, Traefik, Gitea, arr + +Observability and operations + Prometheus stack + -> nodes, pods, Traefik, website probes, Gitea/Pi-hole scrape coverage + -> homelab-alerts + -> Grafana dashboards + jeannie status + -> host/bootstrap checks + -> local services + -> RPi DNS and Kuma + -> Pimox and Kubernetes + -> platform/apps + -> edge/public URLs + +Security learning loop + security-lab namespace + -> isolated OWASP practice targets + -> ZAP/nuclei/trivy/lynis helper commands + -> report-only findings before manual fixes + +Use './jeannie map --dot' to emit a Graphviz DOT version. +EOF