146 lines
3.8 KiB
YAML
146 lines
3.8 KiB
YAML
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: forgejo-metrics-script
|
|
namespace: forgejo-runners
|
|
data:
|
|
server.py: |
|
|
#!/usr/bin/env python3
|
|
"""
|
|
Polls the Forgejo API for waiting action tasks and exposes the count as JSON.
|
|
KEDA metrics-api scaler reads: { "value": <count> }
|
|
"""
|
|
import http.server
|
|
import json
|
|
import os
|
|
import threading
|
|
import time
|
|
import urllib.request
|
|
|
|
FORGEJO_URL = os.environ["FORGEJO_URL"].rstrip("/")
|
|
TOKEN = "69eccfc51f720c21c615cfd5caa422fb02f0ab43"
|
|
REFRESH = int(os.environ.get("REFRESH_INTERVAL", "20"))
|
|
|
|
_state = {"value": 0}
|
|
_lock = threading.Lock()
|
|
|
|
|
|
def fetch(path):
|
|
req = urllib.request.Request(
|
|
f"{FORGEJO_URL}/api/v1{path}",
|
|
headers={"Authorization": f"token {TOKEN}", "Accept": "application/json"},
|
|
)
|
|
with urllib.request.urlopen(req, timeout=10) as r:
|
|
return json.loads(r.read())
|
|
|
|
|
|
def count_pending_jobs():
|
|
total = 0
|
|
page = 1
|
|
while True:
|
|
repos = fetch(f"/repos/search?limit=50&page={page}")
|
|
items = repos.get("data", [])
|
|
for repo in items:
|
|
owner = repo["owner"]["login"]
|
|
name = repo["name"]
|
|
try:
|
|
result = fetch(
|
|
f"/repos/{owner}/{name}/actions/tasks?status=waiting&limit=1"
|
|
)
|
|
total += result.get("total_count", 0)
|
|
except Exception:
|
|
pass
|
|
if len(items) < 50:
|
|
break
|
|
page += 1
|
|
return total
|
|
|
|
|
|
def refresh_loop():
|
|
while True:
|
|
try:
|
|
count = count_pending_jobs()
|
|
with _lock:
|
|
_state["value"] = count
|
|
print(f"pending jobs: {count}", flush=True)
|
|
except Exception as e:
|
|
print(f"refresh error: {e}", flush=True)
|
|
time.sleep(REFRESH)
|
|
|
|
|
|
threading.Thread(target=refresh_loop, daemon=True).start()
|
|
|
|
|
|
class Handler(http.server.BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
with _lock:
|
|
val = _state["value"]
|
|
body = json.dumps({"value": val}).encode()
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
def log_message(self, *_):
|
|
pass
|
|
|
|
|
|
http.server.HTTPServer(("", 8080), Handler).serve_forever()
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: forgejo-metrics-exporter
|
|
namespace: forgejo-runners
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: forgejo-metrics-exporter
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: forgejo-metrics-exporter
|
|
spec:
|
|
containers:
|
|
- name: exporter
|
|
image: python:3.12-alpine
|
|
command: ["python", "/scripts/server.py"]
|
|
env:
|
|
- name: FORGEJO_URL
|
|
value: https://git.juntekim.com
|
|
- name: FORGEJO_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: forgejo-api-secret
|
|
key: token
|
|
- name: REFRESH_INTERVAL
|
|
value: "20"
|
|
ports:
|
|
- containerPort: 8080
|
|
resources:
|
|
requests:
|
|
cpu: 10m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 64Mi
|
|
volumeMounts:
|
|
- name: scripts
|
|
mountPath: /scripts
|
|
volumes:
|
|
- name: scripts
|
|
configMap:
|
|
name: forgejo-metrics-script
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: forgejo-metrics-exporter
|
|
namespace: forgejo-runners
|
|
spec:
|
|
selector:
|
|
app: forgejo-metrics-exporter
|
|
ports:
|
|
- port: 8080
|
|
targetPort: 8080
|