mirror of
https://github.com/fscotto/infra.git
synced 2026-09-27 19:03:47 +00:00
Add dormant Rocky server profile (#2)
* Add dormant Rocky server profile * feat: activate Rocky server profile for prometheus * fix: complete Rocky server migration tooling * Remove FreeBSD profile * feat: add Aegis Fedora CoreOS profile * docs: define Git Flow branch prefixes * Add Aegis TPM-backed btrfs storage Co-authored-by: fscotto <17803710+fscotto@users.noreply.github.com> * Preserve existing Aegis filesystem tables Co-authored-by: fscotto <17803710+fscotto@users.noreply.github.com> * Remove unsupported Aegis TPM storage Co-authored-by: fscotto <17803710+fscotto@users.noreply.github.com> * Configure Aegis pi Btrfs storage Co-authored-by: fscotto <17803710+fscotto@users.noreply.github.com> * Mount Aegis Btrfs as root Co-authored-by: fscotto <17803710+fscotto@users.noreply.github.com> * Handle Maven on Mise * Feature/aegis coreos (#7) * feat(aegis): add ARM image bootstrap workflow * feat(aegis): manage firewall and SSH hardening * feat(aegis): harden Fedora IoT services * chore(aegis): remove local TLS configuration * fix(aegis): restore supported Fedora IoT bootstrap --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: fscotto <17803710+fscotto@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
ae1ca44aa7
commit
eab66b6d3d
19
AGENTS.md
19
AGENTS.md
@@ -1,6 +1,6 @@
|
|||||||
# AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora IoT, WSL, and servers.
|
Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora IoT, WSL, a Rocky Linux 9 server, and an Atlas NAS.
|
||||||
|
|
||||||
## Source Of Truth
|
## Source Of Truth
|
||||||
- Main orchestration: `ansible/site.yml`
|
- Main orchestration: `ansible/site.yml`
|
||||||
@@ -15,7 +15,7 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
|
|||||||
- Current laptop: `nymph = platform_fedora + graphical_desktop + desktop_gnome`
|
- Current laptop: `nymph = platform_fedora + graphical_desktop + desktop_gnome`
|
||||||
- Void desktop profile is also the base for other future/reference hosts via `platform_void + graphical_desktop`
|
- Void desktop profile is also the base for other future/reference hosts via `platform_void + graphical_desktop`
|
||||||
- Workstation: `deadalus` is Windows + Fedora WSL.
|
- Workstation: `deadalus` is Windows + Fedora WSL.
|
||||||
- Ubuntu server: `prometheus`
|
- Rocky server: `prometheus` belongs to `rocky_server`.
|
||||||
- NAS: `atlas` (Rocky Linux 9, reached through SSH)
|
- NAS: `atlas` (Rocky Linux 9, reached through SSH)
|
||||||
- Always-on LAN node: `aegis` (Fedora IoT on Raspberry Pi 4, reached through SSH)
|
- Always-on LAN node: `aegis` (Fedora IoT on Raspberry Pi 4, reached through SSH)
|
||||||
- Hosts intentionally belong to multiple groups; trust `ansible/site.yml` over hostname assumptions.
|
- Hosts intentionally belong to multiple groups; trust `ansible/site.yml` over hostname assumptions.
|
||||||
@@ -45,6 +45,7 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
|
|||||||
- Fedora laptop work: `ansible-playbook ansible/site.yml --limit nymph --check --diff`
|
- Fedora laptop work: `ansible-playbook ansible/site.yml --limit nymph --check --diff`
|
||||||
- WSL workstation dev: `ansible-playbook ansible/site.yml --limit deadalus --check --diff`
|
- WSL workstation dev: `ansible-playbook ansible/site.yml --limit deadalus --check --diff`
|
||||||
- Server: `ansible-playbook ansible/site.yml --limit prometheus --check --diff`
|
- Server: `ansible-playbook ansible/site.yml --limit prometheus --check --diff`
|
||||||
|
- Rocky server after activation: `ansible-playbook ansible/site.yml --limit <host> --check --diff`
|
||||||
- Atlas NAS: `ansible-playbook ansible/site.yml --limit atlas --check --diff`
|
- Atlas NAS: `ansible-playbook ansible/site.yml --limit atlas --check --diff`
|
||||||
- Aegis IoT: `ansible-playbook ansible/site.yml --limit aegis --check --diff`
|
- Aegis IoT: `ansible-playbook ansible/site.yml --limit aegis --check --diff`
|
||||||
- Focused checks:
|
- Focused checks:
|
||||||
@@ -83,7 +84,9 @@ The Void desktop package lists in `ansible/inventory/group_vars/void.yml` are ke
|
|||||||
- `desktop_common_packages` — GUI infrastructure shared by the minimal desktop mode.
|
- `desktop_common_packages` — GUI infrastructure shared by the minimal desktop mode.
|
||||||
- `desktop_minimal_packages` — applications, integration components, and the `emptty` display manager.
|
- `desktop_minimal_packages` — applications, integration components, and the `emptty` display manager.
|
||||||
- `desktop_sway_packages` — binaries specific to the Sway session.
|
- `desktop_sway_packages` — binaries specific to the Sway session.
|
||||||
`profile_packages` in the same file is cross-distro and is overridden by `group_vars/server.yml` and the workstation group vars; do not move desktop-specific Void entries through it.
|
`profile_packages` remains the shared package bucket for Void and Fedora profiles. Rocky uses
|
||||||
|
`rocky_profile_packages` so RPM-specific names do not leak back into the other platforms; do not move
|
||||||
|
desktop-specific Void entries through either bucket.
|
||||||
The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-independent content and `desktop_minimal_dotfiles` carries Thunar, Udiskie, and MIME defaults. `desktop_void_dotfiles` remains reserved for files that need the Void runtime.
|
The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-independent content and `desktop_minimal_dotfiles` carries Thunar, Udiskie, and MIME defaults. `desktop_void_dotfiles` remains reserved for files that need the Void runtime.
|
||||||
|
|
||||||
## Workstation Notes
|
## Workstation Notes
|
||||||
@@ -92,6 +95,16 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
|||||||
- Fedora WSL installs Mise from the official `jdxcode/mise` COPR and uses its pinned Temurin Java 11 JDK; update the declared Mise version deliberately.
|
- Fedora WSL installs Mise from the official `jdxcode/mise` COPR and uses its pinned Temurin Java 11 JDK; update the declared Mise version deliberately.
|
||||||
- Windows applications are installed manually and are not managed from the WSL profile.
|
- Windows applications are installed manually and are not managed from the WSL profile.
|
||||||
|
|
||||||
|
## Rocky Server Notes
|
||||||
|
- `rocky_server` is a child of both `platform_rocky` and `server`; `prometheus` is its active target.
|
||||||
|
- The target must already provide `server_username` with local sudo access before the profile runs.
|
||||||
|
- The Rocky profile installs Docker CE, uses firewalld, preserves SELinux enforcement, and renders the
|
||||||
|
same server Compose stack. It does not transfer data, start containers, update DNS, or cut over traffic.
|
||||||
|
- `scripts/migrate_prometheus_data.sh` is the separate, source-host-run migration path. It dry-runs by
|
||||||
|
default and requires explicit source-stack quiescing before copying persistent Docker data with rsync.
|
||||||
|
- Atlas-only OpenZFS, NFS, Samba, Cockpit, and Syncthing stay selected through Atlas host variables
|
||||||
|
and must not leak into `rocky_server`.
|
||||||
|
|
||||||
## Atlas NAS Notes
|
## Atlas NAS Notes
|
||||||
- `atlas` is a remote Rocky Linux 9 NAS. Keep its connection, LAN, pool and mountpoint values in
|
- `atlas` is a remote Rocky Linux 9 NAS. Keep its connection, LAN, pool and mountpoint values in
|
||||||
`host_vars/atlas.yml`. Bootstrap it once with `-e atlas_connection_username=<existing-admin>`;
|
`host_vars/atlas.yml`. Bootstrap it once with `-e atlas_connection_username=<existing-admin>`;
|
||||||
|
|||||||
37
README.it.md
37
README.it.md
@@ -33,7 +33,6 @@ infra/
|
|||||||
│ ├── common/
|
│ ├── common/
|
||||||
│ ├── desktop/
|
│ ├── desktop/
|
||||||
│ ├── fedora/
|
│ ├── fedora/
|
||||||
│ ├── ubuntu/
|
|
||||||
│ ├── server/
|
│ ├── server/
|
||||||
│ ├── workstation/
|
│ ├── workstation/
|
||||||
│ ├── workstation_dev_wsl/
|
│ ├── workstation_dev_wsl/
|
||||||
@@ -56,8 +55,8 @@ Il repository è diviso in due componenti principali:
|
|||||||
|
|
||||||
# Macchine gestite
|
# Macchine gestite
|
||||||
|
|
||||||
Il repository modella attualmente host Fedora/GNOME, una workstation Fedora WSL, un server Ubuntu e
|
Il repository modella attualmente host Fedora/GNOME, una workstation Fedora WSL, un server Rocky
|
||||||
un NAS Rocky Linux 9. La composizione resta separata in assi indipendenti:
|
Linux 9 e un NAS Rocky Linux 9. La composizione resta separata in assi indipendenti:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
common user environment
|
common user environment
|
||||||
@@ -74,8 +73,8 @@ Matrice target:
|
|||||||
| ikaros | Fedora | Personal workstation | GNOME |
|
| ikaros | Fedora | Personal workstation | GNOME |
|
||||||
| nymph | Fedora | Desktop laptop | GNOME |
|
| nymph | Fedora | Desktop laptop | GNOME |
|
||||||
| deadalus | Fedora WSL | Workstation dev | — |
|
| deadalus | Fedora WSL | Workstation dev | — |
|
||||||
| prometheus | Ubuntu | Server | — |
|
| prometheus | Rocky Linux | Server | — |
|
||||||
| atlas | Rocky 9 | NAS | — |
|
| atlas | Rocky Linux | NAS | — |
|
||||||
|
|
||||||
Regola operativa:
|
Regola operativa:
|
||||||
|
|
||||||
@@ -93,7 +92,7 @@ Nota sullo stato attuale del playbook principale:
|
|||||||
|
|
||||||
- `ansible/site.yml` applica oggi in automatico Fedora/GNOME su `ikaros` e `nymph`
|
- `ansible/site.yml` applica oggi in automatico Fedora/GNOME su `ikaros` e `nymph`
|
||||||
- `ansible/site.yml` applica il profilo Fedora WSL alla workstation `deadalus`
|
- `ansible/site.yml` applica il profilo Fedora WSL alla workstation `deadalus`
|
||||||
- `ansible/site.yml` applica anche il profilo `ubuntu_server` con baseline apt, systemd, dotfiles server e firewall UFW
|
- `ansible/site.yml` applica il profilo server Rocky a `prometheus` con DNF, systemd, dotfiles server e firewalld
|
||||||
- `ansible/site.yml` applica il profilo NAS Rocky su `atlas` tramite SSH remoto
|
- `ansible/site.yml` applica il profilo NAS Rocky su `atlas` tramite SSH remoto
|
||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
@@ -164,7 +163,7 @@ Le applicazioni Windows sono installate e gestite manualmente; il profilo WSL no
|
|||||||
|
|
||||||
Sistema operativo:
|
Sistema operativo:
|
||||||
|
|
||||||
- Ubuntu LTS
|
- Rocky Linux 9
|
||||||
|
|
||||||
Configurazione:
|
Configurazione:
|
||||||
|
|
||||||
@@ -178,12 +177,19 @@ Profilo orientato a servizi server e gestione di dotfiles dedicati.
|
|||||||
|
|
||||||
Lo stato attuale del profilo server include:
|
Lo stato attuale del profilo server include:
|
||||||
|
|
||||||
- installazione pacchetti base Ubuntu via apt
|
- installazione pacchetti Rocky via DNF, EPEL e CRB
|
||||||
- installazione e configurazione di Docker dal repository ufficiale
|
- installazione e configurazione di Docker dal repository ufficiale
|
||||||
- abilitazione dei servizi systemd dichiarati in inventory/group vars
|
- abilitazione dei servizi systemd dichiarati in inventory/group vars
|
||||||
- copia dei dotfiles server e rendering dei template server, incluso il `docker-compose.yml` dello stack servizi
|
- copia dei dotfiles server e rendering dei template server, incluso il `docker-compose.yml` dello stack servizi
|
||||||
- attivazione del firewall UFW con regola SSH esplicita
|
- attivazione di firewalld con servizio SSH esplicitamente abilitato
|
||||||
- apertura delle porte Syncthing `22000/tcp`, `22000/udp` e `21027/udp`, lasciando la GUI non esposta direttamente su UFW
|
- Syncthing escluso dal profilo server Rocky
|
||||||
|
|
||||||
|
### Migrazione dati
|
||||||
|
|
||||||
|
Dopo il provisioning Rocky, eseguire `scripts/migrate_prometheus_data.sh` **sul server Ubuntu
|
||||||
|
sorgente**. Lo script usa rsync, e in dry-run di default; richiede `--quiesce-source --execute` per
|
||||||
|
fermare lo stack sorgente e copiare in modo consistente i dati PostgreSQL. Non avvia container, non
|
||||||
|
cancella dati e non esegue il cutover.
|
||||||
|
|
||||||
Utente del profilo server:
|
Utente del profilo server:
|
||||||
|
|
||||||
@@ -274,7 +280,6 @@ I principali ruoli attualmente presenti sono:
|
|||||||
| ------------------------- | ----------------------------------- |
|
| ------------------------- | ----------------------------------- |
|
||||||
| base | configurazione base comune |
|
| base | configurazione base comune |
|
||||||
| packages_void | installazione pacchetti su Void |
|
| packages_void | installazione pacchetti su Void |
|
||||||
| packages_ubuntu | installazione pacchetti su Ubuntu |
|
|
||||||
| packages_fedora | installazione pacchetti su Fedora |
|
| packages_fedora | installazione pacchetti su Fedora |
|
||||||
| packages_rocky | installazione pacchetti su Rocky Linux 9 |
|
| packages_rocky | installazione pacchetti su Rocky Linux 9 |
|
||||||
| services_runit | gestione servizi runit |
|
| services_runit | gestione servizi runit |
|
||||||
@@ -309,7 +314,7 @@ platform_fedora & role_personal_workstation -> profile_personal_workstation
|
|||||||
platform_fedora & desktop_gnome -> profile_desktop_gnome
|
platform_fedora & desktop_gnome -> profile_desktop_gnome
|
||||||
workstation_dev_fedora -> profile_workstation_dev_common
|
workstation_dev_fedora -> profile_workstation_dev_common
|
||||||
workstation_dev_wsl -> profile_workstation_dev_wsl (dopo platform_fedora + workstation_dev_fedora)
|
workstation_dev_wsl -> profile_workstation_dev_wsl (dopo platform_fedora + workstation_dev_fedora)
|
||||||
ubuntu_server -> packages_ubuntu + services_systemd + profile_server
|
rocky_server -> dotfiles_common + profile_server (dopo platform_rocky)
|
||||||
```
|
```
|
||||||
|
|
||||||
Questo significa che, allo stato attuale:
|
Questo significa che, allo stato attuale:
|
||||||
@@ -318,9 +323,9 @@ Questo significa che, allo stato attuale:
|
|||||||
- `nymph` riceve Fedora Workstation/GNOME come target laptop
|
- `nymph` riceve Fedora Workstation/GNOME come target laptop
|
||||||
- il profilo Void resta selezionabile tramite `platform_void + graphical_desktop` per host futuri
|
- il profilo Void resta selezionabile tramite `platform_void + graphical_desktop` per host futuri
|
||||||
- `deadalus` riceve il profilo Fedora WSL tramite play dev dedicati
|
- `deadalus` riceve il profilo Fedora WSL tramite play dev dedicati
|
||||||
- il server Ubuntu (`prometheus`) e gestito con pacchetti, servizi, dotfiles server e firewall
|
- il server Rocky (`prometheus`) e gestito con pacchetti, servizi, dotfiles server e firewalld
|
||||||
- il NAS Rocky (`atlas`) usa un pool ZFS gia esistente, condivisioni NFSv4/SMB limitate alla LAN e Cockpit/45Drives
|
- il NAS Rocky (`atlas`) usa un pool ZFS gia esistente, condivisioni NFSv4/SMB limitate alla LAN e Cockpit/45Drives
|
||||||
- lo stack container server include `navidrome`, `postgres`, `gitea`, `nginx-proxy-manager` e `syncthing`, con GUI Syncthing raggiungibile tramite la rete Docker `web`
|
- lo stack container server include `navidrome`, `postgres`, `gitea` e `nginx-proxy-manager`
|
||||||
|
|
||||||
# Dotfiles
|
# Dotfiles
|
||||||
|
|
||||||
@@ -332,7 +337,6 @@ dotfiles/
|
|||||||
├── desktop
|
├── desktop
|
||||||
├── server
|
├── server
|
||||||
├── fedora
|
├── fedora
|
||||||
├── ubuntu
|
|
||||||
├── workstation
|
├── workstation
|
||||||
├── workstation_dev_wsl
|
├── workstation_dev_wsl
|
||||||
└── nymph
|
└── nymph
|
||||||
@@ -394,8 +398,7 @@ Allo stato attuale questo comando:
|
|||||||
- per `platform_fedora & role_personal_workstation` applica il layer personale a `ikaros`
|
- per `platform_fedora & role_personal_workstation` applica il layer personale a `ikaros`
|
||||||
- per `platform_fedora & desktop_gnome` applica il profilo GNOME a `ikaros` e `nymph`
|
- per `platform_fedora & desktop_gnome` applica il profilo GNOME a `ikaros` e `nymph`
|
||||||
- per `workstation_dev_wsl` applica i tweak WSL dopo il layer Fedora a `deadalus`, escludendo Flatpak e Snap
|
- per `workstation_dev_wsl` applica i tweak WSL dopo il layer Fedora a `deadalus`, escludendo Flatpak e Snap
|
||||||
- per gli host `ubuntu_server` applica pacchetti Ubuntu, servizi systemd, profilo server, UFW, dotfiles e template dedicati
|
- per `platform_rocky` applica pacchetti Rocky e servizi systemd ad `atlas` e `prometheus`; quindi applica il profilo NAS ad `atlas` e il profilo server a `prometheus`
|
||||||
- per `platform_rocky` applica pacchetti Rocky e servizi systemd ad `atlas`, quindi il profilo NAS dedicato
|
|
||||||
- non riavvia automaticamente il display manager
|
- non riavvia automaticamente il display manager
|
||||||
- carica `secrets/vault.yml` solo se presente
|
- carica `secrets/vault.yml` solo se presente
|
||||||
- carica `secrets/vault.local.yml` solo se presente, dopo `vault.yml`, cosi gli override locali hanno precedenza
|
- carica `secrets/vault.local.yml` solo se presente, dopo `vault.yml`, cosi gli override locali hanno precedenza
|
||||||
|
|||||||
49
README.md
49
README.md
@@ -20,7 +20,6 @@ infra/
|
|||||||
│ ├── common/
|
│ ├── common/
|
||||||
│ ├── desktop/
|
│ ├── desktop/
|
||||||
│ ├── fedora/
|
│ ├── fedora/
|
||||||
│ ├── ubuntu/
|
|
||||||
│ ├── server/
|
│ ├── server/
|
||||||
│ ├── workstation/
|
│ ├── workstation/
|
||||||
│ ├── workstation_dev_wsl/
|
│ ├── workstation_dev_wsl/
|
||||||
@@ -37,8 +36,8 @@ infra/
|
|||||||
## Managed machines
|
## Managed machines
|
||||||
|
|
||||||
The repo currently covers Fedora/GNOME desktops, one Fedora WSL workstation, a Fedora IoT LAN
|
The repo currently covers Fedora/GNOME desktops, one Fedora WSL workstation, a Fedora IoT LAN
|
||||||
node, an Ubuntu server, and a Rocky Linux 9 NAS. Configuration is layered instead of being tied to
|
node, a Rocky Linux 9 server, and a Rocky Linux 9 NAS. Configuration is layered instead of being tied
|
||||||
host names:
|
to host names:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
common user environment
|
common user environment
|
||||||
@@ -54,8 +53,8 @@ common user environment
|
|||||||
| `nymph` | Fedora | Desktop laptop | GNOME |
|
| `nymph` | Fedora | Desktop laptop | GNOME |
|
||||||
| `deadalus` | Fedora WSL | Development workstation | — |
|
| `deadalus` | Fedora WSL | Development workstation | — |
|
||||||
| `aegis` | Fedora IoT | Always-on LAN node | — |
|
| `aegis` | Fedora IoT | Always-on LAN node | — |
|
||||||
| `prometheus` | Ubuntu | Server | — |
|
| `prometheus` | Rocky Linux | Server | — |
|
||||||
| `atlas` | Rocky 9 | NAS | — |
|
| `atlas` | Rocky Linux | NAS | — |
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ikaros must be boring
|
ikaros must be boring
|
||||||
@@ -101,9 +100,14 @@ That gives it Fedora packages through DNF, Docker from the official repository,
|
|||||||
|
|
||||||
## Server
|
## Server
|
||||||
|
|
||||||
`prometheus` is the Ubuntu LTS server. It has no graphical environment and gets server-specific dotfiles and templates.
|
`prometheus` is the Rocky Linux 9 server. It has no graphical environment and gets server-specific
|
||||||
|
dotfiles and templates. The profile provisions configuration only: it does not transfer data, start
|
||||||
|
the Compose stack, update DNS, or perform a cutover.
|
||||||
|
|
||||||
The server profile installs Ubuntu packages, Docker from the official repository, declared systemd services, UFW rules, and the server Compose stack. Syncthing ports `22000/tcp`, `22000/udp`, and `21027/udp` are opened; the Syncthing GUI is not directly opened in UFW.
|
The server profile installs platform-specific packages, Docker CE from the official repository,
|
||||||
|
declared systemd services, the server Compose stack, and firewalld. The Rocky server excludes
|
||||||
|
Syncthing. Rocky bind mounts use private SELinux relabeling for application data while host system
|
||||||
|
files remain unchanged.
|
||||||
|
|
||||||
Server identity comes from `server_username`, `server_user_group`, and `server_user_home` in `ansible/inventory/group_vars/server.yml`. `server_username` defaults to `username`, but it can be overridden, for example:
|
Server identity comes from `server_username`, `server_user_group`, and `server_user_home` in `ansible/inventory/group_vars/server.yml`. `server_username` defaults to `username`, but it can be overridden, for example:
|
||||||
|
|
||||||
@@ -114,6 +118,30 @@ ansible-playbook ansible/site.yml --limit prometheus \
|
|||||||
-e server_user_home=/srv/myuser
|
-e server_user_home=/srv/myuser
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The target must already provide `server_username` with local sudo access.
|
||||||
|
|
||||||
|
### Data migration
|
||||||
|
|
||||||
|
Provision Rocky first, then run the migration script **on the retired Ubuntu source host**. It is
|
||||||
|
dry-run by default and requires an explicit source-stack stop before it can copy PostgreSQL data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./scripts/migrate_prometheus_data.sh \
|
||||||
|
--destination rocky@179.237.102.172 \
|
||||||
|
--identity /root/.ssh/id_ed25519
|
||||||
|
|
||||||
|
sudo ./scripts/migrate_prometheus_data.sh \
|
||||||
|
--destination rocky@179.237.102.172 \
|
||||||
|
--identity /root/.ssh/id_ed25519 \
|
||||||
|
--quiesce-source --execute
|
||||||
|
```
|
||||||
|
|
||||||
|
The script copies Navidrome, music, Nginx Proxy Manager, PostgreSQL and Gitea data. It does not
|
||||||
|
delete data, move Syncthing, copy `/home/git/.ssh`, start containers, update DNS, or perform a
|
||||||
|
cutover. The destination SSH host key must already be trusted and the destination account needs
|
||||||
|
passwordless sudo for `rsync`. It preserves ACLs but not extended attributes, so source SELinux labels
|
||||||
|
are not transferred; the Rocky Compose bind mounts apply their own `:Z` labels when containers start.
|
||||||
|
|
||||||
## Aegis
|
## Aegis
|
||||||
|
|
||||||
`aegis` is a Raspberry Pi 4 running Fedora IoT. Generate Ignition from
|
`aegis` is a Raspberry Pi 4 running Fedora IoT. Generate Ignition from
|
||||||
@@ -238,7 +266,6 @@ ansible-playbook ansible/site.yml --limit deadalus --tags ai_agents --check --di
|
|||||||
| Role | What it does |
|
| Role | What it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `packages_void` | Installs packages on Void. |
|
| `packages_void` | Installs packages on Void. |
|
||||||
| `packages_ubuntu` | Installs packages on Ubuntu. |
|
|
||||||
| `packages_fedora` | Installs packages on Fedora. |
|
| `packages_fedora` | Installs packages on Fedora. |
|
||||||
| `packages_rocky` | Installs packages on Rocky Linux 9. |
|
| `packages_rocky` | Installs packages on Rocky Linux 9. |
|
||||||
| `services_runit` | Manages runit services. |
|
| `services_runit` | Manages runit services. |
|
||||||
@@ -259,25 +286,25 @@ ansible-playbook ansible/site.yml --limit deadalus --tags ai_agents --check --di
|
|||||||
## What `site.yml` runs
|
## What `site.yml` runs
|
||||||
|
|
||||||
```text
|
```text
|
||||||
all -> dotfiles_common
|
all except platform_rocky -> dotfiles_common
|
||||||
platform_void -> packages_void + services_runit
|
platform_void -> packages_void + services_runit
|
||||||
platform_void & graphical_desktop -> profile_desktop_common + profile_desktop_sway + profile_desktop_niri + profile_desktop_host
|
platform_void & graphical_desktop -> profile_desktop_common + profile_desktop_sway + profile_desktop_niri + profile_desktop_host
|
||||||
platform_fedora -> packages_fedora + services_systemd
|
platform_fedora -> packages_fedora + services_systemd
|
||||||
platform_rocky -> packages_rocky + services_systemd
|
platform_rocky -> packages_rocky + services_systemd
|
||||||
role_aegis -> profile_aegis
|
role_aegis -> profile_aegis
|
||||||
atlas -> profile_atlas
|
atlas -> profile_atlas
|
||||||
|
rocky_server -> dotfiles_common + profile_server (after platform_rocky)
|
||||||
platform_fedora & role_personal_workstation -> profile_personal_workstation
|
platform_fedora & role_personal_workstation -> profile_personal_workstation
|
||||||
platform_fedora & desktop_gnome -> profile_desktop_gnome
|
platform_fedora & desktop_gnome -> profile_desktop_gnome
|
||||||
workstation_dev_fedora -> profile_workstation_dev_common
|
workstation_dev_fedora -> profile_workstation_dev_common
|
||||||
workstation_dev_wsl -> profile_workstation_dev_wsl (after platform_fedora + workstation_dev_fedora)
|
workstation_dev_wsl -> profile_workstation_dev_wsl (after platform_fedora + workstation_dev_fedora)
|
||||||
ubuntu_server -> packages_ubuntu + services_systemd + profile_server
|
|
||||||
```
|
```
|
||||||
|
|
||||||
So, in practice:
|
So, in practice:
|
||||||
|
|
||||||
- `platform_fedora` configures `ikaros`, `nymph`, and `deadalus`.
|
- `platform_fedora` configures `ikaros`, `nymph`, and `deadalus`.
|
||||||
- `deadalus` gets the Fedora development layer followed by the WSL layer.
|
- `deadalus` gets the Fedora development layer followed by the WSL layer.
|
||||||
- `ubuntu_server` configures `prometheus`.
|
- `rocky_server` configures the Rocky 9 server, `prometheus`.
|
||||||
- `atlas` receives the Rocky platform layer and the NAS profile through SSH.
|
- `atlas` receives the Rocky platform layer and the NAS profile through SSH.
|
||||||
- `aegis` receives only the immutable Fedora IoT profile through SSH; it does not receive
|
- `aegis` receives only the immutable Fedora IoT profile through SSH; it does not receive
|
||||||
mutable Fedora package or common dotfile roles.
|
mutable Fedora package or common dotfile roles.
|
||||||
|
|||||||
@@ -16,17 +16,39 @@ rocky_syncthing_archive_url: >-
|
|||||||
~ '.tar.gz'
|
~ '.tar.gz'
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
rocky_manage_openzfs_repo: false
|
||||||
|
rocky_manage_syncthing_binary: false
|
||||||
|
rocky_manage_docker_repo: false
|
||||||
|
rocky_manage_github_cli_repo: false
|
||||||
|
|
||||||
|
rocky_docker_repo_url: https://download.docker.com/linux/rhel/docker-ce.repo
|
||||||
|
rocky_docker_repo_file: /etc/yum.repos.d/docker-ce.repo
|
||||||
|
rocky_github_cli_repo_url: https://cli.github.com/packages/rpm/gh-cli.repo
|
||||||
|
rocky_github_cli_repo_file: /etc/yum.repos.d/gh-cli.repo
|
||||||
|
|
||||||
enabled_services:
|
enabled_services:
|
||||||
- firewalld
|
- firewalld
|
||||||
|
|
||||||
|
rocky_common_packages:
|
||||||
|
- bash-completion
|
||||||
|
- bat
|
||||||
|
- fzf
|
||||||
|
- git
|
||||||
|
- jq
|
||||||
|
- tree
|
||||||
|
- unzip
|
||||||
|
- vim-enhanced
|
||||||
|
- wget
|
||||||
|
- zip
|
||||||
|
- zoxide
|
||||||
|
|
||||||
rocky_packages_base:
|
rocky_packages_base:
|
||||||
- cockpit
|
|
||||||
- dnf-plugins-core
|
- dnf-plugins-core
|
||||||
- firewalld
|
- firewalld
|
||||||
- nfs-utils
|
|
||||||
- openssh-server
|
- openssh-server
|
||||||
- policycoreutils-python-utils
|
|
||||||
- rsync
|
- rsync
|
||||||
- samba
|
|
||||||
- sudo
|
- sudo
|
||||||
- zfs
|
|
||||||
|
rocky_profile_packages: []
|
||||||
|
rocky_docker_packages: []
|
||||||
|
rocky_docker_conflicting_packages: []
|
||||||
|
|||||||
59
ansible/inventory/group_vars/rocky_server.yml
Normal file
59
ansible/inventory/group_vars/rocky_server.yml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
rocky_manage_docker_repo: true
|
||||||
|
rocky_manage_github_cli_repo: true
|
||||||
|
|
||||||
|
rocky_profile_packages:
|
||||||
|
- avahi
|
||||||
|
- curl
|
||||||
|
- dmidecode
|
||||||
|
- dosfstools
|
||||||
|
- gcc
|
||||||
|
- gcc-c++
|
||||||
|
- git-delta
|
||||||
|
- gh
|
||||||
|
- gnupg2
|
||||||
|
- htop
|
||||||
|
- make
|
||||||
|
- nmap-ncat
|
||||||
|
- nodejs
|
||||||
|
- parted
|
||||||
|
- pciutils
|
||||||
|
- pinentry
|
||||||
|
- ranger
|
||||||
|
- ripgrep
|
||||||
|
|
||||||
|
rocky_docker_packages:
|
||||||
|
- containerd.io
|
||||||
|
- docker-buildx-plugin
|
||||||
|
- docker-ce
|
||||||
|
- docker-ce-cli
|
||||||
|
- docker-compose-plugin
|
||||||
|
|
||||||
|
rocky_docker_conflicting_packages:
|
||||||
|
- docker
|
||||||
|
- docker-client
|
||||||
|
- docker-client-latest
|
||||||
|
- docker-common
|
||||||
|
- docker-engine
|
||||||
|
- docker-latest
|
||||||
|
- docker-latest-logrotate
|
||||||
|
- docker-logrotate
|
||||||
|
- podman
|
||||||
|
- runc
|
||||||
|
|
||||||
|
enabled_services:
|
||||||
|
- firewalld
|
||||||
|
- docker
|
||||||
|
|
||||||
|
server_firewall_backend: firewalld
|
||||||
|
server_firewalld_zone: public
|
||||||
|
server_firewalld_services:
|
||||||
|
- ssh
|
||||||
|
server_firewalld_ports: []
|
||||||
|
server_firewalld_absent_ports:
|
||||||
|
- 22000/tcp
|
||||||
|
- 22000/udp
|
||||||
|
- 21027/udp
|
||||||
|
server_sshd_service_name: sshd
|
||||||
|
server_compose_selinux_mount_option: Z
|
||||||
|
server_syncthing_enabled: false
|
||||||
@@ -25,6 +25,9 @@ profile_packages:
|
|||||||
- rsync
|
- rsync
|
||||||
|
|
||||||
server_dotfiles:
|
server_dotfiles:
|
||||||
|
- src: .bashrc.d/20-editor-server.sh
|
||||||
|
dest: .bashrc.d/20-editor-server.sh
|
||||||
|
mode: "0644"
|
||||||
- src: .gnupg/gpg-agent.conf
|
- src: .gnupg/gpg-agent.conf
|
||||||
dest: .gnupg/gpg-agent.conf
|
dest: .gnupg/gpg-agent.conf
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
@@ -50,6 +53,10 @@ server_templates:
|
|||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
server_directories:
|
server_directories:
|
||||||
|
- path: "{{ server_user_home }}/.bashrc.d"
|
||||||
|
owner: "{{ server_username }}"
|
||||||
|
group: "{{ server_user_group }}"
|
||||||
|
mode: "0755"
|
||||||
- path: "{{ server_user_home }}/.gnupg"
|
- path: "{{ server_user_home }}/.gnupg"
|
||||||
owner: "{{ server_username }}"
|
owner: "{{ server_username }}"
|
||||||
group: "{{ server_user_group }}"
|
group: "{{ server_user_group }}"
|
||||||
@@ -82,6 +89,17 @@ server_directories:
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
- path: /srv/nextcloud
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
- path: /srv/nextcloud/data
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
server_syncthing_enabled: true
|
||||||
|
server_syncthing_directories:
|
||||||
- path: /opt/syncthing/config
|
- path: /opt/syncthing/config
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
@@ -94,30 +112,6 @@ server_directories:
|
|||||||
owner: "1000"
|
owner: "1000"
|
||||||
group: "1000"
|
group: "1000"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
- path: /srv/nextcloud
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: "0755"
|
|
||||||
- path: /srv/nextcloud/data
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
server_ufw_rules:
|
|
||||||
- rule: allow
|
|
||||||
name: OpenSSH
|
|
||||||
- rule: allow
|
|
||||||
port: "22000"
|
|
||||||
proto: tcp
|
|
||||||
comment: Syncthing sync traffic
|
|
||||||
- rule: allow
|
|
||||||
port: "22000"
|
|
||||||
proto: udp
|
|
||||||
comment: Syncthing QUIC sync traffic
|
|
||||||
- rule: allow
|
|
||||||
port: "21027"
|
|
||||||
proto: udp
|
|
||||||
comment: Syncthing local discovery
|
|
||||||
|
|
||||||
server_sshd_settings:
|
server_sshd_settings:
|
||||||
PermitRootLogin: "no"
|
PermitRootLogin: "no"
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
ubuntu_packages_base:
|
|
||||||
- curl
|
|
||||||
- ufw
|
|
||||||
- htop
|
|
||||||
- build-essential
|
|
||||||
- nodejs
|
|
||||||
- git-delta
|
|
||||||
- jq
|
|
||||||
- ripgrep
|
|
||||||
|
|
||||||
ubuntu_manage_docker_repo: true
|
|
||||||
|
|
||||||
ubuntu_emacs_packages:
|
|
||||||
- emacs
|
|
||||||
- pandoc
|
|
||||||
- latexmk
|
|
||||||
- texlive-luatex
|
|
||||||
- texlive-latex-base
|
|
||||||
- texlive-latex-recommended
|
|
||||||
- texlive-fonts-recommended
|
|
||||||
|
|
||||||
ubuntu_docker_packages:
|
|
||||||
- docker-ce
|
|
||||||
- docker-ce-cli
|
|
||||||
- containerd.io
|
|
||||||
- docker-buildx-plugin
|
|
||||||
- docker-compose-plugin
|
|
||||||
|
|
||||||
enabled_services:
|
|
||||||
- ufw
|
|
||||||
- docker
|
|
||||||
|
|
||||||
ubuntu_dotfiles:
|
|
||||||
- src: .bashrc.d/
|
|
||||||
dest: .bashrc.d/
|
|
||||||
mode: preserve
|
|
||||||
@@ -26,6 +26,16 @@ atlas_zfs_dataset_icloud_photos: icloud_photos
|
|||||||
atlas_mount_root: /CHANGEME_ATLAS_MOUNT_ROOT
|
atlas_mount_root: /CHANGEME_ATLAS_MOUNT_ROOT
|
||||||
atlas_manage_storage: false
|
atlas_manage_storage: false
|
||||||
|
|
||||||
|
rocky_manage_openzfs_repo: true
|
||||||
|
rocky_manage_syncthing_binary: true
|
||||||
|
|
||||||
|
host_packages:
|
||||||
|
- cockpit
|
||||||
|
- nfs-utils
|
||||||
|
- policycoreutils-python-utils
|
||||||
|
- samba
|
||||||
|
- zfs
|
||||||
|
|
||||||
atlas_nfs_exports:
|
atlas_nfs_exports:
|
||||||
- path: "{{ atlas_work_mountpoint }}"
|
- path: "{{ atlas_work_mountpoint }}"
|
||||||
clients: "{{ atlas_lan_subnet }}(rw,sync,no_subtree_check,root_squash)"
|
clients: "{{ atlas_lan_subnet }}(rw,sync,no_subtree_check,root_squash)"
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
hostname: prometheus
|
hostname: prometheus
|
||||||
|
ansible_host: 179.237.102.172
|
||||||
|
ansible_user: rocky
|
||||||
|
ansible_port: 22
|
||||||
|
ansible_ssh_private_key_file: /home/fscotto/.ssh/id_ed25519
|
||||||
|
|
||||||
host_packages: []
|
server_username: rocky
|
||||||
host_enabled_services: []
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ all:
|
|||||||
platform_rocky:
|
platform_rocky:
|
||||||
hosts:
|
hosts:
|
||||||
atlas:
|
atlas:
|
||||||
|
children:
|
||||||
|
rocky_server:
|
||||||
|
|
||||||
void:
|
void:
|
||||||
children:
|
children:
|
||||||
@@ -67,10 +69,6 @@ all:
|
|||||||
nymph:
|
nymph:
|
||||||
ansible_connection: local
|
ansible_connection: local
|
||||||
|
|
||||||
ubuntu:
|
|
||||||
children:
|
|
||||||
ubuntu_server:
|
|
||||||
|
|
||||||
workstation:
|
workstation:
|
||||||
children:
|
children:
|
||||||
workstation_dev:
|
workstation_dev:
|
||||||
@@ -82,7 +80,7 @@ all:
|
|||||||
|
|
||||||
server:
|
server:
|
||||||
children:
|
children:
|
||||||
ubuntu_server:
|
rocky_server:
|
||||||
|
|
||||||
workstation_dev_fedora:
|
workstation_dev_fedora:
|
||||||
hosts:
|
hosts:
|
||||||
@@ -94,7 +92,6 @@ all:
|
|||||||
deadalus:
|
deadalus:
|
||||||
ansible_connection: local
|
ansible_connection: local
|
||||||
|
|
||||||
ubuntu_server:
|
rocky_server:
|
||||||
hosts:
|
hosts:
|
||||||
prometheus:
|
prometheus:
|
||||||
ansible_connection: local
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
ansible.builtin.dnf:
|
ansible.builtin.dnf:
|
||||||
name: "{{ rocky_openzfs_release_rpm }}"
|
name: "{{ rocky_openzfs_release_rpm }}"
|
||||||
state: present
|
state: present
|
||||||
|
when: rocky_manage_openzfs_repo | bool
|
||||||
|
|
||||||
- name: Disable OpenZFS DKMS repository
|
- name: Disable OpenZFS DKMS repository
|
||||||
tags: [packages, storage]
|
tags: [packages, storage]
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
name:
|
name:
|
||||||
- zfs
|
- zfs
|
||||||
state: disabled
|
state: disabled
|
||||||
|
when: rocky_manage_openzfs_repo | bool
|
||||||
|
|
||||||
- name: Enable OpenZFS kmod repository
|
- name: Enable OpenZFS kmod repository
|
||||||
tags: [packages, storage]
|
tags: [packages, storage]
|
||||||
@@ -46,6 +48,36 @@
|
|||||||
name:
|
name:
|
||||||
- zfs-kmod
|
- zfs-kmod
|
||||||
state: enabled
|
state: enabled
|
||||||
|
when: rocky_manage_openzfs_repo | bool
|
||||||
|
|
||||||
|
- name: Configure official Docker RPM repository
|
||||||
|
tags: [packages, docker]
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ rocky_docker_repo_url }}"
|
||||||
|
dest: "{{ rocky_docker_repo_file }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: rocky_manage_docker_repo | bool
|
||||||
|
|
||||||
|
- name: Configure official GitHub CLI RPM repository
|
||||||
|
tags: [packages]
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ rocky_github_cli_repo_url }}"
|
||||||
|
dest: "{{ rocky_github_cli_repo_file }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: rocky_manage_github_cli_repo | bool
|
||||||
|
|
||||||
|
- name: Remove packages conflicting with Docker CE
|
||||||
|
tags: [packages, docker]
|
||||||
|
ansible.builtin.dnf:
|
||||||
|
name: "{{ rocky_docker_conflicting_packages }}"
|
||||||
|
state: absent
|
||||||
|
when:
|
||||||
|
- rocky_manage_docker_repo | bool
|
||||||
|
- rocky_docker_conflicting_packages | length > 0
|
||||||
|
|
||||||
- name: Refresh Rocky package metadata
|
- name: Refresh Rocky package metadata
|
||||||
tags: [packages]
|
tags: [packages]
|
||||||
@@ -58,20 +90,30 @@
|
|||||||
name: >-
|
name: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
(common_packages | default([]))
|
(rocky_common_packages | default([]))
|
||||||
+ (rocky_packages_base | default([]))
|
+ (rocky_packages_base | default([]))
|
||||||
+ (profile_packages | default([]))
|
+ (rocky_profile_packages | default([]))
|
||||||
|
+ (rocky_docker_packages | default([]))
|
||||||
+ (host_packages | default([]))
|
+ (host_packages | default([]))
|
||||||
) | unique
|
) | unique
|
||||||
}}
|
}}
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Add Rocky user to docker group
|
||||||
|
tags: [packages, docker]
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ effective_username }}"
|
||||||
|
groups: docker
|
||||||
|
append: true
|
||||||
|
when: rocky_docker_packages | length > 0
|
||||||
|
|
||||||
- name: Require supported architecture for Syncthing
|
- name: Require supported architecture for Syncthing
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- ansible_facts['architecture'] == 'x86_64'
|
- ansible_facts['architecture'] == 'x86_64'
|
||||||
fail_msg: The pinned Atlas Syncthing binary currently supports x86_64 only.
|
fail_msg: The pinned Atlas Syncthing binary currently supports x86_64 only.
|
||||||
|
when: rocky_manage_syncthing_binary | bool
|
||||||
|
|
||||||
- name: Read installed Syncthing version
|
- name: Read installed Syncthing version
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
@@ -83,6 +125,7 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: false
|
||||||
check_mode: false
|
check_mode: false
|
||||||
|
when: rocky_manage_syncthing_binary | bool
|
||||||
|
|
||||||
- name: Determine whether Syncthing must be installed
|
- name: Determine whether Syncthing must be installed
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
@@ -92,6 +135,7 @@
|
|||||||
('syncthing v' ~ rocky_syncthing_version ~ ' ')
|
('syncthing v' ~ rocky_syncthing_version ~ ' ')
|
||||||
not in (rocky_syncthing_version_check.stdout | default(''))
|
not in (rocky_syncthing_version_check.stdout | default(''))
|
||||||
}}
|
}}
|
||||||
|
when: rocky_manage_syncthing_binary | bool
|
||||||
|
|
||||||
- name: Create temporary Syncthing extraction directory
|
- name: Create temporary Syncthing extraction directory
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
@@ -101,7 +145,9 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
when: rocky_syncthing_install_required | bool
|
when:
|
||||||
|
- rocky_manage_syncthing_binary | bool
|
||||||
|
- rocky_syncthing_install_required | bool
|
||||||
|
|
||||||
- name: Download pinned Syncthing release
|
- name: Download pinned Syncthing release
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
@@ -112,7 +158,9 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
when: rocky_syncthing_install_required | bool
|
when:
|
||||||
|
- rocky_manage_syncthing_binary | bool
|
||||||
|
- rocky_syncthing_install_required | bool
|
||||||
|
|
||||||
- name: Extract pinned Syncthing release
|
- name: Extract pinned Syncthing release
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
@@ -120,7 +168,9 @@
|
|||||||
src: "/tmp/syncthing-{{ rocky_syncthing_version }}.tar.gz"
|
src: "/tmp/syncthing-{{ rocky_syncthing_version }}.tar.gz"
|
||||||
dest: "/tmp/syncthing-{{ rocky_syncthing_version }}"
|
dest: "/tmp/syncthing-{{ rocky_syncthing_version }}"
|
||||||
remote_src: true
|
remote_src: true
|
||||||
when: rocky_syncthing_install_required | bool
|
when:
|
||||||
|
- rocky_manage_syncthing_binary | bool
|
||||||
|
- rocky_syncthing_install_required | bool
|
||||||
|
|
||||||
- name: Install pinned Syncthing binary
|
- name: Install pinned Syncthing binary
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
@@ -136,16 +186,20 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
when: rocky_syncthing_install_required | bool
|
when:
|
||||||
|
- rocky_manage_syncthing_binary | bool
|
||||||
|
- rocky_syncthing_install_required | bool
|
||||||
|
|
||||||
- name: Remove Syncthing release archive
|
- name: Remove Syncthing release archive
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "/tmp/syncthing-{{ rocky_syncthing_version }}.tar.gz"
|
path: "/tmp/syncthing-{{ rocky_syncthing_version }}.tar.gz"
|
||||||
state: absent
|
state: absent
|
||||||
|
when: rocky_manage_syncthing_binary | bool
|
||||||
|
|
||||||
- name: Remove Syncthing extraction directory
|
- name: Remove Syncthing extraction directory
|
||||||
tags: [packages, syncthing]
|
tags: [packages, syncthing]
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "/tmp/syncthing-{{ rocky_syncthing_version }}"
|
path: "/tmp/syncthing-{{ rocky_syncthing_version }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
when: rocky_manage_syncthing_binary | bool
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Copy Ubuntu dotfiles
|
|
||||||
tags: [dotfiles, dotfiles:common]
|
|
||||||
ansible.builtin.copy:
|
|
||||||
src: "{{ playbook_dir }}/../dotfiles/ubuntu/{{ item.src }}"
|
|
||||||
dest: "{{ effective_user_home }}/{{ item.dest }}"
|
|
||||||
owner: "{{ effective_username }}"
|
|
||||||
group: "{{ effective_user_group }}"
|
|
||||||
mode: "{{ item.mode }}"
|
|
||||||
loop: "{{ ubuntu_dotfiles | default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.dest }}"
|
|
||||||
|
|
||||||
- name: Ensure Docker apt keyrings directory exists
|
|
||||||
tags: [packages]
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /etc/apt/keyrings
|
|
||||||
state: directory
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: "0755"
|
|
||||||
when: ubuntu_manage_docker_repo | default(false)
|
|
||||||
|
|
||||||
- name: Download Docker apt repository signing key
|
|
||||||
tags: [packages]
|
|
||||||
ansible.builtin.get_url:
|
|
||||||
url: https://download.docker.com/linux/ubuntu/gpg
|
|
||||||
dest: /etc/apt/keyrings/docker.asc
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: "0644"
|
|
||||||
when: ubuntu_manage_docker_repo | default(false)
|
|
||||||
|
|
||||||
- name: Configure Docker apt repository
|
|
||||||
tags: [packages]
|
|
||||||
ansible.builtin.apt_repository:
|
|
||||||
repo: >-
|
|
||||||
deb [arch={{ 'amd64' if ansible_facts['architecture'] == 'x86_64'
|
|
||||||
else 'arm64' if ansible_facts['architecture'] in ['aarch64', 'arm64']
|
|
||||||
else ansible_facts['architecture'] }}
|
|
||||||
signed-by=/etc/apt/keyrings/docker.asc]
|
|
||||||
https://download.docker.com/linux/ubuntu
|
|
||||||
{{ ansible_facts['distribution_release'] }} stable
|
|
||||||
filename: docker
|
|
||||||
state: present
|
|
||||||
update_cache: true
|
|
||||||
when: ubuntu_manage_docker_repo | default(false)
|
|
||||||
|
|
||||||
- name: Refresh apt package cache
|
|
||||||
tags: [packages]
|
|
||||||
ansible.builtin.apt:
|
|
||||||
update_cache: true
|
|
||||||
cache_valid_time: 3600
|
|
||||||
|
|
||||||
- name: Install packages on Ubuntu
|
|
||||||
tags: [packages]
|
|
||||||
ansible.builtin.apt:
|
|
||||||
name: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(common_packages | default([]))
|
|
||||||
+ (ubuntu_packages_base | default([]))
|
|
||||||
+ (ubuntu_docker_packages | default([]))
|
|
||||||
+ (profile_packages | default([]))
|
|
||||||
+ (
|
|
||||||
(ubuntu_emacs_packages | default([]))
|
|
||||||
if (emacs_enabled | default(false) | bool)
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
+ (host_packages | default([]))
|
|
||||||
)
|
|
||||||
| unique
|
|
||||||
}}
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Add user to docker group
|
|
||||||
tags: [packages]
|
|
||||||
ansible.builtin.user:
|
|
||||||
name: "{{ effective_username }}"
|
|
||||||
groups: docker
|
|
||||||
append: true
|
|
||||||
when: (ubuntu_docker_packages | default([])) | length > 0
|
|
||||||
@@ -2,5 +2,5 @@
|
|||||||
- name: Reload SSH service
|
- name: Reload SSH service
|
||||||
tags: [services]
|
tags: [services]
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
name: ssh
|
name: "{{ server_sshd_service_name }}"
|
||||||
state: reloaded
|
state: reloaded
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Copy server dotfiles
|
- name: Require supported server firewall backend
|
||||||
tags: [dotfiles, dotfiles:server]
|
tags: [services, packages]
|
||||||
ansible.builtin.copy:
|
ansible.builtin.assert:
|
||||||
src: "{{ playbook_dir }}/../dotfiles/server/{{ item.src }}"
|
that:
|
||||||
dest: "{{ server_user_home }}/{{ item.dest }}"
|
- server_firewall_backend == 'firewalld'
|
||||||
owner: "{{ server_username }}"
|
fail_msg: >-
|
||||||
group: "{{ server_user_group }}"
|
server_firewall_backend must be firewalld for the Rocky server profile.
|
||||||
mode: "{{ item.mode }}"
|
|
||||||
loop: "{{ server_dotfiles | default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.dest }}"
|
|
||||||
|
|
||||||
- name: Require server container secret variables
|
- name: Require server container secret variables
|
||||||
tags: [dotfiles, dotfiles:server, services]
|
tags: [dotfiles, dotfiles:server, services]
|
||||||
@@ -30,10 +26,30 @@
|
|||||||
owner: "{{ item.owner }}"
|
owner: "{{ item.owner }}"
|
||||||
group: "{{ item.group }}"
|
group: "{{ item.group }}"
|
||||||
mode: "{{ item.mode }}"
|
mode: "{{ item.mode }}"
|
||||||
loop: "{{ server_directories | default([]) }}"
|
loop: >-
|
||||||
|
{{
|
||||||
|
(server_directories | default([]))
|
||||||
|
+ (
|
||||||
|
server_syncthing_directories | default([])
|
||||||
|
if server_syncthing_enabled | default(true) | bool
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
}}
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.path }}"
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
|
- name: Copy server dotfiles
|
||||||
|
tags: [dotfiles, dotfiles:server]
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ playbook_dir }}/../dotfiles/server/{{ item.src }}"
|
||||||
|
dest: "{{ server_user_home }}/{{ item.dest }}"
|
||||||
|
owner: "{{ server_username }}"
|
||||||
|
group: "{{ server_user_group }}"
|
||||||
|
mode: "{{ item.mode }}"
|
||||||
|
loop: "{{ server_dotfiles | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.dest }}"
|
||||||
|
|
||||||
- name: Render server templates
|
- name: Render server templates
|
||||||
tags: [dotfiles, dotfiles:server]
|
tags: [dotfiles, dotfiles:server]
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -68,33 +84,41 @@
|
|||||||
notify: Reload SSH service
|
notify: Reload SSH service
|
||||||
when: (server_sshd_allow_users | default([])) | length > 0
|
when: (server_sshd_allow_users | default([])) | length > 0
|
||||||
|
|
||||||
- name: Define effective server UFW rules
|
- name: Allow server services through firewalld
|
||||||
tags: [services, packages]
|
tags: [services, packages]
|
||||||
ansible.builtin.set_fact:
|
ansible.posix.firewalld:
|
||||||
server_ufw_rules_effective: "{{ server_ufw_rules | default([]) }}"
|
service: "{{ item }}"
|
||||||
|
zone: "{{ server_firewalld_zone }}"
|
||||||
- name: Apply server UFW rules
|
|
||||||
tags: [services, packages]
|
|
||||||
community.general.ufw:
|
|
||||||
rule: "{{ item.rule }}"
|
|
||||||
name: "{{ item.name | default(omit) }}"
|
|
||||||
port: "{{ item.port | default(omit) }}"
|
|
||||||
proto: "{{ item.proto | default(omit) }}"
|
|
||||||
from_ip: "{{ item.src | default(omit) }}"
|
|
||||||
to_ip: "{{ item.dest | default(omit) }}"
|
|
||||||
from_port: "{{ item.from_port | default(omit) }}"
|
|
||||||
direction: "{{ item.direction | default(omit) }}"
|
|
||||||
interface: "{{ item.interface | default(omit) }}"
|
|
||||||
interface_in: "{{ item.interface_in | default(omit) }}"
|
|
||||||
interface_out: "{{ item.interface_out | default(omit) }}"
|
|
||||||
route: "{{ item.route | default(omit) }}"
|
|
||||||
comment: "{{ item.comment | default(omit) }}"
|
|
||||||
loop: "{{ server_ufw_rules_effective }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.name | default(item.port) }}"
|
|
||||||
|
|
||||||
- name: Enable UFW firewall on server
|
|
||||||
tags: [services, packages]
|
|
||||||
community.general.ufw:
|
|
||||||
state: enabled
|
state: enabled
|
||||||
when: (server_ufw_rules_effective | default([])) | length > 0
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
loop: "{{ server_firewalld_services | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
when: server_firewall_backend == 'firewalld'
|
||||||
|
|
||||||
|
- name: Allow server ports through firewalld
|
||||||
|
tags: [services, packages]
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: "{{ item }}"
|
||||||
|
zone: "{{ server_firewalld_zone }}"
|
||||||
|
state: enabled
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
loop: "{{ server_firewalld_ports | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
when: server_firewall_backend == 'firewalld'
|
||||||
|
|
||||||
|
- name: Remove disabled server ports from firewalld
|
||||||
|
tags: [services, packages]
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: "{{ item }}"
|
||||||
|
zone: "{{ server_firewalld_zone }}"
|
||||||
|
state: disabled
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
loop: "{{ server_firewalld_absent_ports | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
when: server_firewall_backend == 'firewalld'
|
||||||
|
|||||||
@@ -85,6 +85,14 @@
|
|||||||
roles:
|
roles:
|
||||||
- profile_atlas
|
- profile_atlas
|
||||||
|
|
||||||
|
- name: Configure Rocky Linux server
|
||||||
|
hosts: rocky_server
|
||||||
|
become: true
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- dotfiles_common
|
||||||
|
- profile_server
|
||||||
|
|
||||||
- name: Configure personal workstation role on Fedora
|
- name: Configure personal workstation role on Fedora
|
||||||
hosts: platform_fedora:&role_personal_workstation
|
hosts: platform_fedora:&role_personal_workstation
|
||||||
become: true
|
become: true
|
||||||
@@ -112,12 +120,3 @@
|
|||||||
|
|
||||||
roles:
|
roles:
|
||||||
- profile_workstation_dev_wsl
|
- profile_workstation_dev_wsl
|
||||||
|
|
||||||
- name: Configure Ubuntu server
|
|
||||||
hosts: ubuntu_server
|
|
||||||
become: true
|
|
||||||
|
|
||||||
roles:
|
|
||||||
- packages_ubuntu
|
|
||||||
- services_systemd
|
|
||||||
- profile_server
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
{% set selinux_volume_option = server_compose_selinux_mount_option | default('') %}
|
||||||
version: "3.8"
|
version: "3.8"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -14,8 +15,8 @@ services:
|
|||||||
ND_ENABLETRANSCODING: "true"
|
ND_ENABLETRANSCODING: "true"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- "/opt/navidrome/data:/data"
|
- "/opt/navidrome/data:/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
- "/opt/music:/music:ro"
|
- "/opt/music:/music:ro{{ ',' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
user: "1000:1000"
|
user: "1000:1000"
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
@@ -30,8 +31,8 @@ services:
|
|||||||
- "443:443"
|
- "443:443"
|
||||||
- "81:81"
|
- "81:81"
|
||||||
volumes:
|
volumes:
|
||||||
- "/opt/npm/data:/data"
|
- "/opt/npm/data:/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
- "/opt/npm/letsencrypt:/etc/letsencrypt"
|
- "/opt/npm/letsencrypt:/etc/letsencrypt{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
- gitea
|
- gitea
|
||||||
@@ -66,7 +67,7 @@ services:
|
|||||||
POSTGRES_PASSWORD: "{{ vault_postgres_root_password }}"
|
POSTGRES_PASSWORD: "{{ vault_postgres_root_password }}"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- "/opt/postgres/data:/var/lib/postgresql/data"
|
- "/opt/postgres/data:/var/lib/postgresql/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
gitea:
|
gitea:
|
||||||
@@ -79,14 +80,15 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- gitea
|
- gitea
|
||||||
volumes:
|
volumes:
|
||||||
- /opt/gitea/data:/data
|
- /opt/gitea/data:/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}
|
||||||
- /etc/timezone:/etc/timezone:ro
|
- /etc/timezone:/etc/timezone:ro
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- /home/git/.ssh:/data/git/.ssh
|
- /home/git/.ssh:/data/git/.ssh{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
- "127.0.0.1:222:22"
|
- "127.0.0.1:222:22"
|
||||||
|
|
||||||
|
{% if server_syncthing_enabled | default(true) | bool %}
|
||||||
syncthing:
|
syncthing:
|
||||||
image: syncthing/syncthing:2
|
image: syncthing/syncthing:2
|
||||||
container_name: syncthing
|
container_name: syncthing
|
||||||
@@ -95,14 +97,15 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "8384"
|
- "8384"
|
||||||
volumes:
|
volumes:
|
||||||
- "/opt/syncthing/config:/var/syncthing"
|
- "/opt/syncthing/config:/var/syncthing{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
- "/srv/syncthing/data:/data"
|
- "/srv/syncthing/data:/data{{ ':' ~ selinux_volume_option if selinux_volume_option else '' }}"
|
||||||
ports:
|
ports:
|
||||||
- "22000:22000/tcp"
|
- "22000:22000/tcp"
|
||||||
- "22000:22000/udp"
|
- "22000:22000/udp"
|
||||||
- "21027:21027/udp"
|
- "21027:21027/udp"
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
web:
|
web:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Server
|
# Server
|
||||||
|
|
||||||
- Ubuntu minimal server
|
- Rocky Linux 9 server
|
||||||
- Self-hosting services
|
- Self-hosting services
|
||||||
- Low attack surface
|
- Low attack surface
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
case "$(uname -r 2>/dev/null)" in
|
|
||||||
*[Mm]icrosoft*) ;;
|
|
||||||
*) return ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
command -v gpgconf >/dev/null 2>&1 || return
|
|
||||||
|
|
||||||
if tty -s; then
|
|
||||||
export GPG_TTY="$(tty)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
gpgconf --launch gpg-agent >/dev/null 2>&1
|
|
||||||
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
|
|
||||||
|
|
||||||
if [ -n "${GPG_TTY-}" ]; then
|
|
||||||
gpg-connect-agent updatestartuptty /bye >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
164
scripts/migrate_prometheus_data.sh
Normal file
164
scripts/migrate_prometheus_data.sh
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Copy the persistent Docker data from the retired Ubuntu server to the Rocky
|
||||||
|
# replacement. Run this script on the Ubuntu source as root. It is a dry run
|
||||||
|
# unless --execute and --quiesce-source are both supplied. Extended attributes
|
||||||
|
# are deliberately not copied: Rocky must assign its own SELinux labels.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SOURCE_COMPOSE_FILE=/opt/docker/server/docker-compose.yml
|
||||||
|
DESTINATION=
|
||||||
|
IDENTITY_FILE=
|
||||||
|
EXECUTE=false
|
||||||
|
QUIESCE_SOURCE=false
|
||||||
|
|
||||||
|
DATA_PATHS='
|
||||||
|
/opt/navidrome/data
|
||||||
|
/opt/music
|
||||||
|
/opt/npm/data
|
||||||
|
/opt/npm/letsencrypt
|
||||||
|
/opt/postgres/data
|
||||||
|
/opt/gitea/data
|
||||||
|
'
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: sudo ./scripts/migrate_prometheus_data.sh --destination USER@HOST [options]
|
||||||
|
|
||||||
|
Copies persistent Navidrome, Nginx Proxy Manager, PostgreSQL and Gitea data to
|
||||||
|
the Rocky server with rsync. The destination Docker containers must be stopped.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--destination USER@HOST Rocky SSH destination (required).
|
||||||
|
--identity PATH SSH private key readable by root on the source host.
|
||||||
|
--source-compose PATH Source Compose file (default: /opt/docker/server/docker-compose.yml).
|
||||||
|
--quiesce-source Stop the source Compose stack before copying.
|
||||||
|
--execute Perform the transfer; otherwise only show changes.
|
||||||
|
-h, --help Show this help.
|
||||||
|
|
||||||
|
The script never deletes source data, destination-only files, containers, or
|
||||||
|
volumes. It intentionally excludes Syncthing and /home/git/.ssh.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf 'Error: %s\n' "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_command() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--destination)
|
||||||
|
[ "$#" -ge 2 ] || fail '--destination requires USER@HOST'
|
||||||
|
DESTINATION=$2
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--identity)
|
||||||
|
[ "$#" -ge 2 ] || fail '--identity requires a path'
|
||||||
|
IDENTITY_FILE=$2
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--source-compose)
|
||||||
|
[ "$#" -ge 2 ] || fail '--source-compose requires a path'
|
||||||
|
SOURCE_COMPOSE_FILE=$2
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--quiesce-source)
|
||||||
|
QUIESCE_SOURCE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--execute)
|
||||||
|
EXECUTE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
fail "unknown option: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ "$(id -u)" -eq 0 ] || fail 'run this script with sudo on the Ubuntu source host'
|
||||||
|
[ -n "$DESTINATION" ] || fail '--destination is required'
|
||||||
|
|
||||||
|
if [ -n "$IDENTITY_FILE" ]; then
|
||||||
|
[ -r "$IDENTITY_FILE" ] || fail "SSH identity is not readable: $IDENTITY_FILE"
|
||||||
|
case "$IDENTITY_FILE" in
|
||||||
|
*' '*|*"$(printf '\t')"*) fail 'SSH identity paths must not contain whitespace' ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$EXECUTE" = true ] && [ "$QUIESCE_SOURCE" != true ]; then
|
||||||
|
fail '--execute requires --quiesce-source to keep PostgreSQL data consistent'
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_command rsync
|
||||||
|
require_command ssh
|
||||||
|
|
||||||
|
SSH_COMMAND='ssh -o BatchMode=yes'
|
||||||
|
if [ -n "$IDENTITY_FILE" ]; then
|
||||||
|
SSH_COMMAND="$SSH_COMMAND -i $IDENTITY_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_ssh() {
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$SSH_COMMAND "$DESTINATION" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
printf 'Destination: %s\n' "$DESTINATION"
|
||||||
|
printf 'Mode: %s\n' "$( [ "$EXECUTE" = true ] && printf execute || printf dry-run )"
|
||||||
|
printf 'Data paths:\n%s\n' "$DATA_PATHS"
|
||||||
|
|
||||||
|
run_ssh 'sudo -n true' || fail 'destination sudo must be passwordless for this transfer'
|
||||||
|
run_ssh 'sudo -n docker info >/dev/null' \
|
||||||
|
|| fail 'destination Docker daemon is unavailable'
|
||||||
|
if run_ssh 'sudo -n docker ps -q | grep -q .'; then
|
||||||
|
fail 'destination Docker containers must be stopped before migration'
|
||||||
|
fi
|
||||||
|
|
||||||
|
for path in $DATA_PATHS; do
|
||||||
|
[ -d "$path" ] || fail "source directory is missing: $path"
|
||||||
|
run_ssh "sudo -n test -d $path" || fail "destination directory is missing: $path"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$QUIESCE_SOURCE" = true ]; then
|
||||||
|
require_command docker
|
||||||
|
[ -f "$SOURCE_COMPOSE_FILE" ] || fail "source Compose file is missing: $SOURCE_COMPOSE_FILE"
|
||||||
|
|
||||||
|
if [ "$EXECUTE" = true ]; then
|
||||||
|
printf 'Stopping source Compose stack...\n'
|
||||||
|
docker compose -f "$SOURCE_COMPOSE_FILE" stop
|
||||||
|
else
|
||||||
|
printf 'Dry-run: source Compose stack would be stopped.\n'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
for path in $DATA_PATHS; do
|
||||||
|
printf '\nSyncing %s\n' "$path"
|
||||||
|
if [ "$EXECUTE" = true ]; then
|
||||||
|
rsync -aHA --numeric-ids --itemize-changes --human-readable --partial \
|
||||||
|
--rsync-path='sudo -n rsync' -e "$SSH_COMMAND" "$path/" "$DESTINATION:$path/"
|
||||||
|
else
|
||||||
|
rsync -aHA --numeric-ids --itemize-changes --human-readable --partial --dry-run \
|
||||||
|
--rsync-path='sudo -n rsync' -e "$SSH_COMMAND" "$path/" "$DESTINATION:$path/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$EXECUTE" = true ]; then
|
||||||
|
printf '\nVerifying source-to-destination parity...\n'
|
||||||
|
for path in $DATA_PATHS; do
|
||||||
|
rsync -aHA --numeric-ids --itemize-changes --dry-run \
|
||||||
|
--rsync-path='sudo -n rsync' -e "$SSH_COMMAND" "$path/" "$DESTINATION:$path/"
|
||||||
|
done
|
||||||
|
printf '\nTransfer completed. Keep the source stack stopped until application validation on Rocky succeeds.\n'
|
||||||
|
else
|
||||||
|
printf '\nDry-run completed. Re-run with --quiesce-source --execute after reviewing the changes.\n'
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user