From 9a1ddd4bcfbcec93f619001a4b94dc873f5acdc2 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 21 Jan 2026 20:11:11 -0800 Subject: [PATCH 1/3] Remove unused ansible roles migrated to k8s Services devpi, kiwix, and transmission have moved to Kubernetes. Remove their ansible roles and related metrics roles: - devpi, devpi_metrics - kiwix - transmission, transmission_metrics - node_exporter (unused) - podman (unused) Co-Authored-By: Claude Opus 4.5 --- ansible/roles/devpi/defaults/main.yml | 7 - ansible/roles/devpi/handlers/main.yml | 6 - ansible/roles/devpi/tasks/main.yml | 55 -------- ansible/roles/devpi/templates/devpi.plist.j2 | 39 ------ ansible/roles/devpi_metrics/defaults/main.yml | 6 - ansible/roles/devpi_metrics/handlers/main.yml | 6 - ansible/roles/devpi_metrics/meta/main.yml | 4 - ansible/roles/devpi_metrics/tasks/main.yml | 37 ----- .../templates/devpi-metrics.plist.j2 | 21 --- .../templates/devpi-metrics.sh.j2 | 54 -------- ansible/roles/kiwix/defaults/main.yml | 131 ------------------ ansible/roles/kiwix/handlers/main.yml | 6 - ansible/roles/kiwix/meta/main.yml | 4 - ansible/roles/kiwix/tasks/main.yml | 119 ---------------- .../kiwix/templates/kiwix-serve.plist.j2 | 25 ---- .../kiwix/templates/kiwix-symlink-zims.sh.j2 | 50 ------- .../kiwix/templates/kiwix-sync-torrents.sh.j2 | 47 ------- .../kiwix/templates/kiwix-torrents.txt.j2 | 5 - ansible/roles/node_exporter/defaults/main.yml | 2 - ansible/roles/node_exporter/handlers/main.yml | 5 - ansible/roles/node_exporter/tasks/main.yml | 22 --- .../templates/node_exporter.args.j2 | 1 - ansible/roles/podman/handlers/main.yml | 3 - ansible/roles/podman/tasks/main.yml | 58 -------- ansible/roles/transmission/defaults/main.yml | 24 ---- ansible/roles/transmission/handlers/main.yml | 4 - ansible/roles/transmission/tasks/main.yml | 52 ------- .../transmission/templates/settings.json.j2 | 89 ------------ .../transmission_metrics/defaults/main.yml | 7 - .../transmission_metrics/handlers/main.yml | 6 - .../roles/transmission_metrics/meta/main.yml | 4 - .../roles/transmission_metrics/tasks/main.yml | 26 ---- .../templates/transmission-metrics.plist.j2 | 26 ---- .../templates/transmission-metrics.sh.j2 | 120 ---------------- 34 files changed, 1071 deletions(-) delete mode 100644 ansible/roles/devpi/defaults/main.yml delete mode 100644 ansible/roles/devpi/handlers/main.yml delete mode 100644 ansible/roles/devpi/tasks/main.yml delete mode 100644 ansible/roles/devpi/templates/devpi.plist.j2 delete mode 100644 ansible/roles/devpi_metrics/defaults/main.yml delete mode 100644 ansible/roles/devpi_metrics/handlers/main.yml delete mode 100644 ansible/roles/devpi_metrics/meta/main.yml delete mode 100644 ansible/roles/devpi_metrics/tasks/main.yml delete mode 100644 ansible/roles/devpi_metrics/templates/devpi-metrics.plist.j2 delete mode 100644 ansible/roles/devpi_metrics/templates/devpi-metrics.sh.j2 delete mode 100644 ansible/roles/kiwix/defaults/main.yml delete mode 100644 ansible/roles/kiwix/handlers/main.yml delete mode 100644 ansible/roles/kiwix/meta/main.yml delete mode 100644 ansible/roles/kiwix/tasks/main.yml delete mode 100644 ansible/roles/kiwix/templates/kiwix-serve.plist.j2 delete mode 100644 ansible/roles/kiwix/templates/kiwix-symlink-zims.sh.j2 delete mode 100644 ansible/roles/kiwix/templates/kiwix-sync-torrents.sh.j2 delete mode 100644 ansible/roles/kiwix/templates/kiwix-torrents.txt.j2 delete mode 100644 ansible/roles/node_exporter/defaults/main.yml delete mode 100644 ansible/roles/node_exporter/handlers/main.yml delete mode 100644 ansible/roles/node_exporter/tasks/main.yml delete mode 100644 ansible/roles/node_exporter/templates/node_exporter.args.j2 delete mode 100644 ansible/roles/podman/handlers/main.yml delete mode 100644 ansible/roles/podman/tasks/main.yml delete mode 100644 ansible/roles/transmission/defaults/main.yml delete mode 100644 ansible/roles/transmission/handlers/main.yml delete mode 100644 ansible/roles/transmission/tasks/main.yml delete mode 100644 ansible/roles/transmission/templates/settings.json.j2 delete mode 100644 ansible/roles/transmission_metrics/defaults/main.yml delete mode 100644 ansible/roles/transmission_metrics/handlers/main.yml delete mode 100644 ansible/roles/transmission_metrics/meta/main.yml delete mode 100644 ansible/roles/transmission_metrics/tasks/main.yml delete mode 100644 ansible/roles/transmission_metrics/templates/transmission-metrics.plist.j2 delete mode 100644 ansible/roles/transmission_metrics/templates/transmission-metrics.sh.j2 diff --git a/ansible/roles/devpi/defaults/main.yml b/ansible/roles/devpi/defaults/main.yml deleted file mode 100644 index 0fc0569..0000000 --- a/ansible/roles/devpi/defaults/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -devpi_port: 3141 -devpi_serverdir: /Users/erichblume/devpi -devpi_log_dir: /Users/erichblume/Library/Logs -devpi_host: 0.0.0.0 # Listen on all interfaces for Tailscale -devpi_outside_url: https://pypi.tail8d86e.ts.net # URL for Tailscale proxy -devpi_secretfile: /Users/erichblume/devpi/.secret # Persistent auth secret diff --git a/ansible/roles/devpi/handlers/main.yml b/ansible/roles/devpi/handlers/main.yml deleted file mode 100644 index 7e29c43..0000000 --- a/ansible/roles/devpi/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: Reload devpi - ansible.builtin.shell: | - launchctl unload ~/Library/LaunchAgents/mcquack.eblume.devpi.plist 2>/dev/null || true - launchctl load ~/Library/LaunchAgents/mcquack.eblume.devpi.plist - changed_when: true diff --git a/ansible/roles/devpi/tasks/main.yml b/ansible/roles/devpi/tasks/main.yml deleted file mode 100644 index 2c6d03e..0000000 --- a/ansible/roles/devpi/tasks/main.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -# Note: devpi is installed via mise (pipx/uvx), not managed here. -# -# ONE-TIME SETUP (before running ansible): -# -# 1. Add to ~/.config/mise/config.toml on indri: -# -# [tools] -# "pipx:devpi-server" = { version = "latest", uvx = "true", uvx_args = "--with devpi-web" } -# "pipx:devpi-client" = { version = "latest", uvx = "true" } -# -# 2. Install: mise install -# -# 3. Initialize with root password (generate password in 1password): -# mise x -- devpi-init --serverdir {{ devpi_serverdir }} --root-passwd YOUR_PASSWORD -# -# 4. Run ansible to deploy LaunchAgent -# -# 5. Set up Tailscale service (see management log) - -- name: Ensure devpi data directory exists - ansible.builtin.file: - path: "{{ devpi_serverdir }}" - state: directory - mode: '0755' - -- name: Generate devpi secret file if not exists - ansible.builtin.shell: | - openssl rand -hex 32 > "{{ devpi_secretfile }}" - args: - creates: "{{ devpi_secretfile }}" - -- name: Ensure devpi secret file has secure permissions - ansible.builtin.file: - path: "{{ devpi_secretfile }}" - mode: '0600' - -- name: Deploy devpi LaunchAgent plist - ansible.builtin.template: - src: devpi.plist.j2 - dest: ~/Library/LaunchAgents/mcquack.eblume.devpi.plist - mode: '0644' - notify: Reload devpi - -- name: Check if devpi LaunchAgent is loaded - ansible.builtin.command: launchctl list mcquack.eblume.devpi - register: devpi_launchctl_check - changed_when: false - failed_when: false - -- name: Load devpi LaunchAgent if not loaded - ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.devpi.plist - when: devpi_launchctl_check.rc != 0 - changed_when: true - failed_when: false diff --git a/ansible/roles/devpi/templates/devpi.plist.j2 b/ansible/roles/devpi/templates/devpi.plist.j2 deleted file mode 100644 index b2ed6aa..0000000 --- a/ansible/roles/devpi/templates/devpi.plist.j2 +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - KeepAlive - - Label - mcquack.eblume.devpi - EnvironmentVariables - - PATH - /opt/homebrew/bin:/usr/bin:/bin - - ProgramArguments - - /opt/homebrew/opt/mise/bin/mise - x - -- - devpi-server - --serverdir - {{ devpi_serverdir }} - --host - {{ devpi_host }} - --port - {{ devpi_port }} - --outside-url - {{ devpi_outside_url }} - --secretfile - {{ devpi_secretfile }} - - RunAtLoad - - StandardErrorPath - {{ devpi_log_dir }}/mcquack.devpi.err.log - StandardOutPath - {{ devpi_log_dir }}/mcquack.devpi.out.log - - diff --git a/ansible/roles/devpi_metrics/defaults/main.yml b/ansible/roles/devpi_metrics/defaults/main.yml deleted file mode 100644 index d1aa18b..0000000 --- a/ansible/roles/devpi_metrics/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -devpi_metrics_url: http://localhost:3141/+status -devpi_metrics_dir: /opt/homebrew/var/node_exporter/textfile -devpi_metrics_script: /Users/erichblume/bin/devpi-metrics -devpi_metrics_interval: 60 # seconds between metric collection -devpi_metrics_log_dir: /opt/homebrew/var/log diff --git a/ansible/roles/devpi_metrics/handlers/main.yml b/ansible/roles/devpi_metrics/handlers/main.yml deleted file mode 100644 index 81bfd1d..0000000 --- a/ansible/roles/devpi_metrics/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: Reload devpi-metrics - ansible.builtin.shell: | - launchctl unload ~/Library/LaunchAgents/mcquack.eblume.devpi-metrics.plist 2>/dev/null || true - launchctl load ~/Library/LaunchAgents/mcquack.eblume.devpi-metrics.plist - changed_when: true diff --git a/ansible/roles/devpi_metrics/meta/main.yml b/ansible/roles/devpi_metrics/meta/main.yml deleted file mode 100644 index b05a43b..0000000 --- a/ansible/roles/devpi_metrics/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# Role ordering is controlled by indri.yml playbook - do not add dependencies here -# (Ansible's tag accumulation prevents proper deduplication when using meta dependencies) -dependencies: [] diff --git a/ansible/roles/devpi_metrics/tasks/main.yml b/ansible/roles/devpi_metrics/tasks/main.yml deleted file mode 100644 index b267648..0000000 --- a/ansible/roles/devpi_metrics/tasks/main.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -- name: Ensure metrics directory exists - ansible.builtin.file: - path: "{{ devpi_metrics_dir }}" - state: directory - mode: '0755' - -- name: Ensure log directory exists - ansible.builtin.file: - path: "{{ devpi_metrics_log_dir }}" - state: directory - mode: '0755' - -- name: Deploy devpi-metrics script - ansible.builtin.template: - src: devpi-metrics.sh.j2 - dest: "{{ devpi_metrics_script }}" - mode: '0755' - -- name: Deploy devpi-metrics LaunchAgent plist - ansible.builtin.template: - src: devpi-metrics.plist.j2 - dest: ~/Library/LaunchAgents/mcquack.eblume.devpi-metrics.plist - mode: '0644' - notify: Reload devpi-metrics - -- name: Check if devpi-metrics LaunchAgent is loaded - ansible.builtin.command: launchctl list mcquack.eblume.devpi-metrics - register: devpi_metrics_launchctl_check - changed_when: false - failed_when: false - -- name: Load devpi-metrics LaunchAgent if not loaded - ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.devpi-metrics.plist - when: devpi_metrics_launchctl_check.rc != 0 - changed_when: true - failed_when: false diff --git a/ansible/roles/devpi_metrics/templates/devpi-metrics.plist.j2 b/ansible/roles/devpi_metrics/templates/devpi-metrics.plist.j2 deleted file mode 100644 index a8141df..0000000 --- a/ansible/roles/devpi_metrics/templates/devpi-metrics.plist.j2 +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - Label - mcquack.eblume.devpi-metrics - ProgramArguments - - {{ devpi_metrics_script }} - - StartInterval - {{ devpi_metrics_interval }} - RunAtLoad - - StandardErrorPath - {{ devpi_metrics_log_dir }}/mcquack.devpi-metrics.err.log - StandardOutPath - {{ devpi_metrics_log_dir }}/mcquack.devpi-metrics.out.log - - diff --git a/ansible/roles/devpi_metrics/templates/devpi-metrics.sh.j2 b/ansible/roles/devpi_metrics/templates/devpi-metrics.sh.j2 deleted file mode 100644 index 2a1141d..0000000 --- a/ansible/roles/devpi_metrics/templates/devpi-metrics.sh.j2 +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# {{ ansible_managed }} -# Collects devpi-server metrics for node_exporter textfile collector - -set -euo pipefail - -STATUS_URL="{{ devpi_metrics_url }}" -OUTPUT_FILE="{{ devpi_metrics_dir }}/devpi.prom" -TEMP_FILE="${OUTPUT_FILE}.tmp" - -# Fetch status JSON -status_json=$(curl -s -H "Accept: application/json" "$STATUS_URL" 2>/dev/null) - -if [ -z "$status_json" ] || ! echo "$status_json" | jq -e '.result' >/dev/null 2>&1; then - echo "Failed to fetch devpi status" >&2 - exit 1 -fi - -# Start output file -cat > "$TEMP_FILE" << 'HEADER' -# HELP devpi_up devpi-server is up and responding -# TYPE devpi_up gauge -devpi_up 1 - -HEADER - -# Extract serial number using jq -serial=$(echo "$status_json" | jq -r '.result.serial // empty') -if [ -n "$serial" ]; then - cat >> "$TEMP_FILE" << EOF -# HELP devpi_serial Current changelog serial number -# TYPE devpi_serial gauge -devpi_serial $serial - -EOF -fi - -# Parse metrics array using jq - format is ["name", "type", value] -echo "$status_json" | jq -r '.result.metrics[]? | @json' | while read -r metric_json; do - name=$(echo "$metric_json" | jq -r '.[0]') - type=$(echo "$metric_json" | jq -r '.[1]') - value=$(echo "$metric_json" | jq -r '.[2]') - - # Write metric in Prometheus format - cat >> "$TEMP_FILE" << EOF -# HELP $name devpi metric -# TYPE $name $type -$name $value - -EOF -done - -# Atomic move -mv "$TEMP_FILE" "$OUTPUT_FILE" diff --git a/ansible/roles/kiwix/defaults/main.yml b/ansible/roles/kiwix/defaults/main.yml deleted file mode 100644 index 41f3abb..0000000 --- a/ansible/roles/kiwix/defaults/main.yml +++ /dev/null @@ -1,131 +0,0 @@ ---- -kiwix_serve_bin: /Users/erichblume/code/3rd/kiwix-tools/kiwix-serve -kiwix_zim_dir: /Users/erichblume/code/3rd/kiwix-tools -kiwix_bin_dir: /Users/erichblume/.local/bin -kiwix_port: 5501 -kiwix_log_dir: /Users/erichblume/Library/Logs - -# Transmission integration -# When enabled, ZIM archives are downloaded via BitTorrent instead of direct HTTP -kiwix_use_transmission: true -kiwix_torrent_base_url: "https://download.kiwix.org/zim" - -# ZIM archives to download and serve -# Each item needs: category, filename -# Torrent URL: {{ kiwix_torrent_base_url }}/{{ category }}/{{ filename }}.torrent -kiwix_zim_archives: - # Wikipedia - Top 1M articles with images (43G) - - category: wikipedia - filename: wikipedia_en_top1m_maxi_2025-09.zim - - ## Other Wikipedia options: - # - category: wikipedia - # filename: wikipedia_en_all_maxi_2025-08.zim # 111G - Full English Wikipedia - # - category: wikipedia - # filename: wikipedia_en_top_maxi_2025-12.zim # 7.6G - Top 100K articles - - # Project Gutenberg - Public domain books (72G) - - category: gutenberg - filename: gutenberg_en_all_2023-08.zim - - ## Newer Gutenberg (much larger, unclear why): - # - category: gutenberg - # filename: gutenberg_en_all_2025-11.zim # 206G - Full collection (2025) - - # iFixit - Repair guides (3.3G) - - category: ifixit - filename: ifixit_en_all_2025-12.zim - - # Stack Exchange - - category: stack_exchange - filename: superuser.com_en_all_2025-12.zim # 3.7G - # - category: stack_exchange - # filename: serverfault.com_en_all_2025-12.zim # 1.5G - # - category: stack_exchange - # filename: askubuntu.com_en_all_2025-12.zim # 2.6G - # - category: stack_exchange - # filename: unix.stackexchange.com_en_all_2025-12.zim # 1.2G - - category: stack_exchange - filename: math.stackexchange.com_en_all_2025-12.zim # 6.9G - # - category: stack_exchange - # filename: stackoverflow.com_en_all_2023-11.zim # 75G - Full StackOverflow - - # LibreTexts - Open educational resources - - category: libretexts - filename: libretexts.org_en_bio_2025-01.zim # 2.1G - - category: libretexts - filename: libretexts.org_en_chem_2025-01.zim # 2.0G - - category: libretexts - filename: libretexts.org_en_eng_2025-01.zim # 647M - - category: libretexts - filename: libretexts.org_en_math_2025-01.zim # 744M - - category: libretexts - filename: libretexts.org_en_phys_2025-01.zim # 464M - - category: libretexts - filename: libretexts.org_en_human_2025-01.zim # 3.5G - - # DevDocs - Programming documentation - - category: devdocs - filename: devdocs_en_bash_2026-01.zim - - category: devdocs - filename: devdocs_en_c_2026-01.zim - - category: devdocs - filename: devdocs_en_click_2026-01.zim - - category: devdocs - filename: devdocs_en_cmake_2026-01.zim - - category: devdocs - filename: devdocs_en_cpp_2026-01.zim - - category: devdocs - filename: devdocs_en_css_2026-01.zim - - category: devdocs - filename: devdocs_en_django-rest-framework_2026-01.zim - - category: devdocs - filename: devdocs_en_django_2026-01.zim - - category: devdocs - filename: devdocs_en_docker_2026-01.zim - - category: devdocs - filename: devdocs_en_duckdb_2026-01.zim - - category: devdocs - filename: devdocs_en_fish_2026-01.zim - - category: devdocs - filename: devdocs_en_gcc_2026-01.zim - - category: devdocs - filename: devdocs_en_git_2026-01.zim - - category: devdocs - filename: devdocs_en_go_2026-01.zim - - category: devdocs - filename: devdocs_en_godot_2026-01.zim - - category: devdocs - filename: devdocs_en_hammerspoon_2026-01.zim - - category: devdocs - filename: devdocs_en_homebrew_2026-01.zim - - category: devdocs - filename: devdocs_en_javascript_2026-01.zim - - category: devdocs - filename: devdocs_en_kubectl_2026-01.zim - - category: devdocs - filename: devdocs_en_kubernetes_2026-01.zim - - category: devdocs - filename: devdocs_en_latex_2026-01.zim - - category: devdocs - filename: devdocs_en_lua_2026-01.zim - - category: devdocs - filename: devdocs_en_markdown_2026-01.zim - - category: devdocs - filename: devdocs_en_nginx_2026-01.zim - - category: devdocs - filename: devdocs_en_nix_2026-01.zim - - category: devdocs - filename: devdocs_en_postgresql_2026-01.zim - - category: devdocs - filename: devdocs_en_python_2026-01.zim - - category: devdocs - filename: devdocs_en_redis_2026-01.zim - - category: devdocs - filename: devdocs_en_sqlite_2026-01.zim - - category: devdocs - filename: devdocs_en_typescript_2026-01.zim - - category: devdocs - filename: devdocs_en_werkzeug_2026-01.zim - - category: devdocs - filename: devdocs_en_zig_2026-01.zim diff --git a/ansible/roles/kiwix/handlers/main.yml b/ansible/roles/kiwix/handlers/main.yml deleted file mode 100644 index ef6e408..0000000 --- a/ansible/roles/kiwix/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: Restart kiwix-serve - ansible.builtin.shell: | - launchctl unload ~/Library/LaunchAgents/mcquack.eblume.kiwix-serve.plist 2>/dev/null || true - launchctl load ~/Library/LaunchAgents/mcquack.eblume.kiwix-serve.plist - changed_when: true diff --git a/ansible/roles/kiwix/meta/main.yml b/ansible/roles/kiwix/meta/main.yml deleted file mode 100644 index b05a43b..0000000 --- a/ansible/roles/kiwix/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# Role ordering is controlled by indri.yml playbook - do not add dependencies here -# (Ansible's tag accumulation prevents proper deduplication when using meta dependencies) -dependencies: [] diff --git a/ansible/roles/kiwix/tasks/main.yml b/ansible/roles/kiwix/tasks/main.yml deleted file mode 100644 index 85a09a2..0000000 --- a/ansible/roles/kiwix/tasks/main.yml +++ /dev/null @@ -1,119 +0,0 @@ ---- -- name: Ensure kiwix ZIM directory exists - ansible.builtin.file: - path: "{{ kiwix_zim_dir }}" - state: directory - mode: '0755' - -- name: Ensure kiwix bin directory exists - ansible.builtin.file: - path: "{{ kiwix_bin_dir }}" - state: directory - mode: '0755' - -# --- Deploy management scripts --- -- name: Deploy kiwix torrent sync script - ansible.builtin.template: - src: kiwix-sync-torrents.sh.j2 - dest: "{{ kiwix_bin_dir }}/kiwix-sync-torrents.sh" - mode: '0755' - when: kiwix_use_transmission - -- name: Deploy kiwix symlink script - ansible.builtin.template: - src: kiwix-symlink-zims.sh.j2 - dest: "{{ kiwix_bin_dir }}/kiwix-symlink-zims.sh" - mode: '0755' - when: kiwix_use_transmission - -- name: Deploy kiwix torrent list - ansible.builtin.template: - src: kiwix-torrents.txt.j2 - dest: "{{ kiwix_bin_dir }}/kiwix-torrents.txt" - mode: '0644' - when: kiwix_use_transmission - -# --- Transmission-based torrent management --- -- name: Check transmission daemon is responding - ansible.builtin.command: transmission-remote -l - register: kiwix_transmission_check - changed_when: false - failed_when: false - when: kiwix_use_transmission - -- name: Fail if transmission is not running - ansible.builtin.fail: - msg: "Transmission daemon is not responding. Ensure transmission role ran successfully." - when: kiwix_use_transmission and kiwix_transmission_check.rc != 0 - -- name: Sync ZIM torrents to transmission - ansible.builtin.command: "{{ kiwix_bin_dir }}/kiwix-sync-torrents.sh {{ kiwix_bin_dir }}/kiwix-torrents.txt" - register: kiwix_torrent_sync - changed_when: "'Added:' in kiwix_torrent_sync.stdout" - when: kiwix_use_transmission - -# --- Symlink completed ZIM files --- -- name: Symlink completed ZIM files to kiwix directory - ansible.builtin.command: "{{ kiwix_bin_dir }}/kiwix-symlink-zims.sh {{ transmission_download_dir }} {{ kiwix_zim_dir }}" - register: kiwix_symlink_result - changed_when: "'Linked:' in kiwix_symlink_result.stdout" - when: kiwix_use_transmission - notify: Restart kiwix-serve - -# --- Fallback: Direct HTTP download (original behavior) --- -- name: Check which ZIM archives exist (direct download mode) - ansible.builtin.stat: - path: "{{ kiwix_zim_dir }}/{{ item.filename }}" - get_checksum: false - loop: "{{ kiwix_zim_archives }}" - loop_control: - label: "{{ item.filename }}" - register: kiwix_zim_stat - when: not kiwix_use_transmission - -- name: Download missing ZIM archives (direct download mode) - ansible.builtin.get_url: - url: "https://download.kiwix.org/zim/{{ item.item.category }}/{{ item.item.filename }}" - dest: "{{ kiwix_zim_dir }}/{{ item.item.filename }}" - mode: '0644' - timeout: 3600 - loop: "{{ kiwix_zim_stat.results | default([]) }}" - loop_control: - label: "{{ item.item.filename | default('unknown') }}" - when: - - not kiwix_use_transmission - - item.stat is defined - - not item.stat.exists - notify: Restart kiwix-serve - -# --- Determine which archives are available --- -- name: Find available ZIM archives in kiwix directory - ansible.builtin.find: - paths: "{{ kiwix_zim_dir }}" - patterns: "*.zim" - file_type: any # includes symlinks - register: kiwix_available_zim_files - -- name: Build list of available archive filenames - ansible.builtin.set_fact: - kiwix_available_archives: "{{ kiwix_available_zim_files.files | map(attribute='path') | map('basename') | list }}" - -# --- LaunchAgent deployment --- -- name: Deploy kiwix-serve LaunchAgent plist - ansible.builtin.template: - src: kiwix-serve.plist.j2 - dest: ~/Library/LaunchAgents/mcquack.eblume.kiwix-serve.plist - mode: '0644' - notify: Restart kiwix-serve - -- name: Check if kiwix-serve LaunchAgent is loaded - ansible.builtin.command: launchctl list mcquack.eblume.kiwix-serve - register: kiwix_launchctl_check - changed_when: false - failed_when: false - -- name: Load kiwix-serve LaunchAgent if not loaded - ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.kiwix-serve.plist - when: kiwix_launchctl_check.rc != 0 - changed_when: true - failed_when: false diff --git a/ansible/roles/kiwix/templates/kiwix-serve.plist.j2 b/ansible/roles/kiwix/templates/kiwix-serve.plist.j2 deleted file mode 100644 index 26a61d1..0000000 --- a/ansible/roles/kiwix/templates/kiwix-serve.plist.j2 +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - KeepAlive - - Label - mcquack.eblume.kiwix-serve - ProgramArguments - - {{ kiwix_serve_bin }} - --port={{ kiwix_port }} -{% for filename in kiwix_available_archives %} - {{ kiwix_zim_dir }}/{{ filename }} -{% endfor %} - - RunAtLoad - - StandardErrorPath - {{ kiwix_log_dir }}/mcquack.kiwix-serve.err.log - StandardOutPath - {{ kiwix_log_dir }}/mcquack.kiwix-serve.out.log - - diff --git a/ansible/roles/kiwix/templates/kiwix-symlink-zims.sh.j2 b/ansible/roles/kiwix/templates/kiwix-symlink-zims.sh.j2 deleted file mode 100644 index 29e7630..0000000 --- a/ansible/roles/kiwix/templates/kiwix-symlink-zims.sh.j2 +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Symlink completed ZIM files from download directory to kiwix directory -set -euo pipefail - -SOURCE_DIR="${1:-}" -TARGET_DIR="${2:-}" - -if [[ -z "$SOURCE_DIR" || -z "$TARGET_DIR" ]]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -if [[ ! -d "$SOURCE_DIR" ]]; then - echo "Error: Source directory not found: $SOURCE_DIR" >&2 - exit 1 -fi - -if [[ ! -d "$TARGET_DIR" ]]; then - echo "Error: Target directory not found: $TARGET_DIR" >&2 - exit 1 -fi - -created=0 -skipped=0 - -# Find all .zim files in source directory -for zim_file in "$SOURCE_DIR"/*.zim; do - # Handle case where no .zim files exist - [[ -e "$zim_file" ]] || continue - - filename=$(basename "$zim_file") - target_path="$TARGET_DIR/$filename" - - if [[ -e "$target_path" || -L "$target_path" ]]; then - ((skipped++)) || true - else - ln -s "$zim_file" "$target_path" - echo "Linked: $filename" - ((created++)) || true - fi -done - -echo "Symlink complete: $created created, $skipped already present" - -# Exit with special code if new symlinks were created (for ansible changed detection) -if [[ $created -gt 0 ]]; then - exit 0 -else - exit 0 -fi diff --git a/ansible/roles/kiwix/templates/kiwix-sync-torrents.sh.j2 b/ansible/roles/kiwix/templates/kiwix-sync-torrents.sh.j2 deleted file mode 100644 index 4a293b3..0000000 --- a/ansible/roles/kiwix/templates/kiwix-sync-torrents.sh.j2 +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Sync ZIM archive torrents to transmission -# Reads torrent URLs from stdin or file, adds any missing to transmission -set -euo pipefail - -TORRENT_LIST="${1:-}" - -if [[ -z "$TORRENT_LIST" ]]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -if [[ ! -f "$TORRENT_LIST" ]]; then - echo "Error: Torrent list file not found: $TORRENT_LIST" >&2 - exit 1 -fi - -# Get current torrents from transmission (extract names, skip header/footer) -# Note: Use sed '$d' instead of head -n -1 for macOS compatibility -current_torrents=$(transmission-remote -l 2>/dev/null | tail -n +2 | sed '$d' | awk '{print $NF}' || true) - -added=0 -skipped=0 - -while IFS= read -r torrent_url || [[ -n "$torrent_url" ]]; do - # Skip empty lines and comments - [[ -z "$torrent_url" || "$torrent_url" =~ ^# ]] && continue - - # Extract base name from URL (remove .torrent extension and path) - base_name=$(basename "$torrent_url" .torrent) - # Also try without .zim in case transmission reports it differently - base_without_zim="${base_name%.zim}" - - # Check if already in transmission - if echo "$current_torrents" | grep -qF "$base_without_zim"; then - ((skipped++)) || true - else - if transmission-remote -a "$torrent_url" 2>/dev/null; then - echo "Added: $base_name" - ((added++)) || true - else - echo "Warning: Failed to add $torrent_url" >&2 - fi - fi -done < "$TORRENT_LIST" - -echo "Sync complete: $added added, $skipped already present" diff --git a/ansible/roles/kiwix/templates/kiwix-torrents.txt.j2 b/ansible/roles/kiwix/templates/kiwix-torrents.txt.j2 deleted file mode 100644 index fcc4b4e..0000000 --- a/ansible/roles/kiwix/templates/kiwix-torrents.txt.j2 +++ /dev/null @@ -1,5 +0,0 @@ -# ZIM archive torrent URLs for kiwix -# Generated by ansible - do not edit manually -{% for archive in kiwix_zim_archives %} -{{ kiwix_torrent_base_url }}/{{ archive.category }}/{{ archive.filename }}.torrent -{% endfor %} diff --git a/ansible/roles/node_exporter/defaults/main.yml b/ansible/roles/node_exporter/defaults/main.yml deleted file mode 100644 index a8f3e63..0000000 --- a/ansible/roles/node_exporter/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -node_exporter_textfile_dir: /opt/homebrew/var/node_exporter/textfile diff --git a/ansible/roles/node_exporter/handlers/main.yml b/ansible/roles/node_exporter/handlers/main.yml deleted file mode 100644 index fa0c59b..0000000 --- a/ansible/roles/node_exporter/handlers/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: Restart node_exporter - ansible.builtin.command: brew services restart node_exporter - listen: Restart node_exporter - changed_when: true diff --git a/ansible/roles/node_exporter/tasks/main.yml b/ansible/roles/node_exporter/tasks/main.yml deleted file mode 100644 index 55c0398..0000000 --- a/ansible/roles/node_exporter/tasks/main.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -# Note: node_exporter is installed via homebrew manually. -# This role manages the args file to enable textfile collector. - -- name: Create textfile collector directory - ansible.builtin.file: - path: "{{ node_exporter_textfile_dir }}" - state: directory - mode: '0755' - -- name: Configure node_exporter args - ansible.builtin.template: - src: node_exporter.args.j2 - dest: /opt/homebrew/etc/node_exporter.args - mode: '0644' - notify: Restart node_exporter - -- name: Ensure node_exporter service is started - ansible.builtin.command: brew services start node_exporter - register: node_exporter_brew_start - changed_when: "'Successfully started' in node_exporter_brew_start.stdout" - failed_when: false diff --git a/ansible/roles/node_exporter/templates/node_exporter.args.j2 b/ansible/roles/node_exporter/templates/node_exporter.args.j2 deleted file mode 100644 index be763d0..0000000 --- a/ansible/roles/node_exporter/templates/node_exporter.args.j2 +++ /dev/null @@ -1 +0,0 @@ ---collector.textfile.directory={{ node_exporter_textfile_dir }} diff --git a/ansible/roles/podman/handlers/main.yml b/ansible/roles/podman/handlers/main.yml deleted file mode 100644 index 89a6a94..0000000 --- a/ansible/roles/podman/handlers/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -# No handlers currently - podman machine start is unreliable via Ansible -# See known issue in tasks/main.yml diff --git a/ansible/roles/podman/tasks/main.yml b/ansible/roles/podman/tasks/main.yml deleted file mode 100644 index e1bb78f..0000000 --- a/ansible/roles/podman/tasks/main.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- -# Podman installation and machine setup for indri -# Used as container runtime for minikube -# -# KNOWN ISSUE: podman machine init/start has reliability issues when run via -# Ansible/SSH. The machine sometimes gets stuck in "Starting" state due to a -# race condition (see https://github.com/containers/podman/issues/16945). -# Additionally, Apple Hypervisor may require GUI session context. -# -# WORKAROUND: If the machine fails to start via Ansible, manually run on indri: -# podman machine rm -f podman-machine-default -# podman machine init --cpus 4 --memory 8192 --disk-size 220 -# podman machine start -# -# TODO: Investigate proper LaunchAgent or other solution for reliable automation. - -- name: Install podman via homebrew - community.general.homebrew: - name: podman - state: present - -- name: Check if podman machine exists - ansible.builtin.command: - cmd: podman machine list --format json - register: podman_machine_list - changed_when: false - check_mode: false # Safe to run in check mode - read-only - -- name: Initialize podman machine (if not exists) - ansible.builtin.command: - cmd: podman machine init --cpus 4 --memory 8192 --disk-size 220 - register: podman_init - changed_when: podman_init.rc == 0 - failed_when: podman_init.rc not in [0, 125] # 125 = already exists - when: podman_machine_list.stdout == '[]' - -- name: Check if podman machine is running - ansible.builtin.command: - cmd: podman machine list --format "{{ '{{' }}.Running{{ '}}' }}" - register: podman_running - changed_when: false - check_mode: false # Safe to run in check mode - read-only - -- name: Start podman machine (if stopped) - ansible.builtin.command: - cmd: podman machine start - register: podman_start - changed_when: "'started successfully' in podman_start.stdout" - failed_when: false # Don't fail - see known issue above - when: "'true' not in podman_running.stdout" - -- name: Warn if podman machine failed to start - ansible.builtin.debug: - msg: "WARNING: podman machine may not have started. Run 'podman machine start' manually on indri if needed." - when: - - "'true' not in podman_running.stdout" - - podman_start is defined - - podman_start.rc != 0 or "'started successfully' not in podman_start.stdout" diff --git a/ansible/roles/transmission/defaults/main.yml b/ansible/roles/transmission/defaults/main.yml deleted file mode 100644 index 6eb9391..0000000 --- a/ansible/roles/transmission/defaults/main.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -# Homebrew's transmission-cli service uses this config directory -transmission_config_dir: /opt/homebrew/var/transmission - -# Download directories -transmission_download_dir: /Users/erichblume/transmission -transmission_incomplete_dir: /Users/erichblume/transmission/.incomplete - -# RPC settings (local only - no authentication needed) -transmission_rpc_enabled: true -transmission_rpc_port: 9091 -transmission_rpc_bind_address: "127.0.0.1" -transmission_rpc_authentication_required: false -transmission_rpc_whitelist_enabled: true -transmission_rpc_whitelist: "127.0.0.1" - -# Speed limits (KB/s, 0 = unlimited) -transmission_speed_limit_down: 0 -transmission_speed_limit_up: 100 - -# P2P settings -transmission_dht_enabled: true -transmission_pex_enabled: true -transmission_encryption: 1 # 0=prefer unencrypted, 1=prefer encrypted, 2=require encrypted diff --git a/ansible/roles/transmission/handlers/main.yml b/ansible/roles/transmission/handlers/main.yml deleted file mode 100644 index db948a4..0000000 --- a/ansible/roles/transmission/handlers/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -- name: Restart transmission - ansible.builtin.command: brew services restart transmission-cli - changed_when: true diff --git a/ansible/roles/transmission/tasks/main.yml b/ansible/roles/transmission/tasks/main.yml deleted file mode 100644 index f264b0d..0000000 --- a/ansible/roles/transmission/tasks/main.yml +++ /dev/null @@ -1,52 +0,0 @@ ---- -- name: Install transmission-cli via homebrew - community.general.homebrew: - name: transmission-cli - state: present - -- name: Ensure transmission download directory exists - ansible.builtin.file: - path: "{{ transmission_download_dir }}" - state: directory - mode: '0755' - -- name: Ensure transmission incomplete directory exists - ansible.builtin.file: - path: "{{ transmission_incomplete_dir }}" - state: directory - mode: '0755' - -- name: Remove old config directory (was deployed to wrong location) - ansible.builtin.file: - path: ~/.config/transmission-daemon - state: absent - -# Note: transmission must be stopped before modifying settings.json -# otherwise it may overwrite our changes on shutdown -- name: Check if settings.json needs updating - ansible.builtin.template: - src: settings.json.j2 - dest: "{{ transmission_config_dir }}/settings.json" - mode: '0600' - check_mode: true - register: transmission_settings_check - -- name: Stop transmission before config changes - ansible.builtin.command: brew services stop transmission-cli - when: transmission_settings_check.changed - register: transmission_brew_stop - changed_when: false - failed_when: false - -- name: Deploy transmission settings.json - ansible.builtin.template: - src: settings.json.j2 - dest: "{{ transmission_config_dir }}/settings.json" - mode: '0600' - notify: Restart transmission - -- name: Ensure transmission service is started - ansible.builtin.command: brew services start transmission-cli - register: transmission_brew_start - changed_when: "'Successfully started' in transmission_brew_start.stdout" - failed_when: false diff --git a/ansible/roles/transmission/templates/settings.json.j2 b/ansible/roles/transmission/templates/settings.json.j2 deleted file mode 100644 index 36b0513..0000000 --- a/ansible/roles/transmission/templates/settings.json.j2 +++ /dev/null @@ -1,89 +0,0 @@ -{# - RPC is required for transmission-remote CLI to manage torrents. - Config is secure: bound to localhost only, no auth needed. - - rpc-password uses a static hash starting with '{' so transmission - recognizes it as pre-hashed and won't regenerate it on restart. - Without this, transmission writes a new hash each startup causing - perpetual ansible diffs. -#} -{ - "_comment": "{{ ansible_managed }}", - "alt-speed-down": 50, - "alt-speed-enabled": false, - "alt-speed-time-begin": 540, - "alt-speed-time-day": 127, - "alt-speed-time-enabled": false, - "alt-speed-time-end": 1020, - "alt-speed-up": 50, - "announce-ip": "", - "announce-ip-enabled": false, - "anti-brute-force-enabled": false, - "anti-brute-force-threshold": 100, - "bind-address-ipv4": "0.0.0.0", - "bind-address-ipv6": "::", - "blocklist-enabled": false, - "blocklist-url": "http://www.example.com/blocklist", - "cache-size-mb": 4, - "default-trackers": "", - "dht-enabled": {{ transmission_dht_enabled | lower }}, - "download-dir": "{{ transmission_download_dir }}", - "download-queue-enabled": true, - "download-queue-size": 5, - "encryption": {{ transmission_encryption }}, - "idle-seeding-limit": 30, - "idle-seeding-limit-enabled": false, - "incomplete-dir": "{{ transmission_incomplete_dir }}", - "incomplete-dir-enabled": true, - "lpd-enabled": true, - "message-level": 4, - "peer-congestion-algorithm": "", - "peer-limit-global": 200, - "peer-limit-per-torrent": 50, - "peer-port": 51413, - "peer-port-random-high": 65535, - "peer-port-random-low": 49152, - "peer-port-random-on-start": false, - "peer-socket-tos": "le", - "pex-enabled": {{ transmission_pex_enabled | lower }}, - "port-forwarding-enabled": true, - "preallocation": 1, - "prefetch-enabled": true, - "queue-stalled-enabled": true, - "queue-stalled-minutes": 30, - "ratio-limit": 2, - "ratio-limit-enabled": false, - "rename-partial-files": false, - "rpc-authentication-required": {{ transmission_rpc_authentication_required | lower }}, - "rpc-bind-address": "{{ transmission_rpc_bind_address }}", - "rpc-enabled": {{ transmission_rpc_enabled | lower }}, - "rpc-host-whitelist": "", - "rpc-host-whitelist-enabled": true, - "rpc-password": "{00000000000000000000000000000000000000000000000e", - "rpc-port": {{ transmission_rpc_port }}, - "rpc-socket-mode": "0750", - "rpc-url": "/transmission/", - "rpc-username": "", - "rpc-whitelist": "{{ transmission_rpc_whitelist }}", - "rpc-whitelist-enabled": {{ transmission_rpc_whitelist_enabled | lower }}, - "scrape-paused-torrents-enabled": true, - "script-torrent-added-enabled": false, - "script-torrent-added-filename": "", - "script-torrent-done-enabled": false, - "script-torrent-done-filename": "", - "script-torrent-done-seeding-enabled": false, - "script-torrent-done-seeding-filename": "", - "seed-queue-enabled": false, - "seed-queue-size": 10, - "speed-limit-down": {{ transmission_speed_limit_down }}, - "speed-limit-down-enabled": {{ (transmission_speed_limit_down > 0) | lower }}, - "speed-limit-up": {{ transmission_speed_limit_up }}, - "speed-limit-up-enabled": {{ (transmission_speed_limit_up > 0) | lower }}, - "start-added-torrents": true, - "tcp-enabled": true, - "torrent-added-verify-mode": "fast", - "trash-original-torrent-files": false, - "umask": "022", - "upload-slots-per-torrent": 8, - "utp-enabled": true -} diff --git a/ansible/roles/transmission_metrics/defaults/main.yml b/ansible/roles/transmission_metrics/defaults/main.yml deleted file mode 100644 index 9e9e2b0..0000000 --- a/ansible/roles/transmission_metrics/defaults/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -transmission_metrics_rpc_host: 127.0.0.1 -transmission_metrics_rpc_port: 9091 -transmission_metrics_dir: /opt/homebrew/var/node_exporter/textfile -transmission_metrics_script: /Users/erichblume/bin/transmission-metrics -transmission_metrics_interval: 60 # seconds between metric collection -transmission_metrics_log_dir: /opt/homebrew/var/log diff --git a/ansible/roles/transmission_metrics/handlers/main.yml b/ansible/roles/transmission_metrics/handlers/main.yml deleted file mode 100644 index 5e4d922..0000000 --- a/ansible/roles/transmission_metrics/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: Reload transmission-metrics - ansible.builtin.shell: | - launchctl unload ~/Library/LaunchAgents/mcquack.eblume.transmission-metrics.plist 2>/dev/null || true - launchctl load ~/Library/LaunchAgents/mcquack.eblume.transmission-metrics.plist - changed_when: true diff --git a/ansible/roles/transmission_metrics/meta/main.yml b/ansible/roles/transmission_metrics/meta/main.yml deleted file mode 100644 index b05a43b..0000000 --- a/ansible/roles/transmission_metrics/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# Role ordering is controlled by indri.yml playbook - do not add dependencies here -# (Ansible's tag accumulation prevents proper deduplication when using meta dependencies) -dependencies: [] diff --git a/ansible/roles/transmission_metrics/tasks/main.yml b/ansible/roles/transmission_metrics/tasks/main.yml deleted file mode 100644 index 31323c9..0000000 --- a/ansible/roles/transmission_metrics/tasks/main.yml +++ /dev/null @@ -1,26 +0,0 @@ ---- -- name: Deploy transmission metrics collection script - ansible.builtin.template: - src: transmission-metrics.sh.j2 - dest: "{{ transmission_metrics_script }}" - mode: '0755' - notify: Reload transmission-metrics - -- name: Deploy transmission-metrics LaunchAgent plist - ansible.builtin.template: - src: transmission-metrics.plist.j2 - dest: ~/Library/LaunchAgents/mcquack.eblume.transmission-metrics.plist - mode: '0644' - notify: Reload transmission-metrics - -- name: Check if transmission-metrics LaunchAgent is loaded - ansible.builtin.command: launchctl list mcquack.eblume.transmission-metrics - register: transmission_metrics_launchctl_check - changed_when: false - failed_when: false - -- name: Load transmission-metrics LaunchAgent if not loaded - ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.transmission-metrics.plist - when: transmission_metrics_launchctl_check.rc != 0 - changed_when: true - failed_when: false diff --git a/ansible/roles/transmission_metrics/templates/transmission-metrics.plist.j2 b/ansible/roles/transmission_metrics/templates/transmission-metrics.plist.j2 deleted file mode 100644 index a68e0c8..0000000 --- a/ansible/roles/transmission_metrics/templates/transmission-metrics.plist.j2 +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Label - mcquack.eblume.transmission-metrics - EnvironmentVariables - - PATH - /opt/homebrew/bin:/usr/bin:/bin - - ProgramArguments - - {{ transmission_metrics_script }} - - StartInterval - {{ transmission_metrics_interval }} - RunAtLoad - - StandardErrorPath - {{ transmission_metrics_log_dir }}/transmission-metrics.err.log - StandardOutPath - {{ transmission_metrics_log_dir }}/transmission-metrics.out.log - - diff --git a/ansible/roles/transmission_metrics/templates/transmission-metrics.sh.j2 b/ansible/roles/transmission_metrics/templates/transmission-metrics.sh.j2 deleted file mode 100644 index 0e394af..0000000 --- a/ansible/roles/transmission_metrics/templates/transmission-metrics.sh.j2 +++ /dev/null @@ -1,120 +0,0 @@ -#!/bin/bash -# {{ ansible_managed }} -# Collects transmission-daemon metrics for node_exporter textfile collector - -set -euo pipefail - -RPC_URL="http://{{ transmission_metrics_rpc_host }}:{{ transmission_metrics_rpc_port }}/transmission/rpc" -OUTPUT_FILE="{{ transmission_metrics_dir }}/transmission.prom" -TEMP_FILE="${OUTPUT_FILE}.tmp" - -# Get session ID (required for transmission RPC) -# Note: transmission doesn't support HEAD requests, so we make a request and parse -# the 409 response headers. We use sed to stop at the blank line (which has \r) before body. -get_session_id() { - curl -s -i "$RPC_URL" 2>/dev/null | sed '/^\r$/q' | grep -i '^X-Transmission-Session-Id:' | awk '{print $2}' | tr -d '\r' -} - -# Make RPC request -rpc_request() { - local method="$1" - local args="${2:-}" - local session_id - session_id=$(get_session_id) - - if [ -z "$session_id" ]; then - echo "Failed to get session ID" >&2 - return 1 - fi - - local payload - if [ -n "$args" ]; then - payload="{\"method\": \"$method\", \"arguments\": $args}" - else - payload="{\"method\": \"$method\"}" - fi - - curl -s "$RPC_URL" \ - -H "X-Transmission-Session-Id: $session_id" \ - -H "Content-Type: application/json" \ - -d "$payload" -} - -# Get session stats -session_stats=$(rpc_request "session-stats") - -if [ -z "$session_stats" ] || ! echo "$session_stats" | grep -q '"result":"success"'; then - echo "Failed to get session stats" >&2 - exit 1 -fi - -# Extract values using grep/sed (avoiding jq dependency) -extract_json_int() { - echo "$1" | grep -o "\"$2\":[0-9]*" | head -1 | sed "s/\"$2\"://" -} - -# Session stats -download_speed=$(extract_json_int "$session_stats" "downloadSpeed") -upload_speed=$(extract_json_int "$session_stats" "uploadSpeed") -torrents_active=$(extract_json_int "$session_stats" "activeTorrentCount") -torrents_paused=$(extract_json_int "$session_stats" "pausedTorrentCount") -torrents_total=$(extract_json_int "$session_stats" "torrentCount") - -# Cumulative stats -downloaded_bytes=$(echo "$session_stats" | grep -o '"cumulative-stats":{[^}]*}' | grep -o '"downloadedBytes":[0-9]*' | sed 's/"downloadedBytes"://') -uploaded_bytes=$(echo "$session_stats" | grep -o '"cumulative-stats":{[^}]*}' | grep -o '"uploadedBytes":[0-9]*' | sed 's/"uploadedBytes"://') -seconds_active=$(echo "$session_stats" | grep -o '"cumulative-stats":{[^}]*}' | grep -o '"secondsActive":[0-9]*' | sed 's/"secondsActive"://') - -# Get total size of all torrents -torrent_info=$(rpc_request "torrent-get" '{"fields": ["totalSize"]}') -total_size_bytes=0 -if echo "$torrent_info" | grep -q '"result":"success"'; then - # Sum all totalSize values - total_size_bytes=$(echo "$torrent_info" | grep -o '"totalSize":[0-9]*' | sed 's/"totalSize"://' | awk '{sum += $1} END {print sum}') -fi - -# Write metrics -cat > "$TEMP_FILE" << EOF -# HELP transmission_download_speed_bytes Current download speed in bytes per second -# TYPE transmission_download_speed_bytes gauge -transmission_download_speed_bytes ${download_speed:-0} - -# HELP transmission_upload_speed_bytes Current upload speed in bytes per second -# TYPE transmission_upload_speed_bytes gauge -transmission_upload_speed_bytes ${upload_speed:-0} - -# HELP transmission_torrents_active Number of active torrents -# TYPE transmission_torrents_active gauge -transmission_torrents_active ${torrents_active:-0} - -# HELP transmission_torrents_paused Number of paused torrents -# TYPE transmission_torrents_paused gauge -transmission_torrents_paused ${torrents_paused:-0} - -# HELP transmission_torrents_total Total number of torrents -# TYPE transmission_torrents_total gauge -transmission_torrents_total ${torrents_total:-0} - -# HELP transmission_torrents_size_bytes Total size of all torrents in bytes -# TYPE transmission_torrents_size_bytes gauge -transmission_torrents_size_bytes ${total_size_bytes:-0} - -# HELP transmission_downloaded_bytes_total Total bytes downloaded (cumulative) -# TYPE transmission_downloaded_bytes_total counter -transmission_downloaded_bytes_total ${downloaded_bytes:-0} - -# HELP transmission_uploaded_bytes_total Total bytes uploaded (cumulative) -# TYPE transmission_uploaded_bytes_total counter -transmission_uploaded_bytes_total ${uploaded_bytes:-0} - -# HELP transmission_seconds_active_total Total seconds transmission has been active -# TYPE transmission_seconds_active_total counter -transmission_seconds_active_total ${seconds_active:-0} - -# HELP transmission_up Transmission daemon is up and responding -# TYPE transmission_up gauge -transmission_up 1 -EOF - -# Atomic move -mv "$TEMP_FILE" "$OUTPUT_FILE" -- 2.50.1 (Apple Git-155) From 6a613f8369f15e26ccf94aa23fddbbef16dcd1b6 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 21 Jan 2026 20:13:35 -0800 Subject: [PATCH 2/3] Remove migrated service tags from indri Services grafana, kiwix, devpi, pg (blumeops-pg), and feed (miniflux) have migrated to Kubernetes. They now have their own Tailscale devices via the tailscale-operator, so indri no longer needs these service tags. Remaining tags on indri: homelab, blumeops, forge, loki, registry, k8s-api Co-Authored-By: Claude Opus 4.5 --- pulumi/__main__.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pulumi/__main__.py b/pulumi/__main__.py index 0a08ee8..7c76c26 100644 --- a/pulumi/__main__.py +++ b/pulumi/__main__.py @@ -36,7 +36,8 @@ acl = tailscale.Acl( # Tags control access via the ACL policy in policy.hujson. # indri - Mac Mini M1, primary homelab server -# Hosts all user-facing services (grafana, forge, kiwix, etc.) +# Hosts forge, loki, zot registry, and the k8s control plane. +# Other services (grafana, kiwix, devpi, etc.) run in k8s with their own Tailscale devices. indri = tailscale.get_device(name="indri.tail8d86e.ts.net") indri_tags = tailscale.DeviceTags( "indri-tags", @@ -44,16 +45,11 @@ indri_tags = tailscale.DeviceTags( tags=[ "tag:homelab", # Server role - allows SSH from workstations "tag:blumeops", # Managed by this IaC - # Service tags - enable fine-grained access control per service - "tag:grafana", + # Service tags for services still hosted directly on indri "tag:forge", - "tag:kiwix", - "tag:devpi", "tag:loki", - "tag:pg", - "tag:feed", "tag:registry", # Zot container registry - "tag:k8s-api", # Kubernetes API server + "tag:k8s-api", # Kubernetes API server (minikube) ], ) -- 2.50.1 (Apple Git-155) From 2402ee67e6d8a20b3a715917568428c0c84584b5 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 21 Jan 2026 20:17:00 -0800 Subject: [PATCH 3/3] Configure ArgoCD to ignore kiwix zim-hash annotation The zim-watcher CronJob updates the kiwix.blumeops/zim-hash annotation on the deployment to trigger restarts when ZIM files change. This causes ArgoCD to show the app as OutOfSync since the live value differs from git. Add ignoreDifferences to the kiwix Application to exclude this annotation from sync status calculations. Co-Authored-By: Claude Opus 4.5 --- argocd/apps/kiwix.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/argocd/apps/kiwix.yaml b/argocd/apps/kiwix.yaml index d69da7c..70be2c1 100644 --- a/argocd/apps/kiwix.yaml +++ b/argocd/apps/kiwix.yaml @@ -16,3 +16,9 @@ spec: syncPolicy: syncOptions: - CreateNamespace=true + # Ignore zim-hash annotation - updated dynamically by zim-watcher CronJob + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /metadata/annotations/kiwix.blumeops~1zim-hash -- 2.50.1 (Apple Git-155)