Remove unused indri tags and ansible roles (#41)
## Summary - Remove ansible roles for services migrated to k8s: devpi, kiwix, transmission - Also remove unused node_exporter and podman ansible roles - Remove service tags from indri for k8s-hosted services (grafana, kiwix, devpi, pg, feed) - Update indri description to reflect current architecture ## Changes **Ansible roles removed** (34 files, ~1000 lines): - devpi, devpi_metrics - kiwix - transmission, transmission_metrics - node_exporter - podman **Pulumi indri tags removed**: - tag:grafana, tag:kiwix, tag:devpi, tag:pg, tag:feed These services now run in k8s with their own Tailscale devices via tailscale-operator. ## Deployment and Testing - [x] Verified remaining ansible roles match indri.yml - [x] Verified no playbooks or role dependencies reference removed roles - [ ] Run `pulumi preview` to verify tag changes - [ ] Run `pulumi up` to apply tag changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.tail8d86e.ts.net/eblume/blumeops/pulls/41
This commit is contained in:
parent
6a140107c6
commit
5a829e0afd
36 changed files with 10 additions and 1079 deletions
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- {{ ansible_managed }} -->
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>KeepAlive</key>
|
|
||||||
<true/>
|
|
||||||
<key>Label</key>
|
|
||||||
<string>mcquack.eblume.devpi</string>
|
|
||||||
<key>EnvironmentVariables</key>
|
|
||||||
<dict>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
||||||
</dict>
|
|
||||||
<key>ProgramArguments</key>
|
|
||||||
<array>
|
|
||||||
<string>/opt/homebrew/opt/mise/bin/mise</string>
|
|
||||||
<string>x</string>
|
|
||||||
<string>--</string>
|
|
||||||
<string>devpi-server</string>
|
|
||||||
<string>--serverdir</string>
|
|
||||||
<string>{{ devpi_serverdir }}</string>
|
|
||||||
<string>--host</string>
|
|
||||||
<string>{{ devpi_host }}</string>
|
|
||||||
<string>--port</string>
|
|
||||||
<string>{{ devpi_port }}</string>
|
|
||||||
<string>--outside-url</string>
|
|
||||||
<string>{{ devpi_outside_url }}</string>
|
|
||||||
<string>--secretfile</string>
|
|
||||||
<string>{{ devpi_secretfile }}</string>
|
|
||||||
</array>
|
|
||||||
<key>RunAtLoad</key>
|
|
||||||
<true/>
|
|
||||||
<key>StandardErrorPath</key>
|
|
||||||
<string>{{ devpi_log_dir }}/mcquack.devpi.err.log</string>
|
|
||||||
<key>StandardOutPath</key>
|
|
||||||
<string>{{ devpi_log_dir }}/mcquack.devpi.out.log</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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: []
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- {{ ansible_managed }} -->
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>Label</key>
|
|
||||||
<string>mcquack.eblume.devpi-metrics</string>
|
|
||||||
<key>ProgramArguments</key>
|
|
||||||
<array>
|
|
||||||
<string>{{ devpi_metrics_script }}</string>
|
|
||||||
</array>
|
|
||||||
<key>StartInterval</key>
|
|
||||||
<integer>{{ devpi_metrics_interval }}</integer>
|
|
||||||
<key>RunAtLoad</key>
|
|
||||||
<true/>
|
|
||||||
<key>StandardErrorPath</key>
|
|
||||||
<string>{{ devpi_metrics_log_dir }}/mcquack.devpi-metrics.err.log</string>
|
|
||||||
<key>StandardOutPath</key>
|
|
||||||
<string>{{ devpi_metrics_log_dir }}/mcquack.devpi-metrics.out.log</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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: []
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- {{ ansible_managed }} -->
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>KeepAlive</key>
|
|
||||||
<true/>
|
|
||||||
<key>Label</key>
|
|
||||||
<string>mcquack.eblume.kiwix-serve</string>
|
|
||||||
<key>ProgramArguments</key>
|
|
||||||
<array>
|
|
||||||
<string>{{ kiwix_serve_bin }}</string>
|
|
||||||
<string>--port={{ kiwix_port }}</string>
|
|
||||||
{% for filename in kiwix_available_archives %}
|
|
||||||
<string>{{ kiwix_zim_dir }}/{{ filename }}</string>
|
|
||||||
{% endfor %}
|
|
||||||
</array>
|
|
||||||
<key>RunAtLoad</key>
|
|
||||||
<true/>
|
|
||||||
<key>StandardErrorPath</key>
|
|
||||||
<string>{{ kiwix_log_dir }}/mcquack.kiwix-serve.err.log</string>
|
|
||||||
<key>StandardOutPath</key>
|
|
||||||
<string>{{ kiwix_log_dir }}/mcquack.kiwix-serve.out.log</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -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 <source-dir> <target-dir>" >&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
|
|
||||||
|
|
@ -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 <torrent-list-file>" >&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"
|
|
||||||
|
|
@ -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 %}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
---
|
|
||||||
node_exporter_textfile_dir: /opt/homebrew/var/node_exporter/textfile
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
- name: Restart node_exporter
|
|
||||||
ansible.builtin.command: brew services restart node_exporter
|
|
||||||
listen: Restart node_exporter
|
|
||||||
changed_when: true
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
--collector.textfile.directory={{ node_exporter_textfile_dir }}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
# No handlers currently - podman machine start is unreliable via Ansible
|
|
||||||
# See known issue in tasks/main.yml
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
- name: Restart transmission
|
|
||||||
ansible.builtin.command: brew services restart transmission-cli
|
|
||||||
changed_when: true
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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: []
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- {{ ansible_managed }} -->
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>Label</key>
|
|
||||||
<string>mcquack.eblume.transmission-metrics</string>
|
|
||||||
<key>EnvironmentVariables</key>
|
|
||||||
<dict>
|
|
||||||
<key>PATH</key>
|
|
||||||
<string>/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
||||||
</dict>
|
|
||||||
<key>ProgramArguments</key>
|
|
||||||
<array>
|
|
||||||
<string>{{ transmission_metrics_script }}</string>
|
|
||||||
</array>
|
|
||||||
<key>StartInterval</key>
|
|
||||||
<integer>{{ transmission_metrics_interval }}</integer>
|
|
||||||
<key>RunAtLoad</key>
|
|
||||||
<true/>
|
|
||||||
<key>StandardErrorPath</key>
|
|
||||||
<string>{{ transmission_metrics_log_dir }}/transmission-metrics.err.log</string>
|
|
||||||
<key>StandardOutPath</key>
|
|
||||||
<string>{{ transmission_metrics_log_dir }}/transmission-metrics.out.log</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -16,3 +16,9 @@ spec:
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
syncOptions:
|
syncOptions:
|
||||||
- CreateNamespace=true
|
- CreateNamespace=true
|
||||||
|
# Ignore zim-hash annotation - updated dynamically by zim-watcher CronJob
|
||||||
|
ignoreDifferences:
|
||||||
|
- group: apps
|
||||||
|
kind: Deployment
|
||||||
|
jsonPointers:
|
||||||
|
- /metadata/annotations/kiwix.blumeops~1zim-hash
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ acl = tailscale.Acl(
|
||||||
# Tags control access via the ACL policy in policy.hujson.
|
# Tags control access via the ACL policy in policy.hujson.
|
||||||
|
|
||||||
# indri - Mac Mini M1, primary homelab server
|
# 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 = tailscale.get_device(name="indri.tail8d86e.ts.net")
|
||||||
indri_tags = tailscale.DeviceTags(
|
indri_tags = tailscale.DeviceTags(
|
||||||
"indri-tags",
|
"indri-tags",
|
||||||
|
|
@ -44,16 +45,11 @@ indri_tags = tailscale.DeviceTags(
|
||||||
tags=[
|
tags=[
|
||||||
"tag:homelab", # Server role - allows SSH from workstations
|
"tag:homelab", # Server role - allows SSH from workstations
|
||||||
"tag:blumeops", # Managed by this IaC
|
"tag:blumeops", # Managed by this IaC
|
||||||
# Service tags - enable fine-grained access control per service
|
# Service tags for services still hosted directly on indri
|
||||||
"tag:grafana",
|
|
||||||
"tag:forge",
|
"tag:forge",
|
||||||
"tag:kiwix",
|
|
||||||
"tag:devpi",
|
|
||||||
"tag:loki",
|
"tag:loki",
|
||||||
"tag:pg",
|
|
||||||
"tag:feed",
|
|
||||||
"tag:registry", # Zot container registry
|
"tag:registry", # Zot container registry
|
||||||
"tag:k8s-api", # Kubernetes API server
|
"tag:k8s-api", # Kubernetes API server (minikube)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue