286 lines
7.6 KiB
YAML
286 lines
7.6 KiB
YAML
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: heimdall-config
|
|
namespace: heimdall
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
storageClassName: openebs-hostpath-retain
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: heimdall-link-seed
|
|
namespace: heimdall
|
|
data:
|
|
links.json: |
|
|
[
|
|
{
|
|
"title": "Website",
|
|
"url": "https://lab2025.duckdns.org/",
|
|
"description": "Public portfolio website",
|
|
"colour": "#0f766e",
|
|
"class": "Website"
|
|
},
|
|
{
|
|
"title": "Demo Apps",
|
|
"url": "https://lab2025.duckdns.org/demo-apps/",
|
|
"description": "Static browser-side demo catalog",
|
|
"colour": "#2563eb",
|
|
"class": "Demos"
|
|
},
|
|
{
|
|
"title": "Gitea",
|
|
"url": "https://lab2025.duckdns.org/git/",
|
|
"description": "External Git service on the Raspberry Pi",
|
|
"colour": "#609926",
|
|
"class": "Gitea"
|
|
},
|
|
{
|
|
"title": "Grafana",
|
|
"url": "http://100.77.80.72:30083/",
|
|
"description": "Monitoring dashboards",
|
|
"colour": "#f97316",
|
|
"class": "Grafana"
|
|
},
|
|
{
|
|
"title": "Prometheus",
|
|
"url": "http://100.77.80.72:30084/",
|
|
"description": "Prometheus query UI",
|
|
"colour": "#dc2626",
|
|
"class": "Prometheus"
|
|
},
|
|
{
|
|
"title": "Alertmanager",
|
|
"url": "http://100.77.80.72:30085/",
|
|
"description": "Alert routing and silences",
|
|
"colour": "#b91c1c",
|
|
"class": "Alertmanager"
|
|
},
|
|
{
|
|
"title": "Argo CD",
|
|
"url": "https://100.77.80.72:30086/",
|
|
"description": "GitOps application sync status",
|
|
"colour": "#0ea5e9",
|
|
"class": "ArgoCD"
|
|
},
|
|
{
|
|
"title": "Container Registry",
|
|
"url": "http://100.77.80.72:30500/v2/_catalog",
|
|
"description": "Local image registry catalog endpoint",
|
|
"colour": "#334155",
|
|
"class": "Docker"
|
|
},
|
|
{
|
|
"title": "Heimdall",
|
|
"url": "http://100.77.80.72:30082/",
|
|
"description": "Homelab service dashboard",
|
|
"colour": "#7c3aed",
|
|
"class": "Heimdall"
|
|
}
|
|
]
|
|
seed.py: |
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
import time
|
|
from datetime import datetime, timezone
|
|
|
|
DB_CANDIDATES = (
|
|
"/config/www/app.sqlite",
|
|
"/config/app.sqlite",
|
|
)
|
|
LINKS_PATH = "/seed/links.json"
|
|
|
|
def find_database():
|
|
while True:
|
|
for path in DB_CANDIDATES:
|
|
if os.path.exists(path):
|
|
return path
|
|
time.sleep(5)
|
|
|
|
def table_exists(conn, name):
|
|
row = conn.execute(
|
|
"select name from sqlite_master where type = 'table' and name = ?",
|
|
(name,),
|
|
).fetchone()
|
|
return row is not None
|
|
|
|
def columns(conn, table):
|
|
return {row[1] for row in conn.execute(f"pragma table_info({table})")}
|
|
|
|
def wait_for_items_table(db_path):
|
|
while True:
|
|
try:
|
|
conn = sqlite3.connect(db_path)
|
|
if table_exists(conn, "items"):
|
|
return conn
|
|
conn.close()
|
|
except sqlite3.Error:
|
|
pass
|
|
time.sleep(5)
|
|
|
|
def upsert_links(conn, links):
|
|
item_columns = columns(conn, "items")
|
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
for order, link in enumerate(links):
|
|
existing = conn.execute(
|
|
"select id from items where title = ?",
|
|
(link["title"],),
|
|
).fetchone()
|
|
values = {
|
|
"title": link["title"],
|
|
"url": link["url"],
|
|
"description": link.get("description"),
|
|
"colour": link.get("colour"),
|
|
"pinned": 1,
|
|
"order": order,
|
|
"type": 0,
|
|
"user_id": 1,
|
|
"class": link.get("class"),
|
|
"appid": None,
|
|
"appdescription": link.get("description"),
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
"deleted_at": None,
|
|
}
|
|
|
|
if existing:
|
|
update_columns = [
|
|
name for name in values
|
|
if name in item_columns and name not in ("id", "title", "created_at")
|
|
]
|
|
assignments = ", ".join(f"{name} = ?" for name in update_columns)
|
|
params = [values[name] for name in update_columns]
|
|
params.append(existing[0])
|
|
conn.execute(f"update items set {assignments} where id = ?", params)
|
|
continue
|
|
|
|
insert_columns = [
|
|
name for name in values
|
|
if name in item_columns and name != "deleted_at"
|
|
]
|
|
placeholders = ", ".join("?" for _ in insert_columns)
|
|
conn.execute(
|
|
f"insert into items ({', '.join(insert_columns)}) values ({placeholders})",
|
|
[values[name] for name in insert_columns],
|
|
)
|
|
|
|
conn.commit()
|
|
|
|
with open(LINKS_PATH, encoding="utf-8") as links_file:
|
|
links = json.load(links_file)
|
|
|
|
db_path = find_database()
|
|
connection = wait_for_items_table(db_path)
|
|
try:
|
|
upsert_links(connection, links)
|
|
finally:
|
|
connection.close()
|
|
|
|
while True:
|
|
time.sleep(3600)
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: heimdall
|
|
namespace: heimdall
|
|
labels:
|
|
app: heimdall
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: heimdall
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: heimdall
|
|
spec:
|
|
nodeSelector:
|
|
kubernetes.io/os: linux
|
|
homelab.dev/workload-class: platform
|
|
securityContext:
|
|
fsGroup: 1000
|
|
fsGroupChangePolicy: OnRootMismatch
|
|
containers:
|
|
- name: heimdall
|
|
image: lscr.io/linuxserver/heimdall:version-2.7.6
|
|
imagePullPolicy: IfNotPresent
|
|
env:
|
|
- name: PUID
|
|
value: "1000"
|
|
- name: PGID
|
|
value: "1000"
|
|
- name: TZ
|
|
value: America/Mexico_City
|
|
ports:
|
|
- containerPort: 80
|
|
name: http
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: http
|
|
initialDelaySeconds: 20
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: http
|
|
initialDelaySeconds: 60
|
|
periodSeconds: 30
|
|
resources:
|
|
requests:
|
|
cpu: 25m
|
|
memory: 128Mi
|
|
limits:
|
|
memory: 512Mi
|
|
volumeMounts:
|
|
- name: heimdall-config
|
|
mountPath: /config
|
|
- name: link-seeder
|
|
image: python:3.12-alpine
|
|
imagePullPolicy: IfNotPresent
|
|
command:
|
|
- python
|
|
- /seed/seed.py
|
|
resources:
|
|
requests:
|
|
cpu: 5m
|
|
memory: 32Mi
|
|
limits:
|
|
memory: 96Mi
|
|
volumeMounts:
|
|
- name: heimdall-config
|
|
mountPath: /config
|
|
- name: link-seed
|
|
mountPath: /seed
|
|
readOnly: true
|
|
volumes:
|
|
- name: heimdall-config
|
|
persistentVolumeClaim:
|
|
claimName: heimdall-config
|
|
- name: link-seed
|
|
configMap:
|
|
name: heimdall-link-seed
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: heimdall
|
|
namespace: heimdall
|
|
spec:
|
|
type: NodePort
|
|
ports:
|
|
- port: 80
|
|
targetPort: http
|
|
nodePort: 30082
|
|
selector:
|
|
app: heimdall
|