bee6848458
gates / gates (push) Successful in 8s
Two channels moved off main in the same change, because either one left behind makes the other cosmetic. Channel 1 — the served script. webpage.yaml git-synced /scripts/ from --branch=main every 30s and nginx served that tree, so pushing this file WAS publishing it: within half a minute it was what every new machine downloaded and ran as root, with no staging and no rollback but another push. The sync is now SPLIT: the website keeps tracking main at the same cadence (a copy edit must never need a release) and /scripts/ tracks the tag installer-v<SCRIPT_VERSION>. PROVEN before the manifest was touched: git-sync v4.4.0 follows a tag AND notices a MOVED one — measured on a throwaway sync against this repo, "update required ... local:<old> remote:<new>" -> "updated successfully", within one period. The moved-tag half is what the publish model rests on. Channel 2 — the sixteen files fetched at run time. fetch_raw pulled from $AGENT_REPO/raw/branch/main; it now pulls raw/tag/v$ART_AGENT_VER. That is a correctness fix, not only a channel one (R-183): a fresh install fetched the vouched agent BINARY while taking its unit file, sudoers and guarded wrappers from whatever main held. Two refs, one install, nothing compared them. Their correct ref was never SCRIPT_VERSION — they do not live in this repo. No fallback to a branch: a vouched version whose tag is missing fails loudly rather than quietly serving main. Channel 3 — the URL — needed no change, recorded rather than left silent: https://felhom.eu/scripts/felhom-host-install.sh never carried a ref, so both producers follow the tag with no edit. No hub change, no hub version bump. Gate 6 in hostinstall_gates.py pins all three structurally with no network, so it stays in --fast and runs in CI. It deliberately does NOT assert "a tag exists for the current SCRIPT_VERSION": that would go red on the very push that bumps the version, before publishing — and publishing being separate is the ruling.
452 lines
13 KiB
YAML
452 lines
13 KiB
YAML
# FileBrowser + Webpage deployment for felhom.eu
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: filebrowser-files
|
|
namespace: felhom-system
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteMany
|
|
storageClassName: longhorn
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: filebrowser-db
|
|
namespace: felhom-system
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
storageClassName: longhorn
|
|
resources:
|
|
requests:
|
|
storage: 100Mi
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: filebrowser-config
|
|
namespace: felhom-system
|
|
data:
|
|
.filebrowser.json: |
|
|
{
|
|
"port": 8080,
|
|
"baseURL": "",
|
|
"address": "0.0.0.0",
|
|
"log": "stdout",
|
|
"database": "/database/filebrowser.db",
|
|
"root": "/srv"
|
|
}
|
|
---
|
|
# ===================
|
|
# NGINX CONFIG FOR CLEAN URLs
|
|
# ===================
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: nginx-config
|
|
namespace: felhom-system
|
|
data:
|
|
default.conf: |
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
root /usr/share/nginx/html/current/website;
|
|
index index.html;
|
|
|
|
# Enable clean URLs - serve .html files without extension
|
|
location / {
|
|
try_files $uri $uri.html $uri/ =404;
|
|
}
|
|
|
|
location = /sitemap.xml {
|
|
types { application/xml xml; }
|
|
default_type application/xml;
|
|
}
|
|
|
|
# Host-install script. It lives at the repo's /scripts (outside the website doc-root),
|
|
# synced into .../current/scripts by git-sync (see the sparse-checkout ConfigMap). Served
|
|
# as text/plain so operators can inspect it in a browser before download-then-run.
|
|
# R-110: served from the INSTALLER TAG's tree, not the website's. The URL is unchanged
|
|
# (https://felhom.eu/scripts/felhom-host-install.sh) — it never carried a ref, so every
|
|
# producer of it (the bootstrap script, the hub's day-0 command) follows the tag with no
|
|
# edit. What changed is which tree this root points at.
|
|
location /scripts/ {
|
|
root /usr/share/nginx/scripts/current;
|
|
default_type text/plain;
|
|
}
|
|
|
|
# Cache static assets
|
|
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
|
expires 7d;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Security headers
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
|
|
# Error pages
|
|
error_page 404 /404.html;
|
|
error_page 500 502 503 504 /50x.html;
|
|
}
|
|
---
|
|
# ===================
|
|
# FILEBROWSER
|
|
# ===================
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: filebrowser
|
|
namespace: felhom-system
|
|
labels:
|
|
app: filebrowser
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels:
|
|
app: filebrowser
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: filebrowser
|
|
spec:
|
|
# filebrowser v2.63.13 (debian default) runs as a non-root UID by default
|
|
# and can't write to PVC files left by the previous v2-alpine image (which
|
|
# ran as root). Force root explicitly so the existing PVC contents are
|
|
# readable + writable. (The alternative -- chown the PVC then drop perms --
|
|
# needs a one-shot initContainer; not worth the moving parts here.)
|
|
securityContext:
|
|
runAsUser: 0
|
|
runAsGroup: 0
|
|
containers:
|
|
- name: filebrowser
|
|
image: filebrowser/filebrowser:v2.63.13
|
|
# v2.63.x default config path is `/config/settings.json`; our ConfigMap
|
|
# is mounted at `/.filebrowser.json`. Tell filebrowser to read it
|
|
# explicitly so it picks up port 8080 (else it falls back to port 80
|
|
# and the readiness probe on 8080 fails).
|
|
args: ["-c", "/.filebrowser.json"]
|
|
ports:
|
|
- containerPort: 8080
|
|
volumeMounts:
|
|
- name: files
|
|
mountPath: /srv
|
|
- name: database
|
|
mountPath: /database
|
|
- name: config
|
|
mountPath: /.filebrowser.json
|
|
subPath: .filebrowser.json
|
|
resources:
|
|
requests:
|
|
memory: "64Mi"
|
|
cpu: "50m"
|
|
limits:
|
|
memory: "256Mi"
|
|
cpu: "500m"
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 8080
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 30
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 8080
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
volumes:
|
|
- name: files
|
|
persistentVolumeClaim:
|
|
claimName: filebrowser-files
|
|
- name: database
|
|
persistentVolumeClaim:
|
|
claimName: filebrowser-db
|
|
- name: config
|
|
configMap:
|
|
name: filebrowser-config
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: filebrowser
|
|
namespace: felhom-system
|
|
spec:
|
|
selector:
|
|
app: filebrowser
|
|
ports:
|
|
- port: 80
|
|
targetPort: 8080
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: filebrowser
|
|
namespace: felhom-system
|
|
annotations:
|
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
|
spec:
|
|
ingressClassName: nginx-internal
|
|
tls:
|
|
- hosts:
|
|
- files.felhom.eu
|
|
secretName: filebrowser-tls
|
|
rules:
|
|
- host: files.felhom.eu
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: filebrowser
|
|
port:
|
|
number: 80
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: git-sync-sparse-checkout
|
|
namespace: felhom-system
|
|
data:
|
|
# R-110: TWO sparse-checkouts, because there are now two syncs with two different refs.
|
|
# The website tracks `main` (a copy edit must never need a release); /scripts/ tracks the
|
|
# installer TAG (pushing the installer must never publish it).
|
|
sparse-checkout: |
|
|
/website/
|
|
sparse-checkout-scripts: |
|
|
/scripts/
|
|
---
|
|
# ===================
|
|
# WEBPAGE (nginx)
|
|
# ===================
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: felhom-webpage
|
|
namespace: felhom-system
|
|
labels:
|
|
app: felhom-webpage
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: felhom-webpage
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: felhom-webpage
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:alpine
|
|
ports:
|
|
- containerPort: 80
|
|
volumeMounts:
|
|
- name: git-data
|
|
mountPath: /usr/share/nginx/html
|
|
readOnly: true
|
|
- name: git-data-scripts
|
|
mountPath: /usr/share/nginx/scripts
|
|
readOnly: true
|
|
- name: nginx-config
|
|
mountPath: /etc/nginx/conf.d/default.conf
|
|
subPath: default.conf
|
|
resources:
|
|
requests:
|
|
memory: "32Mi"
|
|
cpu: "10m"
|
|
limits:
|
|
memory: "128Mi"
|
|
cpu: "200m"
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 80
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 30
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 80
|
|
initialDelaySeconds: 3
|
|
periodSeconds: 10
|
|
|
|
# ── The WEBSITE sync — tracks `main`, unchanged cadence ──────────────────────────────
|
|
# Deliberately still a branch: the site is content, and a typo fix must reach felhom.eu in
|
|
# thirty seconds without cutting a release. Only /scripts/ moved to a tag (R-110).
|
|
- name: git-sync
|
|
image: registry.k8s.io/git-sync/git-sync:v4.4.0
|
|
args:
|
|
- --repo=https://gitea.dooplex.hu/admin/felhom.eu.git
|
|
- --ref=main
|
|
- --root=/git
|
|
- --link=current
|
|
- --period=30s
|
|
# Only sync the website subdirectory
|
|
- --sparse-checkout-file=/etc/git-sync/sparse-checkout
|
|
volumeMounts:
|
|
- name: git-data
|
|
mountPath: /git
|
|
- name: sparse-checkout
|
|
mountPath: /etc/git-sync
|
|
resources:
|
|
requests:
|
|
memory: "32Mi"
|
|
cpu: "10m"
|
|
limits:
|
|
memory: "128Mi"
|
|
cpu: "100m"
|
|
securityContext:
|
|
runAsUser: 65534 # nobody
|
|
|
|
# ── The INSTALLER sync — tracks a TAG (R-110, operator ruling 2026-08-03) ─────────────
|
|
# felhom-host-install.sh runs as root on a virgin machine. Before this it was served
|
|
# straight from `main`, so pushing it WAS publishing it: within thirty seconds it was what
|
|
# every new machine downloaded and ran, with no staging and no rollback but another push.
|
|
#
|
|
# Publishing is now moving this tag; rolling back is moving it back. PROVEN, not assumed:
|
|
# git-sync v4.4.0 follows a tag AND notices a moved one — measured 2026-08-03 on a
|
|
# throwaway sync against this very repo (`update required … local:<old> remote:<new>` →
|
|
# `updated successfully`, one period, ~20 s).
|
|
#
|
|
# Bump this ref when the installer's published version changes. `hostinstall_gates.py`
|
|
# gate 6 fails if this sync stops naming an `installer-v…` tag.
|
|
- name: git-sync-scripts
|
|
image: registry.k8s.io/git-sync/git-sync:v4.4.0
|
|
args:
|
|
- --repo=https://gitea.dooplex.hu/admin/felhom.eu.git
|
|
- --ref=installer-v1.23.0
|
|
- --root=/git-scripts
|
|
- --link=current
|
|
- --period=30s
|
|
- --sparse-checkout-file=/etc/git-sync-scripts/sparse-checkout
|
|
volumeMounts:
|
|
- name: git-data-scripts
|
|
mountPath: /git-scripts
|
|
- name: sparse-checkout-scripts
|
|
mountPath: /etc/git-sync-scripts
|
|
resources:
|
|
requests:
|
|
memory: "32Mi"
|
|
cpu: "10m"
|
|
limits:
|
|
memory: "128Mi"
|
|
cpu: "100m"
|
|
securityContext:
|
|
runAsUser: 65534 # nobody
|
|
|
|
# Init container: wait for first sync before nginx starts
|
|
initContainers:
|
|
# BOTH trees are seeded before nginx accepts traffic. The second one is why /scripts/ has
|
|
# no 404 window across this change: a fresh pod does not become ready until the installer
|
|
# tag has been checked out, exactly as the website already worked.
|
|
- name: git-sync-init
|
|
image: registry.k8s.io/git-sync/git-sync:v4.4.0
|
|
args:
|
|
- --repo=https://gitea.dooplex.hu/admin/felhom.eu.git
|
|
- --ref=main
|
|
- --root=/git
|
|
- --link=current
|
|
- --one-time
|
|
- --sparse-checkout-file=/etc/git-sync/sparse-checkout
|
|
volumeMounts:
|
|
- name: git-data
|
|
mountPath: /git
|
|
- name: sparse-checkout
|
|
mountPath: /etc/git-sync
|
|
securityContext:
|
|
runAsUser: 65534
|
|
- name: git-sync-scripts-init
|
|
image: registry.k8s.io/git-sync/git-sync:v4.4.0
|
|
args:
|
|
- --repo=https://gitea.dooplex.hu/admin/felhom.eu.git
|
|
- --ref=installer-v1.23.0
|
|
- --root=/git-scripts
|
|
- --link=current
|
|
- --one-time
|
|
- --sparse-checkout-file=/etc/git-sync-scripts/sparse-checkout
|
|
volumeMounts:
|
|
- name: git-data-scripts
|
|
mountPath: /git-scripts
|
|
- name: sparse-checkout-scripts
|
|
mountPath: /etc/git-sync-scripts
|
|
securityContext:
|
|
runAsUser: 65534
|
|
|
|
volumes:
|
|
- name: git-data
|
|
emptyDir: {}
|
|
- name: git-data-scripts
|
|
emptyDir: {}
|
|
- name: nginx-config
|
|
configMap:
|
|
name: nginx-config
|
|
- name: sparse-checkout
|
|
configMap:
|
|
name: git-sync-sparse-checkout
|
|
items:
|
|
- key: sparse-checkout
|
|
path: sparse-checkout
|
|
- name: sparse-checkout-scripts
|
|
configMap:
|
|
name: git-sync-sparse-checkout
|
|
items:
|
|
- key: sparse-checkout-scripts
|
|
path: sparse-checkout
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: felhom-webpage
|
|
namespace: felhom-system
|
|
spec:
|
|
selector:
|
|
app: felhom-webpage
|
|
ports:
|
|
- port: 80
|
|
targetPort: 80
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: felhom-webpage
|
|
namespace: felhom-system
|
|
annotations:
|
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
spec:
|
|
ingressClassName: nginx-internal
|
|
tls:
|
|
- hosts:
|
|
- felhom.eu
|
|
- www.felhom.eu
|
|
secretName: felhom-webpage-tls
|
|
rules:
|
|
- host: felhom.eu
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: felhom-webpage
|
|
port:
|
|
number: 80
|
|
- host: www.felhom.eu
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: felhom-webpage
|
|
port:
|
|
number: 80 |