Split Artcraft into independent chart applications
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: artcraft-homelab
|
||||
type: application
|
||||
version: 0.1.0
|
||||
@@ -1,45 +0,0 @@
|
||||
"""Download and unpack an official, versioned upstream web bundle."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
import urllib.request
|
||||
import zipfile
|
||||
|
||||
url = os.environ["RELEASE_URL"]
|
||||
if not url.startswith("https://github.com/storytold/"):
|
||||
raise ValueError("Expected an official storytold GitHub release URL")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
archive = Path(tmp) / "release.zip"
|
||||
for attempt in range(4):
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=120) as response, archive.open("wb") as out:
|
||||
shutil.copyfileobj(response, out)
|
||||
break
|
||||
except Exception:
|
||||
if attempt == 3:
|
||||
raise
|
||||
time.sleep(2 ** attempt)
|
||||
unpacked = Path(tmp) / "unpacked"
|
||||
with zipfile.ZipFile(archive) as bundle:
|
||||
for member in bundle.infolist():
|
||||
path = Path(member.filename)
|
||||
if path.is_absolute() or ".." in path.parts or (member.external_attr >> 16) & 0o170000 == 0o120000:
|
||||
raise ValueError(f"Unsafe archive entry: {path}")
|
||||
bundle.extractall(unpacked)
|
||||
roots = [p.parent for p in unpacked.glob("*/index.html")]
|
||||
if (unpacked / "index.html").exists():
|
||||
roots = [unpacked]
|
||||
if len(roots) != 1:
|
||||
raise ValueError("Expected exactly one web root")
|
||||
if not any(roots[0].rglob("*.wasm")) and not any(roots[0].rglob("*.part-*")):
|
||||
raise ValueError("Missing WebAssembly payload")
|
||||
# Kubernetes owns the emptyDir root; do not try to chmod that directory.
|
||||
for source in roots[0].iterdir():
|
||||
target = Path("/srv/web") / source.name
|
||||
if source.is_dir():
|
||||
shutil.copytree(source, target, dirs_exist_ok=True)
|
||||
else:
|
||||
shutil.copy2(source, target)
|
||||
print(f"Installed {url}", flush=True)
|
||||
@@ -1,37 +0,0 @@
|
||||
worker_processes auto;
|
||||
pid /tmp/nginx.pid;
|
||||
error_log /dev/stderr warn;
|
||||
events { worker_connections 1024; }
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
access_log /dev/stdout;
|
||||
client_body_temp_path /tmp/client_temp;
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
sendfile on;
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types application/wasm application/javascript text/javascript application/octet-stream text/css application/json;
|
||||
server {
|
||||
listen 8080;
|
||||
root /srv/web;
|
||||
index index.html;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
# Some upstream apps use stable filenames. Revalidation prevents stale builds.
|
||||
add_header Cache-Control no-cache always;
|
||||
location / { try_files $uri $uri/ =404; }
|
||||
location ~ /\. { deny all; }
|
||||
location = /_headers { deny all; }
|
||||
location = /nginx_status { return 404; }
|
||||
}
|
||||
server {
|
||||
listen 127.0.0.1:8081;
|
||||
access_log off;
|
||||
location = /nginx_status { stub_status; }
|
||||
location / { return 404; }
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
{{- range .Values.apps }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
progressDeadlineSeconds: 900
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
annotations:
|
||||
artcraft.brunner.ninja/version: {{ .version | quote }}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 101
|
||||
runAsGroup: 101
|
||||
fsGroup: 101
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: download-release
|
||||
image: {{ $.Values.images.downloader }}
|
||||
command: [python, /config/download.py]
|
||||
env:
|
||||
- name: RELEASE_URL
|
||||
value: {{ .releaseUrl | quote }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
resources:
|
||||
requests:
|
||||
cpu: 25m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: web
|
||||
mountPath: /srv/web
|
||||
- name: config
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
containers:
|
||||
- name: nginx
|
||||
image: {{ $.Values.images.nginx }}
|
||||
command: [nginx, -g, "daemon off;", -c, /config/nginx.conf]
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 128Mi
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /index.html
|
||||
port: http
|
||||
periodSeconds: 2
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /index.html
|
||||
port: http
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /index.html
|
||||
port: http
|
||||
periodSeconds: 15
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: web
|
||||
mountPath: /srv/web
|
||||
readOnly: true
|
||||
- name: config
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
- name: nginx-exporter
|
||||
image: {{ $.Values.images.exporter }}
|
||||
args:
|
||||
- --nginx.scrape-uri=http://127.0.0.1:8081/nginx_status
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: 9113
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
memory: 16Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 2
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: metrics
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
volumes:
|
||||
- name: web
|
||||
emptyDir:
|
||||
sizeLimit: 1Gi
|
||||
- name: tmp
|
||||
emptyDir:
|
||||
sizeLimit: 1Gi
|
||||
- name: config
|
||||
configMap:
|
||||
name: {{ $.Values.configMapName }}
|
||||
nodeSelector:
|
||||
odroid-h3: enabled
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: DoNotSchedule
|
||||
nodeTaintsPolicy: Honor
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
- name: metrics
|
||||
port: 9113
|
||||
targetPort: metrics
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ .name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .host }}
|
||||
rules:
|
||||
- host: {{ .host }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .name }}
|
||||
port:
|
||||
name: http
|
||||
---
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ .name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ .name }}
|
||||
app.kubernetes.io/part-of: artcraft
|
||||
endpoints:
|
||||
- port: metrics
|
||||
path: /metrics
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
---
|
||||
{{- end }}
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Values.configMapName }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
nginx.conf: |
|
||||
{{ .Files.Get "files/nginx.conf" | indent 4 }}
|
||||
download.py: |
|
||||
{{ .Files.Get "files/download.py" | indent 4 }}
|
||||
@@ -1,4 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ .Release.Namespace }}
|
||||
@@ -1,58 +0,0 @@
|
||||
configMapName: artcraft-config-4h2gh76d48
|
||||
images:
|
||||
downloader: docker.io/library/python:3.13-alpine
|
||||
nginx: docker.io/library/nginx:1.30.6-alpine
|
||||
exporter: docker.io/nginx/nginx-prometheus-exporter:1.5.3
|
||||
apps:
|
||||
- name: cadcraft
|
||||
version: v0.7.0
|
||||
releaseUrl: https://github.com/storytold/cadcraft/releases/download/v0.7.0/cadcraft-web-0.7.0.zip
|
||||
host: autocad.brunner.ninja
|
||||
- name: deckcraft
|
||||
version: v0.7.0
|
||||
releaseUrl: https://github.com/storytold/deckcraft/releases/download/v0.7.0/deckcraft-web-0.7.0.zip
|
||||
host: powerpoint.brunner.ninja
|
||||
- name: designcraft
|
||||
version: v0.8.0
|
||||
releaseUrl: https://github.com/storytold/designcraft/releases/download/v0.8.0/designcraft-web-0.8.0.zip
|
||||
host: indesign.brunner.ninja
|
||||
- name: effectcraft
|
||||
version: v0.10.0
|
||||
releaseUrl: https://github.com/storytold/effectcraft/releases/download/v0.10.0/effectcraft-web-0.10.0.zip
|
||||
host: aftereffects.brunner.ninja
|
||||
- name: figcraft
|
||||
version: v0.6.0
|
||||
releaseUrl: https://github.com/storytold/figcraft/releases/download/v0.6.0/figcraft-web-0.6.0.zip
|
||||
host: figma.brunner.ninja
|
||||
- name: filmcraft
|
||||
version: v0.8.0
|
||||
releaseUrl: https://github.com/storytold/filmcraft/releases/download/v0.8.0/filmcraft-web-0.8.0.zip
|
||||
host: premiere.brunner.ninja
|
||||
- name: gridcraft
|
||||
version: v0.7.0
|
||||
releaseUrl: https://github.com/storytold/gridcraft/releases/download/v0.7.0/gridcraft-web-0.7.0.zip
|
||||
host: excel.brunner.ninja
|
||||
- name: lightcraft
|
||||
version: v0.8.0
|
||||
releaseUrl: https://github.com/storytold/lightcraft/releases/download/v0.8.0/lightcraft-web-0.8.0.zip
|
||||
host: lightroom.brunner.ninja
|
||||
- name: pdfcraft
|
||||
version: v0.8.0
|
||||
releaseUrl: https://github.com/storytold/pdfcraft/releases/download/v0.8.0/pdfcraft-web-0.8.0.zip
|
||||
host: acrobat.brunner.ninja
|
||||
- name: photocraft
|
||||
version: v0.9.0
|
||||
releaseUrl: https://github.com/storytold/photocraft/releases/download/v0.9.0/photocraft-web-0.9.0.zip
|
||||
host: photoshop.brunner.ninja
|
||||
- name: soundcraft
|
||||
version: v0.7.0
|
||||
releaseUrl: https://github.com/storytold/soundcraft/releases/download/v0.7.0/soundcraft-web-0.7.0.zip
|
||||
host: protools.brunner.ninja
|
||||
- name: vectorcraft
|
||||
version: v0.12.0
|
||||
releaseUrl: https://github.com/storytold/vectorcraft/releases/download/v0.12.0/vectorcraft-web-0.12.0.zip
|
||||
host: illustrator.brunner.ninja
|
||||
- name: wordcraft
|
||||
version: v0.7.0
|
||||
releaseUrl: https://github.com/storytold/wordcraft/releases/download/v0.7.0/wordcraft-web-0.7.0.zip
|
||||
host: word.brunner.ninja
|
||||
@@ -7,6 +7,7 @@ spec:
|
||||
description: Stateless Artcraft web applications
|
||||
sourceRepos:
|
||||
- https://code.brunner.ninja/feedc0de/homelab-gitops.git
|
||||
- https://brunner.ninja/charts
|
||||
destinations:
|
||||
- namespace: artcraft
|
||||
server: https://kubernetes.default.svc
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: artcraft
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: artcraft
|
||||
source:
|
||||
repoURL: https://code.brunner.ninja/feedc0de/homelab-gitops.git
|
||||
targetRevision: main
|
||||
path: apps/artcraft
|
||||
helm:
|
||||
releaseName: artcraft
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: artcraft
|
||||
syncPolicy:
|
||||
automated:
|
||||
enabled: true
|
||||
prune: true
|
||||
selfHeal: true
|
||||
@@ -17,7 +17,19 @@ resources:
|
||||
- watschn-studio.yaml
|
||||
- artcraft-project.yaml
|
||||
- coturn-project.yaml
|
||||
- artcraft.yaml
|
||||
- cadcraft.yaml
|
||||
- deckcraft.yaml
|
||||
- designcraft.yaml
|
||||
- effectcraft.yaml
|
||||
- figcraft.yaml
|
||||
- filmcraft.yaml
|
||||
- gridcraft.yaml
|
||||
- lightcraft.yaml
|
||||
- pdfcraft.yaml
|
||||
- photocraft.yaml
|
||||
- soundcraft.yaml
|
||||
- vectorcraft.yaml
|
||||
- wordcraft.yaml
|
||||
- brunner-ninja.yaml
|
||||
- coturn.yaml
|
||||
- flaresolverr.yaml
|
||||
|
||||
Reference in New Issue
Block a user