From 6ca021990269cf5b48f88e608811a2930b4588e0 Mon Sep 17 00:00:00 2001 From: Jeremie Fraeys Date: Fri, 23 Jan 2026 14:01:54 -0500 Subject: [PATCH] Deploy infra-controller to services server --- .env.example | 4 +- .forgejo/workflows/deploy.yml | 75 +++++++++++++++++--------- install.sh | 16 ++---- scripts/deploy-app | 2 - scripts/infra-deregister | 16 ------ scripts/infra-register-stdin | 24 --------- scripts/sync-infra | 23 -------- src/config.py | 15 +----- systemd/infra-controller-once.service | 4 +- systemd/infra-controller-watch.service | 29 ++++++++++ systemd/infra-controller.env | 10 ---- systemd/infra-controller.path | 11 ---- systemd/infra-controller.service | 4 +- 13 files changed, 88 insertions(+), 145 deletions(-) delete mode 100644 scripts/infra-deregister delete mode 100644 scripts/infra-register-stdin delete mode 100644 scripts/sync-infra create mode 100644 systemd/infra-controller-watch.service delete mode 100644 systemd/infra-controller.path diff --git a/.env.example b/.env.example index d1d48e1..61dd5b4 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1 @@ -CONFIG_PATH=/etc/infra-controller/config.yml -ACTIVE_APPS_DIR=/var/run/active-apps -LOG_LEVEL=INFO +CONFIG_PATH=/etc/infra-controller/config.toml diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 68d1d28..a374642 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -59,34 +59,59 @@ jobs: exit 1 } - # Deploy app locally on the runner host - - name: Deploy App (Docker Compose) - shell: bash - run: | - set -euo pipefail - APP_NAME="${{ github.event.repository.name }}" - APP_PATH="/srv/apps/$APP_NAME" - echo "Deploying $APP_NAME from $APP_PATH..." - if [[ ! -d "$APP_PATH" ]]; then - echo "Skipping deploy: $APP_PATH not found on this runner" - exit 0 - fi - cd "$APP_PATH" - docker compose pull - docker compose up -d - - # Register app on the services server (triggers infra-controller.path) - - name: Register App Requirements + # Deploy infra-controller to services server + - name: Deploy infra-controller (services server) shell: bash env: SERVICE_HOST: ${{ secrets.SERVICE_HOST }} SERVICE_USER: ${{ secrets.SERVICE_USER }} run: | set -euo pipefail - APP_NAME="${{ github.event.repository.name }}" - echo "Registering app $APP_NAME with infra-controller..." - if [[ -f .infra.toml ]]; then - ssh -i ~/.ssh/id_ed25519 "$SERVICE_USER@$SERVICE_HOST" infra-register-stdin "$APP_NAME" < .infra.toml - else - ssh -i ~/.ssh/id_ed25519 "$SERVICE_USER@$SERVICE_HOST" infra-deregister "$APP_NAME" - fi + + : "${SERVICE_HOST:?Missing secret SERVICE_HOST}" + : "${SERVICE_USER:?Missing secret SERVICE_USER}" + + GIT_SHA="${{ github.sha }}" + REPO_URL="$(git remote get-url origin)" + + echo "Deploying infra-controller ($GIT_SHA) to $SERVICE_USER@$SERVICE_HOST ..." + + ssh -i ~/.ssh/id_ed25519 "$SERVICE_USER@$SERVICE_HOST" /bin/sh -lc "set -euo pipefail + + sudo mkdir -p /opt/infra-controller /etc/infra-controller /var/lib/infra-controller /var/log/infra-controller + + if [ ! -d /opt/infra-controller/.git ]; then + sudo rm -rf /opt/infra-controller/* + sudo git clone '$REPO_URL' /opt/infra-controller + fi + + cd /opt/infra-controller + sudo git fetch --all --prune + sudo git checkout -f '$GIT_SHA' + + if [ ! -d /opt/infra-controller/venv ]; then + sudo python3 -m venv /opt/infra-controller/venv + fi + sudo /opt/infra-controller/venv/bin/pip install --upgrade pip + sudo /opt/infra-controller/venv/bin/pip install -e . + + if [ ! -f /etc/infra-controller/config.toml ]; then + sudo cp config/controller.toml.example /etc/infra-controller/config.toml + fi + if [ ! -f /etc/infra-controller/controller.env ]; then + sudo cp systemd/infra-controller.env /etc/infra-controller/controller.env + fi + + sudo cp systemd/infra-controller.service /etc/systemd/system/ + sudo cp systemd/infra-controller-once.service /etc/systemd/system/ + sudo cp systemd/infra-controller-watch.service /etc/systemd/system/ + sudo systemctl daemon-reload + + sudo systemctl disable --now infra-controller.path 2>/dev/null || true + sudo systemctl enable --now infra-controller-watch.service + sudo systemctl restart infra-controller-watch.service + + sudo chown -R infractl:infractl /opt/infra-controller /var/lib/infra-controller /var/log/infra-controller || true + + /opt/infra-controller/venv/bin/infra-controller --once + " diff --git a/install.sh b/install.sh index 644275e..7551df8 100644 --- a/install.sh +++ b/install.sh @@ -18,7 +18,6 @@ fi sudo mkdir -p /opt/infra-controller sudo mkdir -p /etc/infra-controller -sudo mkdir -p /var/run/active-apps sudo mkdir -p /var/lib/infra-controller sudo mkdir -p /var/log/infra-controller @@ -27,13 +26,6 @@ sudo python3 -m venv /opt/infra-controller/venv sudo /opt/infra-controller/venv/bin/pip install --upgrade pip sudo /opt/infra-controller/venv/bin/pip install -e . -echo "Installing helper scripts..." -sudo install -d /usr/local/sbin -sudo install -m 0755 scripts/deploy-app /usr/local/sbin/deploy-app -sudo install -m 0755 scripts/sync-infra /usr/local/sbin/sync-infra -sudo install -m 0755 scripts/infra-register-stdin /usr/local/sbin/infra-register-stdin -sudo install -m 0755 scripts/infra-deregister /usr/local/sbin/infra-deregister - if [ ! -f /etc/infra-controller/config.toml ]; then echo "Installing default configuration..." sudo cp config/controller.toml.example /etc/infra-controller/config.toml @@ -46,16 +38,14 @@ fi echo "Installing systemd service..." sudo cp systemd/infra-controller.service /etc/systemd/system/ sudo cp systemd/infra-controller-once.service /etc/systemd/system/ -sudo cp systemd/infra-controller.path /etc/systemd/system/ +sudo cp systemd/infra-controller-watch.service /etc/systemd/system/ sudo systemctl daemon-reload -echo "Enabling infra-controller path trigger..." -sudo systemctl enable infra-controller.path +echo "Enabling infra-controller watch service..." +sudo systemctl enable infra-controller-watch.service sudo chown -R infractl:infractl /opt/infra-controller sudo chown -R infractl:infractl /var/lib/infra-controller sudo chown -R infractl:infractl /var/log/infra-controller -sudo chown -R infractl:infractl /var/run/active-apps -sudo chmod 755 /var/run/active-apps echo "Installation complete!" diff --git a/scripts/deploy-app b/scripts/deploy-app index 7c87ee9..eb1686a 100644 --- a/scripts/deploy-app +++ b/scripts/deploy-app @@ -39,5 +39,3 @@ else echo "ERROR: deploy.sh missing or not executable" >&2 exit 1 fi - -/usr/local/sbin/sync-infra "$APP_NAME" "$APP_DIR" diff --git a/scripts/infra-deregister b/scripts/infra-deregister deleted file mode 100644 index af4f7d0..0000000 --- a/scripts/infra-deregister +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -APP_NAME="${1:-}" - -if [[ -z "$APP_NAME" ]]; then - echo "usage: infra-deregister " >&2 - exit 2 -fi - -if ! [[ "$APP_NAME" =~ ^[A-Za-z0-9._-]+$ ]]; then - echo "invalid app name: $APP_NAME" >&2 - exit 2 -fi - -rm -f "/var/run/active-apps/$APP_NAME.toml" "/var/run/active-apps/$APP_NAME.yml" "/var/run/active-apps/$APP_NAME.yaml" diff --git a/scripts/infra-register-stdin b/scripts/infra-register-stdin deleted file mode 100644 index efe96ce..0000000 --- a/scripts/infra-register-stdin +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -APP_NAME="${1:-}" - -if [[ -z "$APP_NAME" ]]; then - echo "usage: infra-register-stdin " >&2 - exit 2 -fi - -if ! [[ "$APP_NAME" =~ ^[A-Za-z0-9._-]+$ ]]; then - echo "invalid app name: $APP_NAME" >&2 - exit 2 -fi - -DST_DIR="/var/run/active-apps" -DST="$DST_DIR/$APP_NAME.toml" -TMP="$DST.toml.tmp" - -mkdir -p "$DST_DIR" - -cat > "$TMP" - -mv "$TMP" "$DST" diff --git a/scripts/sync-infra b/scripts/sync-infra deleted file mode 100644 index 2d3a68e..0000000 --- a/scripts/sync-infra +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -APP_NAME="${1:-}" -APP_DIR="${2:-}" - -if [[ -z "$APP_NAME" || -z "$APP_DIR" ]]; then - echo "usage: sync-infra " >&2 - exit 2 -fi - -if ! [[ "$APP_NAME" =~ ^[A-Za-z0-9._-]+$ ]]; then - echo "invalid app name: $APP_NAME" >&2 - exit 2 -fi - -INFRA_FILE="$APP_DIR/.infra.toml" - -if [[ -f "$INFRA_FILE" ]]; then - ssh infra@services-server infra-register-stdin "$APP_NAME" < "$INFRA_FILE" -else - ssh infra@services-server infra-deregister "$APP_NAME" -fi diff --git a/src/config.py b/src/config.py index e1ccf94..3c24e61 100644 --- a/src/config.py +++ b/src/config.py @@ -181,20 +181,7 @@ def parse_logging_config(config: ControllerConfig, data: Any) -> None: def apply_env_overrides(config: ControllerConfig) -> None: """Apply environment variable overrides to configuration.""" - if val := os.getenv("ACTIVE_APPS_DIR"): - config.discovery.file_based_path = Path(val) - if val := os.getenv("SCAN_PATHS"): - config.discovery.scan_paths = [Path(p.strip()) for p in val.split(",") if p.strip()] - if val := os.getenv("DOCKER_BASE_DIR"): - config.docker.base_dir = Path(val) - if val := os.getenv("DOCKER_COMPOSE_FILE"): - config.docker.compose_file = val - if val := os.getenv("CHECK_INTERVAL"): - config.services.check_interval_seconds = int(val) - if val := os.getenv("GRACE_PERIOD_MINUTES"): - config.services.grace_period_minutes = int(val) - if val := os.getenv("LOG_LEVEL"): - config.logging.level = val + return def load_config(config_path: str | os.PathLike[str] | None = None) -> ControllerConfig: diff --git a/systemd/infra-controller-once.service b/systemd/infra-controller-once.service index 199246a..cbb287d 100644 --- a/systemd/infra-controller-once.service +++ b/systemd/infra-controller-once.service @@ -16,8 +16,8 @@ WorkingDirectory=/opt/infra-controller NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/var/run/active-apps /var/lib/infra-controller +ProtectHome=read-only +ReadWritePaths=/var/lib/infra-controller /var/log/infra-controller CapabilityBoundingSet= ExecStart=/opt/infra-controller/venv/bin/infra-controller --once diff --git a/systemd/infra-controller-watch.service b/systemd/infra-controller-watch.service new file mode 100644 index 0000000..e76b255 --- /dev/null +++ b/systemd/infra-controller-watch.service @@ -0,0 +1,29 @@ +[Unit] +Description=Watch for .infra.* changes and run infra-controller +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=infractl +Group=infractl +EnvironmentFile=/etc/infra-controller/controller.env +Environment=PYTHONUNBUFFERED=1 +WorkingDirectory=/opt/infra-controller +ExecStart=/bin/sh -lc 'inotifywait -m -r -e create,modify,delete,move --format "%w%f" /home /opt/apps | while read -r p; do case "$p" in *"/.infra."*) /opt/infra-controller/venv/bin/infra-controller --once ;; esac; done' +Restart=always +RestartSec=2 + +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ProtectHome=read-only +ReadWritePaths=/var/lib/infra-controller /var/log/infra-controller +CapabilityBoundingSet= + +StandardOutput=journal +StandardError=journal +SyslogIdentifier=infra-controller + +[Install] +WantedBy=multi-user.target diff --git a/systemd/infra-controller.env b/systemd/infra-controller.env index 9ba26d4..61dd5b4 100644 --- a/systemd/infra-controller.env +++ b/systemd/infra-controller.env @@ -1,11 +1 @@ CONFIG_PATH=/etc/infra-controller/config.toml - -ACTIVE_APPS_DIR=/var/run/active-apps - -DOCKER_BASE_DIR=/opt -DOCKER_COMPOSE_FILE=docker-compose.yml - -CHECK_INTERVAL=60 -GRACE_PERIOD_MINUTES=15 - -LOG_LEVEL=INFO diff --git a/systemd/infra-controller.path b/systemd/infra-controller.path deleted file mode 100644 index 5bc8d37..0000000 --- a/systemd/infra-controller.path +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Run infra-controller when active app registrations change - -[Path] -PathExists=/var/run/active-apps -PathModified=/var/run/active-apps -DirectoryNotEmpty=/var/run/active-apps -Unit=infra-controller-once.service - -[Install] -WantedBy=multi-user.target diff --git a/systemd/infra-controller.service b/systemd/infra-controller.service index 47af5b9..3757690 100644 --- a/systemd/infra-controller.service +++ b/systemd/infra-controller.service @@ -18,8 +18,8 @@ WorkingDirectory=/opt/infra-controller NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/var/run/active-apps /var/lib/infra-controller +ProtectHome=read-only +ReadWritePaths=/var/lib/infra-controller /var/log/infra-controller CapabilityBoundingSet= # Execution