fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip]
6d224861 came from a --no-checkout worktree whose empty index made the
commit drop every file except two. This restores 05b50d2b's full tree and
correctly adds stacks/stem95su/gdrive-sync.tf + the service-catalog stem95su
entry. Forward-only (parent=6d224861, no force-push); [ci skip] since the
live infra was never applied from the broken commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6d224861c4
commit
fd0f4a0365
1166 changed files with 358546 additions and 0 deletions
270
stacks/servarr/mam-farming/main.tf
Normal file
270
stacks/servarr/mam-farming/main.tf
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
variable "namespace" {
|
||||
type = string
|
||||
default = "servarr"
|
||||
}
|
||||
|
||||
locals {
|
||||
python_image = "docker.io/library/python:3.12-alpine"
|
||||
pip_prefix = "pip install -q requests > /dev/null 2>&1; python3 /tmp/script.py"
|
||||
|
||||
# Dry-run window was satisfied by a one-shot test on 2026-04-19 that
|
||||
# produced 466 `never_started` candidates and 0 matches in any other
|
||||
# reason bucket — consistent with Phase B's expected 495 stuck torrents.
|
||||
# Enforcing from here on.
|
||||
janitor_dry_run = "0"
|
||||
}
|
||||
|
||||
# ----------------------------- NFS data volume -----------------------
|
||||
# Migrated off proxmox-lvm (2026-06-04): the cookie + grabbed-ID dedup list
|
||||
# are two plain-text files (no embedded DB), so NFS is safe and removes this
|
||||
# volume from the per-VM SCSI-LUN hotplug path entirely — a stuck `query-pci`
|
||||
# on a disk-heavy node VM used to wedge the grabber in ContainerCreating (the
|
||||
# disk never enumerated, Forbid blocked every run → MAMFarmingStuck). NFS
|
||||
# mounts over the network, consumes zero LUN slots, and is RWX so the grabber
|
||||
# and bp-spender can co-schedule on any node. See docs/architecture/storage.md
|
||||
# "Per-VM SCSI-LUN cap" lever #1.
|
||||
module "mam_data_nfs" {
|
||||
source = "../../../modules/kubernetes/nfs_volume"
|
||||
name = "servarr-mam-farming-data"
|
||||
namespace = var.namespace
|
||||
nfs_server = "192.168.1.127"
|
||||
nfs_path = "/srv/nfs/servarr/mam-farming"
|
||||
storage = "1Gi"
|
||||
}
|
||||
|
||||
# --------------------------- Grabber ---------------------------------
|
||||
# Every 30 minutes: skip while ratio < 1.2 or class == Mouse; otherwise
|
||||
# grab up to 5 small-but-popular freeleech torrents. Existing ConfigMap
|
||||
# + CronJob are adopted via imports in the parent stack.
|
||||
|
||||
resource "kubernetes_config_map" "grabber_script" {
|
||||
metadata {
|
||||
name = "mam-freeleech-grabber-script"
|
||||
namespace = var.namespace
|
||||
}
|
||||
data = {
|
||||
"script.py" = file("${path.module}/files/freeleech-grabber.py")
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_cron_job_v1" "grabber" {
|
||||
metadata {
|
||||
name = "mam-freeleech-grabber"
|
||||
namespace = var.namespace
|
||||
}
|
||||
spec {
|
||||
schedule = "*/30 * * * *"
|
||||
concurrency_policy = "Forbid"
|
||||
successful_jobs_history_limit = 3
|
||||
failed_jobs_history_limit = 3
|
||||
job_template {
|
||||
metadata {}
|
||||
spec {
|
||||
backoff_limit = 2
|
||||
ttl_seconds_after_finished = 300
|
||||
template {
|
||||
metadata {}
|
||||
spec {
|
||||
restart_policy = "Never"
|
||||
container {
|
||||
name = "freeleech-grabber"
|
||||
image = local.python_image
|
||||
command = ["/bin/sh", "-c", local.pip_prefix]
|
||||
env {
|
||||
name = "MAM_ID"
|
||||
value_from {
|
||||
secret_key_ref {
|
||||
name = "servarr-secrets"
|
||||
key = "mam_id"
|
||||
}
|
||||
}
|
||||
}
|
||||
resources {
|
||||
requests = { memory = "64Mi", cpu = "10m" }
|
||||
limits = { memory = "128Mi" }
|
||||
}
|
||||
volume_mount {
|
||||
name = "script"
|
||||
mount_path = "/tmp/script.py"
|
||||
sub_path = "script.py"
|
||||
}
|
||||
volume_mount {
|
||||
name = "data"
|
||||
mount_path = "/data"
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "script"
|
||||
config_map {
|
||||
name = kubernetes_config_map.grabber_script.metadata[0].name
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "data"
|
||||
persistent_volume_claim {
|
||||
claim_name = module.mam_data_nfs.claim_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
# KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2
|
||||
ignore_changes = [spec[0].job_template[0].spec[0].template[0].spec[0].dns_config]
|
||||
}
|
||||
}
|
||||
|
||||
# --------------------------- BP Spender ------------------------------
|
||||
# Every 6 hours: compute the upload deficit against TARGET_RATIO and buy
|
||||
# exactly what we need (+1 GiB margin), capped by BP reserve. Existing
|
||||
# ConfigMap + CronJob are adopted via imports in the parent stack.
|
||||
|
||||
resource "kubernetes_config_map" "bp_spender_script" {
|
||||
metadata {
|
||||
name = "mam-bp-spender-script"
|
||||
namespace = var.namespace
|
||||
}
|
||||
data = {
|
||||
"script.py" = file("${path.module}/files/bp-spender.py")
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_cron_job_v1" "bp_spender" {
|
||||
metadata {
|
||||
name = "mam-bp-spender"
|
||||
namespace = var.namespace
|
||||
}
|
||||
spec {
|
||||
schedule = "0 */6 * * *"
|
||||
concurrency_policy = "Forbid"
|
||||
successful_jobs_history_limit = 3
|
||||
failed_jobs_history_limit = 3
|
||||
job_template {
|
||||
metadata {}
|
||||
spec {
|
||||
backoff_limit = 2
|
||||
ttl_seconds_after_finished = 300
|
||||
template {
|
||||
metadata {}
|
||||
spec {
|
||||
restart_policy = "Never"
|
||||
container {
|
||||
name = "bp-spender"
|
||||
image = local.python_image
|
||||
command = ["/bin/sh", "-c", local.pip_prefix]
|
||||
env {
|
||||
name = "MAM_ID"
|
||||
value_from {
|
||||
secret_key_ref {
|
||||
name = "servarr-secrets"
|
||||
key = "mam_id"
|
||||
}
|
||||
}
|
||||
}
|
||||
resources {
|
||||
requests = { memory = "64Mi", cpu = "10m" }
|
||||
limits = { memory = "128Mi" }
|
||||
}
|
||||
volume_mount {
|
||||
name = "script"
|
||||
mount_path = "/tmp/script.py"
|
||||
sub_path = "script.py"
|
||||
}
|
||||
volume_mount {
|
||||
name = "data"
|
||||
mount_path = "/data"
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "script"
|
||||
config_map {
|
||||
name = kubernetes_config_map.bp_spender_script.metadata[0].name
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "data"
|
||||
persistent_volume_claim {
|
||||
claim_name = module.mam_data_nfs.claim_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
# KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2
|
||||
ignore_changes = [spec[0].job_template[0].spec[0].template[0].spec[0].dns_config]
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------- Janitor -------------------------------
|
||||
# New: every 15 minutes, independent of grabber ratio guard. Deletes
|
||||
# stuck/unregistered/redundant torrents in category=mam-farming while
|
||||
# preserving torrents inside the 72h H&R window.
|
||||
|
||||
resource "kubernetes_config_map" "janitor_script" {
|
||||
metadata {
|
||||
name = "mam-farming-janitor-script"
|
||||
namespace = var.namespace
|
||||
}
|
||||
data = {
|
||||
"script.py" = file("${path.module}/files/mam-farming-janitor.py")
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_cron_job_v1" "janitor" {
|
||||
metadata {
|
||||
name = "mam-farming-janitor"
|
||||
namespace = var.namespace
|
||||
}
|
||||
spec {
|
||||
schedule = "*/15 * * * *"
|
||||
concurrency_policy = "Forbid"
|
||||
successful_jobs_history_limit = 3
|
||||
failed_jobs_history_limit = 3
|
||||
job_template {
|
||||
metadata {}
|
||||
spec {
|
||||
backoff_limit = 2
|
||||
ttl_seconds_after_finished = 300
|
||||
template {
|
||||
metadata {}
|
||||
spec {
|
||||
restart_policy = "Never"
|
||||
container {
|
||||
name = "farming-janitor"
|
||||
image = local.python_image
|
||||
command = ["/bin/sh", "-c", local.pip_prefix]
|
||||
env {
|
||||
name = "DRY_RUN"
|
||||
value = local.janitor_dry_run
|
||||
}
|
||||
resources {
|
||||
requests = { memory = "64Mi", cpu = "10m" }
|
||||
limits = { memory = "128Mi" }
|
||||
}
|
||||
volume_mount {
|
||||
name = "script"
|
||||
mount_path = "/tmp/script.py"
|
||||
sub_path = "script.py"
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "script"
|
||||
config_map {
|
||||
name = kubernetes_config_map.janitor_script.metadata[0].name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
# KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2
|
||||
ignore_changes = [spec[0].job_template[0].spec[0].template[0].spec[0].dns_config]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue