Compare commits
36 Commits
codex/s58-
...
codex/l4-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e2c960be3 | ||
|
|
c482b66187 | ||
|
|
bacb756173 | ||
|
|
8a576c95ed | ||
|
|
41c2243f09 | ||
|
|
c21e602e4d | ||
|
|
9f6b71c400 | ||
|
|
26f90acf1f | ||
|
|
ab00d22657 | ||
|
|
c1a43c64b3 | ||
|
|
7103658342 | ||
|
|
6b12b2bb49 | ||
|
|
a4c9e44a36 | ||
|
|
9674a9555e | ||
|
|
318252da76 | ||
|
|
3798b7c00e | ||
|
|
2707f1ae1e | ||
|
|
a7e7c1ae72 | ||
|
|
c8df788d72 | ||
|
|
b1a4d7120e | ||
|
|
4b57b8e939 | ||
|
|
70f36c546b | ||
|
|
cdbddd71af | ||
|
|
81ac1f3e4f | ||
| b842738a0e | |||
|
|
f0cb7a5e81 | ||
| ac0f665323 | |||
|
|
c4b08f41ab | ||
|
|
417d3830ae | ||
| cb4ea13e7a | |||
|
|
a3cd67d6bb | ||
|
|
81a3ddac4c | ||
| 300f8ad546 | |||
| fe38c2641f | |||
|
|
3b40dfb185 | ||
| 103878671c |
@@ -1,448 +1,453 @@
|
|||||||
# Authentik OIDC backend
|
# Authentik OIDC backend
|
||||||
# ArgoCD-managed. BlueJay Lab.
|
# ArgoCD-managed. BlueJay Lab.
|
||||||
#
|
#
|
||||||
# Stack:
|
# Stack:
|
||||||
# - PostgreSQL 16 StatefulSet (single replica, Longhorn RWO 5Gi)
|
# - PostgreSQL 16 StatefulSet (single replica, Longhorn RWO 5Gi)
|
||||||
# - Redis 7 Deployment (no persistence — session/cache only)
|
# - Redis 7 Deployment (no persistence — session/cache only)
|
||||||
# - Authentik server + worker Deployments (image ghcr.io/goauthentik/server:2024.12.3)
|
# - Authentik server + worker Deployments (image ghcr.io/goauthentik/server:2024.12.3)
|
||||||
# - Media PVC shared between server + worker (Longhorn RWO 2Gi)
|
# - Media PVC shared between server + worker (Longhorn RWO 2Gi)
|
||||||
# - Certificate via step-ca-acme ClusterIssuer
|
# - Certificate via step-ca-acme ClusterIssuer
|
||||||
# - Traefik IngressRoute at id.iamworkin.lan
|
# - Traefik IngressRoute at id.iamworkin.lan
|
||||||
#
|
#
|
||||||
# Secrets come from 1Password item "authentik-credentials" (IAmWorkin vault, id y6i74ch22q5wvm7znquq4nhhcu)
|
# Secrets come from 1Password item "authentik-credentials" (IAmWorkin vault, id y6i74ch22q5wvm7znquq4nhhcu)
|
||||||
# via the OnePasswordItem CRD, materialized into k8s Secret authentik/authentik-credentials.
|
# via the OnePasswordItem CRD, materialized into k8s Secret authentik/authentik-credentials.
|
||||||
#
|
#
|
||||||
# Why the discovery URL is /application/o/pimanager/ : Authentik issues per-application OIDC providers.
|
# Why the discovery URL is /application/o/pimanager/ : Authentik issues per-application OIDC providers.
|
||||||
# The pimanager OIDC application/provider is created after the cluster pods are healthy (manual or
|
# The pimanager OIDC application/provider is created after the cluster pods are healthy (manual or
|
||||||
# via API once the bootstrap token is available — see Notes substrate).
|
# via API once the bootstrap token is available — see Notes substrate).
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Namespace
|
kind: Namespace
|
||||||
metadata:
|
metadata:
|
||||||
name: authentik
|
name: authentik
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/part-of: bluejay-infra
|
app.kubernetes.io/part-of: bluejay-infra
|
||||||
|
|
||||||
---
|
---
|
||||||
# 1Password operator pulls the authentik-credentials item into a k8s Secret of the same name.
|
# 1Password operator pulls the authentik-credentials item into a k8s Secret of the same name.
|
||||||
# Field labels in 1P become Secret keys: AUTHENTIK_SECRET_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD,
|
# Field labels in 1P become Secret keys: AUTHENTIK_SECRET_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD,
|
||||||
# BOOTSTRAP_ADMIN_PASSWORD, BOOTSTRAP_ADMIN_TOKEN, BOOTSTRAP_ADMIN_EMAIL.
|
# BOOTSTRAP_ADMIN_PASSWORD, BOOTSTRAP_ADMIN_TOKEN, BOOTSTRAP_ADMIN_EMAIL.
|
||||||
apiVersion: onepassword.com/v1
|
apiVersion: onepassword.com/v1
|
||||||
kind: OnePasswordItem
|
kind: OnePasswordItem
|
||||||
metadata:
|
metadata:
|
||||||
name: authentik-credentials
|
name: authentik-credentials
|
||||||
namespace: authentik
|
namespace: authentik
|
||||||
spec:
|
spec:
|
||||||
itemPath: "vaults/IAmWorkin/items/authentik-credentials"
|
itemPath: "vaults/IAmWorkin/items/authentik-credentials"
|
||||||
|
|
||||||
---
|
---
|
||||||
# Shared media volume for server + worker pods.
|
# Shared media volume for server + worker pods.
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
metadata:
|
metadata:
|
||||||
name: authentik-media
|
name: authentik-media
|
||||||
namespace: authentik
|
namespace: authentik
|
||||||
spec:
|
spec:
|
||||||
storageClassName: longhorn
|
storageClassName: longhorn
|
||||||
accessModes: [ReadWriteOnce]
|
accessModes: [ReadWriteOnce]
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 2Gi
|
storage: 2Gi
|
||||||
|
|
||||||
---
|
---
|
||||||
# PostgreSQL 16 StatefulSet — Authentik's primary store.
|
# PostgreSQL 16 StatefulSet — Authentik's primary store.
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
metadata:
|
metadata:
|
||||||
name: authentik-postgres
|
name: authentik-postgres
|
||||||
namespace: authentik
|
namespace: authentik
|
||||||
labels:
|
labels:
|
||||||
app: authentik-postgres
|
app: authentik-postgres
|
||||||
argocd.argoproj.io/instance: infra-authentik
|
argocd.argoproj.io/instance: infra-authentik
|
||||||
spec:
|
spec:
|
||||||
persistentVolumeClaimRetentionPolicy:
|
persistentVolumeClaimRetentionPolicy:
|
||||||
whenDeleted: Retain
|
whenDeleted: Retain
|
||||||
whenScaled: Retain
|
whenScaled: Retain
|
||||||
podManagementPolicy: OrderedReady
|
podManagementPolicy: OrderedReady
|
||||||
serviceName: authentik-postgres
|
serviceName: authentik-postgres
|
||||||
replicas: 1
|
replicas: 1
|
||||||
revisionHistoryLimit: 10
|
revisionHistoryLimit: 10
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: authentik-postgres
|
app: authentik-postgres
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: authentik-postgres
|
app: authentik-postgres
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: postgres
|
- name: postgres
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 5432
|
- containerPort: 5432
|
||||||
name: postgres
|
name: postgres
|
||||||
env:
|
env:
|
||||||
- name: POSTGRES_USER
|
- name: POSTGRES_USER
|
||||||
value: authentik
|
value: authentik
|
||||||
- name: POSTGRES_PASSWORD
|
- name: POSTGRES_PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: authentik-credentials
|
name: authentik-credentials
|
||||||
key: POSTGRES_PASSWORD
|
key: POSTGRES_PASSWORD
|
||||||
- name: POSTGRES_DB
|
- name: POSTGRES_DB
|
||||||
value: authentik
|
value: authentik
|
||||||
- name: POSTGRES_INITDB_ARGS
|
- name: POSTGRES_INITDB_ARGS
|
||||||
value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
|
value: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
|
||||||
- name: PGDATA
|
- name: PGDATA
|
||||||
value: /var/lib/postgresql/data/pgdata
|
value: /var/lib/postgresql/data/pgdata
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
exec:
|
exec:
|
||||||
command: ["pg_isready", "-U", "authentik"]
|
command: ["pg_isready", "-U", "authentik"]
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
exec:
|
exec:
|
||||||
command: ["pg_isready", "-U", "authentik"]
|
command: ["pg_isready", "-U", "authentik"]
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 30
|
periodSeconds: 30
|
||||||
resources:
|
resources:
|
||||||
requests: { cpu: 100m, memory: 256Mi }
|
requests: { cpu: 100m, memory: 256Mi }
|
||||||
limits: { cpu: 1000m, memory: 1Gi }
|
limits: { cpu: 1000m, memory: 1Gi }
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: pgdata
|
- name: pgdata
|
||||||
mountPath: /var/lib/postgresql/data
|
mountPath: /var/lib/postgresql/data
|
||||||
volumeClaimTemplates:
|
volumeClaimTemplates:
|
||||||
- metadata:
|
# apiVersion/kind included deliberately: this STS was created via ArgoCD ServerSideApply,
|
||||||
name: pgdata
|
# so the live object carries PVC TypeMeta inside volumeClaimTemplates; omitting it here
|
||||||
spec:
|
# leaves the app eternally OutOfSync even though kubectl SSA dry-run shows no change.
|
||||||
storageClassName: longhorn
|
- apiVersion: v1
|
||||||
accessModes: [ReadWriteOnce]
|
kind: PersistentVolumeClaim
|
||||||
volumeMode: Filesystem
|
metadata:
|
||||||
resources:
|
name: pgdata
|
||||||
requests:
|
spec:
|
||||||
storage: 5Gi
|
storageClassName: longhorn
|
||||||
|
accessModes: [ReadWriteOnce]
|
||||||
---
|
volumeMode: Filesystem
|
||||||
apiVersion: v1
|
resources:
|
||||||
kind: Service
|
requests:
|
||||||
metadata:
|
storage: 5Gi
|
||||||
name: authentik-postgres
|
|
||||||
namespace: authentik
|
---
|
||||||
spec:
|
apiVersion: v1
|
||||||
clusterIP: None
|
kind: Service
|
||||||
selector:
|
metadata:
|
||||||
app: authentik-postgres
|
name: authentik-postgres
|
||||||
ports:
|
namespace: authentik
|
||||||
- name: postgres
|
spec:
|
||||||
port: 5432
|
clusterIP: None
|
||||||
targetPort: 5432
|
selector:
|
||||||
|
app: authentik-postgres
|
||||||
---
|
ports:
|
||||||
# Redis 7 — session storage + Celery broker. No persistence needed (cache).
|
- name: postgres
|
||||||
apiVersion: apps/v1
|
port: 5432
|
||||||
kind: Deployment
|
targetPort: 5432
|
||||||
metadata:
|
|
||||||
name: authentik-redis
|
---
|
||||||
namespace: authentik
|
# Redis 7 — session storage + Celery broker. No persistence needed (cache).
|
||||||
labels:
|
apiVersion: apps/v1
|
||||||
app: authentik-redis
|
kind: Deployment
|
||||||
argocd.argoproj.io/instance: infra-authentik
|
metadata:
|
||||||
spec:
|
name: authentik-redis
|
||||||
replicas: 1
|
namespace: authentik
|
||||||
strategy:
|
labels:
|
||||||
type: Recreate
|
app: authentik-redis
|
||||||
selector:
|
argocd.argoproj.io/instance: infra-authentik
|
||||||
matchLabels:
|
spec:
|
||||||
app: authentik-redis
|
replicas: 1
|
||||||
template:
|
strategy:
|
||||||
metadata:
|
type: Recreate
|
||||||
labels:
|
selector:
|
||||||
app: authentik-redis
|
matchLabels:
|
||||||
spec:
|
app: authentik-redis
|
||||||
containers:
|
template:
|
||||||
- name: redis
|
metadata:
|
||||||
image: redis:7-alpine
|
labels:
|
||||||
args:
|
app: authentik-redis
|
||||||
- "--save"
|
spec:
|
||||||
- ""
|
containers:
|
||||||
- "--appendonly"
|
- name: redis
|
||||||
- "no"
|
image: redis:7-alpine
|
||||||
- "--requirepass"
|
args:
|
||||||
- "$(REDIS_PASSWORD)"
|
- "--save"
|
||||||
env:
|
- ""
|
||||||
- name: REDIS_PASSWORD
|
- "--appendonly"
|
||||||
valueFrom:
|
- "no"
|
||||||
secretKeyRef:
|
- "--requirepass"
|
||||||
name: authentik-credentials
|
- "$(REDIS_PASSWORD)"
|
||||||
key: REDIS_PASSWORD
|
env:
|
||||||
ports:
|
- name: REDIS_PASSWORD
|
||||||
- containerPort: 6379
|
valueFrom:
|
||||||
name: redis
|
secretKeyRef:
|
||||||
readinessProbe:
|
name: authentik-credentials
|
||||||
tcpSocket: { port: 6379 }
|
key: REDIS_PASSWORD
|
||||||
initialDelaySeconds: 5
|
ports:
|
||||||
periodSeconds: 5
|
- containerPort: 6379
|
||||||
livenessProbe:
|
name: redis
|
||||||
tcpSocket: { port: 6379 }
|
readinessProbe:
|
||||||
initialDelaySeconds: 30
|
tcpSocket: { port: 6379 }
|
||||||
periodSeconds: 30
|
initialDelaySeconds: 5
|
||||||
resources:
|
periodSeconds: 5
|
||||||
requests: { cpu: 50m, memory: 64Mi }
|
livenessProbe:
|
||||||
limits: { cpu: 500m, memory: 256Mi }
|
tcpSocket: { port: 6379 }
|
||||||
|
initialDelaySeconds: 30
|
||||||
---
|
periodSeconds: 30
|
||||||
apiVersion: v1
|
resources:
|
||||||
kind: Service
|
requests: { cpu: 50m, memory: 64Mi }
|
||||||
metadata:
|
limits: { cpu: 500m, memory: 256Mi }
|
||||||
name: authentik-redis
|
|
||||||
namespace: authentik
|
---
|
||||||
spec:
|
apiVersion: v1
|
||||||
selector:
|
kind: Service
|
||||||
app: authentik-redis
|
metadata:
|
||||||
ports:
|
name: authentik-redis
|
||||||
- name: redis
|
namespace: authentik
|
||||||
port: 6379
|
spec:
|
||||||
targetPort: 6379
|
selector:
|
||||||
|
app: authentik-redis
|
||||||
---
|
ports:
|
||||||
# Authentik server Deployment — HTTP frontend on :9000.
|
- name: redis
|
||||||
apiVersion: apps/v1
|
port: 6379
|
||||||
kind: Deployment
|
targetPort: 6379
|
||||||
metadata:
|
|
||||||
name: authentik-server
|
---
|
||||||
namespace: authentik
|
# Authentik server Deployment — HTTP frontend on :9000.
|
||||||
labels:
|
apiVersion: apps/v1
|
||||||
app: authentik-server
|
kind: Deployment
|
||||||
argocd.argoproj.io/instance: infra-authentik
|
metadata:
|
||||||
spec:
|
name: authentik-server
|
||||||
replicas: 1
|
namespace: authentik
|
||||||
strategy:
|
labels:
|
||||||
type: Recreate # shares /media RWO PVC with worker
|
app: authentik-server
|
||||||
selector:
|
argocd.argoproj.io/instance: infra-authentik
|
||||||
matchLabels:
|
spec:
|
||||||
app: authentik-server
|
replicas: 1
|
||||||
template:
|
strategy:
|
||||||
metadata:
|
type: Recreate # shares /media RWO PVC with worker
|
||||||
labels:
|
selector:
|
||||||
app: authentik-server
|
matchLabels:
|
||||||
spec:
|
app: authentik-server
|
||||||
securityContext:
|
template:
|
||||||
# Authentik image runs as uid 1000 "authentik" but the Longhorn PVC mounts
|
metadata:
|
||||||
# root:root by default. fsGroup recursively chgrp + chmod g+rwx so the
|
labels:
|
||||||
# non-root container can mkdir /media/public during the tenant_files migration.
|
app: authentik-server
|
||||||
fsGroup: 1000
|
spec:
|
||||||
containers:
|
securityContext:
|
||||||
- name: server
|
# Authentik image runs as uid 1000 "authentik" but the Longhorn PVC mounts
|
||||||
image: ghcr.io/goauthentik/server:2024.12.3
|
# root:root by default. fsGroup recursively chgrp + chmod g+rwx so the
|
||||||
args: ["server"]
|
# non-root container can mkdir /media/public during the tenant_files migration.
|
||||||
ports:
|
fsGroup: 1000
|
||||||
- containerPort: 9000
|
containers:
|
||||||
name: http
|
- name: server
|
||||||
- containerPort: 9443
|
image: ghcr.io/goauthentik/server:2024.12.3
|
||||||
name: https
|
args: ["server"]
|
||||||
env:
|
ports:
|
||||||
- name: AUTHENTIK_SECRET_KEY
|
- containerPort: 9000
|
||||||
valueFrom:
|
name: http
|
||||||
secretKeyRef:
|
- containerPort: 9443
|
||||||
name: authentik-credentials
|
name: https
|
||||||
key: AUTHENTIK_SECRET_KEY
|
env:
|
||||||
- name: AUTHENTIK_REDIS__HOST
|
- name: AUTHENTIK_SECRET_KEY
|
||||||
value: authentik-redis
|
valueFrom:
|
||||||
- name: AUTHENTIK_REDIS__PASSWORD
|
secretKeyRef:
|
||||||
valueFrom:
|
name: authentik-credentials
|
||||||
secretKeyRef:
|
key: AUTHENTIK_SECRET_KEY
|
||||||
name: authentik-credentials
|
- name: AUTHENTIK_REDIS__HOST
|
||||||
key: REDIS_PASSWORD
|
value: authentik-redis
|
||||||
- name: AUTHENTIK_POSTGRESQL__HOST
|
- name: AUTHENTIK_REDIS__PASSWORD
|
||||||
value: authentik-postgres
|
valueFrom:
|
||||||
- name: AUTHENTIK_POSTGRESQL__NAME
|
secretKeyRef:
|
||||||
value: authentik
|
name: authentik-credentials
|
||||||
- name: AUTHENTIK_POSTGRESQL__USER
|
key: REDIS_PASSWORD
|
||||||
value: authentik
|
- name: AUTHENTIK_POSTGRESQL__HOST
|
||||||
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
value: authentik-postgres
|
||||||
valueFrom:
|
- name: AUTHENTIK_POSTGRESQL__NAME
|
||||||
secretKeyRef:
|
value: authentik
|
||||||
name: authentik-credentials
|
- name: AUTHENTIK_POSTGRESQL__USER
|
||||||
key: POSTGRES_PASSWORD
|
value: authentik
|
||||||
- name: AUTHENTIK_BOOTSTRAP_PASSWORD
|
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: authentik-credentials
|
name: authentik-credentials
|
||||||
key: BOOTSTRAP_ADMIN_PASSWORD
|
key: POSTGRES_PASSWORD
|
||||||
- name: AUTHENTIK_BOOTSTRAP_TOKEN
|
- name: AUTHENTIK_BOOTSTRAP_PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: authentik-credentials
|
name: authentik-credentials
|
||||||
key: BOOTSTRAP_ADMIN_TOKEN
|
key: BOOTSTRAP_ADMIN_PASSWORD
|
||||||
- name: AUTHENTIK_BOOTSTRAP_EMAIL
|
- name: AUTHENTIK_BOOTSTRAP_TOKEN
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: authentik-credentials
|
name: authentik-credentials
|
||||||
key: BOOTSTRAP_ADMIN_EMAIL
|
key: BOOTSTRAP_ADMIN_TOKEN
|
||||||
- name: AUTHENTIK_DISABLE_UPDATE_CHECK
|
- name: AUTHENTIK_BOOTSTRAP_EMAIL
|
||||||
value: "true"
|
valueFrom:
|
||||||
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
secretKeyRef:
|
||||||
value: "false"
|
name: authentik-credentials
|
||||||
- name: AUTHENTIK_LOG_LEVEL
|
key: BOOTSTRAP_ADMIN_EMAIL
|
||||||
value: info
|
- name: AUTHENTIK_DISABLE_UPDATE_CHECK
|
||||||
# First-boot Authentik can take 3+ min on the migration phase
|
value: "true"
|
||||||
# (waiting on DB lock while worker also runs migrations). Initial
|
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
||||||
# delays are generous so kubelet doesn't kill the pod mid-migration;
|
value: "false"
|
||||||
# periodSeconds keeps post-startup probing responsive.
|
- name: AUTHENTIK_LOG_LEVEL
|
||||||
readinessProbe:
|
value: info
|
||||||
httpGet:
|
# First-boot Authentik can take 3+ min on the migration phase
|
||||||
path: /-/health/ready/
|
# (waiting on DB lock while worker also runs migrations). Initial
|
||||||
port: 9000
|
# delays are generous so kubelet doesn't kill the pod mid-migration;
|
||||||
initialDelaySeconds: 60
|
# periodSeconds keeps post-startup probing responsive.
|
||||||
periodSeconds: 10
|
readinessProbe:
|
||||||
timeoutSeconds: 5
|
httpGet:
|
||||||
failureThreshold: 12
|
path: /-/health/ready/
|
||||||
livenessProbe:
|
port: 9000
|
||||||
httpGet:
|
initialDelaySeconds: 60
|
||||||
path: /-/health/live/
|
periodSeconds: 10
|
||||||
port: 9000
|
timeoutSeconds: 5
|
||||||
initialDelaySeconds: 300
|
failureThreshold: 12
|
||||||
periodSeconds: 30
|
livenessProbe:
|
||||||
timeoutSeconds: 10
|
httpGet:
|
||||||
failureThreshold: 3
|
path: /-/health/live/
|
||||||
startupProbe:
|
port: 9000
|
||||||
httpGet:
|
initialDelaySeconds: 300
|
||||||
path: /-/health/live/
|
periodSeconds: 30
|
||||||
port: 9000
|
timeoutSeconds: 10
|
||||||
initialDelaySeconds: 30
|
failureThreshold: 3
|
||||||
periodSeconds: 15
|
startupProbe:
|
||||||
timeoutSeconds: 10
|
httpGet:
|
||||||
failureThreshold: 40 # 30s + 40*15s = 10.5 min budget
|
path: /-/health/live/
|
||||||
resources:
|
port: 9000
|
||||||
requests: { cpu: 150m, memory: 512Mi }
|
initialDelaySeconds: 30
|
||||||
limits: { cpu: 1500m, memory: 1Gi }
|
periodSeconds: 15
|
||||||
volumeMounts:
|
timeoutSeconds: 10
|
||||||
- name: media
|
failureThreshold: 40 # 30s + 40*15s = 10.5 min budget
|
||||||
mountPath: /media
|
resources:
|
||||||
volumes:
|
requests: { cpu: 150m, memory: 512Mi }
|
||||||
- name: media
|
limits: { cpu: 1500m, memory: 1Gi }
|
||||||
persistentVolumeClaim:
|
volumeMounts:
|
||||||
claimName: authentik-media
|
- name: media
|
||||||
|
mountPath: /media
|
||||||
---
|
volumes:
|
||||||
# Authentik worker Deployment — runs Celery background tasks.
|
- name: media
|
||||||
apiVersion: apps/v1
|
persistentVolumeClaim:
|
||||||
kind: Deployment
|
claimName: authentik-media
|
||||||
metadata:
|
|
||||||
name: authentik-worker
|
---
|
||||||
namespace: authentik
|
# Authentik worker Deployment — runs Celery background tasks.
|
||||||
labels:
|
apiVersion: apps/v1
|
||||||
app: authentik-worker
|
kind: Deployment
|
||||||
argocd.argoproj.io/instance: infra-authentik
|
metadata:
|
||||||
spec:
|
name: authentik-worker
|
||||||
replicas: 1
|
namespace: authentik
|
||||||
strategy:
|
labels:
|
||||||
type: Recreate # shares /media RWO PVC with server
|
app: authentik-worker
|
||||||
selector:
|
argocd.argoproj.io/instance: infra-authentik
|
||||||
matchLabels:
|
spec:
|
||||||
app: authentik-worker
|
replicas: 1
|
||||||
template:
|
strategy:
|
||||||
metadata:
|
type: Recreate # shares /media RWO PVC with server
|
||||||
labels:
|
selector:
|
||||||
app: authentik-worker
|
matchLabels:
|
||||||
spec:
|
app: authentik-worker
|
||||||
securityContext:
|
template:
|
||||||
# Same as server pod — non-root uid 1000 needs PVC group write.
|
metadata:
|
||||||
fsGroup: 1000
|
labels:
|
||||||
containers:
|
app: authentik-worker
|
||||||
- name: worker
|
spec:
|
||||||
image: ghcr.io/goauthentik/server:2024.12.3
|
securityContext:
|
||||||
args: ["worker"]
|
# Same as server pod — non-root uid 1000 needs PVC group write.
|
||||||
env:
|
fsGroup: 1000
|
||||||
- name: AUTHENTIK_SECRET_KEY
|
containers:
|
||||||
valueFrom:
|
- name: worker
|
||||||
secretKeyRef:
|
image: ghcr.io/goauthentik/server:2024.12.3
|
||||||
name: authentik-credentials
|
args: ["worker"]
|
||||||
key: AUTHENTIK_SECRET_KEY
|
env:
|
||||||
- name: AUTHENTIK_REDIS__HOST
|
- name: AUTHENTIK_SECRET_KEY
|
||||||
value: authentik-redis
|
valueFrom:
|
||||||
- name: AUTHENTIK_REDIS__PASSWORD
|
secretKeyRef:
|
||||||
valueFrom:
|
name: authentik-credentials
|
||||||
secretKeyRef:
|
key: AUTHENTIK_SECRET_KEY
|
||||||
name: authentik-credentials
|
- name: AUTHENTIK_REDIS__HOST
|
||||||
key: REDIS_PASSWORD
|
value: authentik-redis
|
||||||
- name: AUTHENTIK_POSTGRESQL__HOST
|
- name: AUTHENTIK_REDIS__PASSWORD
|
||||||
value: authentik-postgres
|
valueFrom:
|
||||||
- name: AUTHENTIK_POSTGRESQL__NAME
|
secretKeyRef:
|
||||||
value: authentik
|
name: authentik-credentials
|
||||||
- name: AUTHENTIK_POSTGRESQL__USER
|
key: REDIS_PASSWORD
|
||||||
value: authentik
|
- name: AUTHENTIK_POSTGRESQL__HOST
|
||||||
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
value: authentik-postgres
|
||||||
valueFrom:
|
- name: AUTHENTIK_POSTGRESQL__NAME
|
||||||
secretKeyRef:
|
value: authentik
|
||||||
name: authentik-credentials
|
- name: AUTHENTIK_POSTGRESQL__USER
|
||||||
key: POSTGRES_PASSWORD
|
value: authentik
|
||||||
- name: AUTHENTIK_DISABLE_UPDATE_CHECK
|
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
||||||
value: "true"
|
valueFrom:
|
||||||
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
secretKeyRef:
|
||||||
value: "false"
|
name: authentik-credentials
|
||||||
- name: AUTHENTIK_LOG_LEVEL
|
key: POSTGRES_PASSWORD
|
||||||
value: info
|
- name: AUTHENTIK_DISABLE_UPDATE_CHECK
|
||||||
resources:
|
value: "true"
|
||||||
requests: { cpu: 100m, memory: 256Mi }
|
- name: AUTHENTIK_ERROR_REPORTING__ENABLED
|
||||||
limits: { cpu: 1000m, memory: 768Mi }
|
value: "false"
|
||||||
volumeMounts:
|
- name: AUTHENTIK_LOG_LEVEL
|
||||||
- name: media
|
value: info
|
||||||
mountPath: /media
|
resources:
|
||||||
volumes:
|
requests: { cpu: 100m, memory: 256Mi }
|
||||||
- name: media
|
limits: { cpu: 1000m, memory: 768Mi }
|
||||||
persistentVolumeClaim:
|
volumeMounts:
|
||||||
claimName: authentik-media
|
- name: media
|
||||||
|
mountPath: /media
|
||||||
---
|
volumes:
|
||||||
apiVersion: v1
|
- name: media
|
||||||
kind: Service
|
persistentVolumeClaim:
|
||||||
metadata:
|
claimName: authentik-media
|
||||||
name: authentik-server
|
|
||||||
namespace: authentik
|
---
|
||||||
spec:
|
apiVersion: v1
|
||||||
selector:
|
kind: Service
|
||||||
app: authentik-server
|
metadata:
|
||||||
ports:
|
name: authentik-server
|
||||||
- name: http
|
namespace: authentik
|
||||||
port: 9000
|
spec:
|
||||||
targetPort: 9000
|
selector:
|
||||||
- name: https
|
app: authentik-server
|
||||||
port: 9443
|
ports:
|
||||||
targetPort: 9443
|
- name: http
|
||||||
|
port: 9000
|
||||||
---
|
targetPort: 9000
|
||||||
# step-ca leaf certificate for id.iamworkin.lan.
|
- name: https
|
||||||
# step-ca container resolver uses pfSense Unbound, so the public A record for id.iamworkin.lan
|
port: 9443
|
||||||
# MUST exist before this Certificate is applied (cert-manager HTTP-01 will silently 2h-backoff
|
targetPort: 9443
|
||||||
# otherwise). Added 2026-05-25 via scripts/pfsense-add-id-host.py.
|
|
||||||
apiVersion: cert-manager.io/v1
|
---
|
||||||
kind: Certificate
|
# step-ca leaf certificate for id.iamworkin.lan.
|
||||||
metadata:
|
# step-ca container resolver uses pfSense Unbound, so the public A record for id.iamworkin.lan
|
||||||
name: authentik-tls
|
# MUST exist before this Certificate is applied (cert-manager HTTP-01 will silently 2h-backoff
|
||||||
namespace: authentik
|
# otherwise). Added 2026-05-25 via scripts/pfsense-add-id-host.py.
|
||||||
spec:
|
apiVersion: cert-manager.io/v1
|
||||||
secretName: authentik-tls
|
kind: Certificate
|
||||||
dnsNames:
|
metadata:
|
||||||
- id.iamworkin.lan
|
name: authentik-tls
|
||||||
issuerRef:
|
namespace: authentik
|
||||||
name: step-ca-acme
|
spec:
|
||||||
kind: ClusterIssuer
|
secretName: authentik-tls
|
||||||
|
dnsNames:
|
||||||
---
|
- id.iamworkin.lan
|
||||||
apiVersion: traefik.io/v1alpha1
|
issuerRef:
|
||||||
kind: IngressRoute
|
name: step-ca-acme
|
||||||
metadata:
|
kind: ClusterIssuer
|
||||||
name: authentik
|
|
||||||
namespace: authentik
|
---
|
||||||
spec:
|
apiVersion: traefik.io/v1alpha1
|
||||||
entryPoints: [websecure]
|
kind: IngressRoute
|
||||||
routes:
|
metadata:
|
||||||
- match: Host(`id.iamworkin.lan`)
|
name: authentik
|
||||||
kind: Rule
|
namespace: authentik
|
||||||
services:
|
spec:
|
||||||
- name: authentik-server
|
entryPoints: [websecure]
|
||||||
port: 9000
|
routes:
|
||||||
tls:
|
- match: Host(`id.iamworkin.lan`)
|
||||||
secretName: authentik-tls
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: authentik-server
|
||||||
|
port: 9000
|
||||||
|
tls:
|
||||||
|
secretName: authentik-tls
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
prometheus.io/path: /metrics/prometheus
|
prometheus.io/path: /metrics/prometheus
|
||||||
prometheus.io/port: "5000"
|
prometheus.io/port: "5000"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
@@ -54,6 +56,7 @@ spec:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
- envFrom:
|
- envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: aistation-web-config
|
name: aistation-web-config
|
||||||
@@ -167,3 +170,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: aistation-web-tls
|
secretName: aistation-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose aistation-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: aistation-web-public
|
||||||
|
# namespace: fc-aistation
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`aistation.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: aistation-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: aistation-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ spec:
|
|||||||
app.kubernetes.io/name: chat-web
|
app.kubernetes.io/name: chat-web
|
||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
@@ -128,6 +130,7 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8080
|
containerPort: 8080
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: chat-web-config
|
name: chat-web-config
|
||||||
|
|||||||
@@ -14,6 +14,20 @@
|
|||||||
# cluster-rebuild repeatability. See
|
# cluster-rebuild repeatability. See
|
||||||
# feedback_networkpolicies_belong_in_bluejay_infra.md.
|
# feedback_networkpolicies_belong_in_bluejay_infra.md.
|
||||||
---
|
---
|
||||||
|
# OIDC client secret for the RemoteDesktop end-user sign-in (fleet regroup L9,
|
||||||
|
# 2026-06-12). The Authentik provider `remotedesktop` already exists; the 1P item
|
||||||
|
# `remotedesktop-oidc-client` (vault IAmWorkin) carries issuer_url / client_id /
|
||||||
|
# client_secret, and the 1Password operator mints the same-named K8s Secret that
|
||||||
|
# k8s/web-deployment.yaml (FlowerCore.RemoteDesktop repo) consumes with
|
||||||
|
# optional:true. Gate stays OFF (Q-RD-16) — this is flip-READINESS only.
|
||||||
|
apiVersion: onepassword.com/v1
|
||||||
|
kind: OnePasswordItem
|
||||||
|
metadata:
|
||||||
|
name: remotedesktop-oidc-client
|
||||||
|
namespace: fc-desktop
|
||||||
|
spec:
|
||||||
|
itemPath: "vaults/IAmWorkin/items/remotedesktop-oidc-client"
|
||||||
|
---
|
||||||
apiVersion: cert-manager.io/v1
|
apiVersion: cert-manager.io/v1
|
||||||
kind: Certificate
|
kind: Certificate
|
||||||
metadata:
|
metadata:
|
||||||
@@ -51,3 +65,26 @@ spec:
|
|||||||
port: 8080
|
port: 8080
|
||||||
tls:
|
tls:
|
||||||
secretName: remotedesktop-web-tls
|
secretName: remotedesktop-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose remotedesktop-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: remotedesktop-web-public
|
||||||
|
# namespace: fc-desktop
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`desktop.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: remotedesktop-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: remotedesktop-web
|
||||||
|
# port: 8080
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ metadata:
|
|||||||
flowercore.io/created-by: bluejay-infra
|
flowercore.io/created-by: bluejay-infra
|
||||||
rules:
|
rules:
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- devices.flowercore.io
|
- flowercore.io
|
||||||
resources:
|
resources:
|
||||||
- '*'
|
- '*'
|
||||||
verbs:
|
verbs:
|
||||||
@@ -23,7 +23,7 @@ rules:
|
|||||||
- patch
|
- patch
|
||||||
- delete
|
- delete
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- devices.flowercore.io
|
- flowercore.io
|
||||||
resources:
|
resources:
|
||||||
- devices/status
|
- devices/status
|
||||||
- devices/finalizers
|
- devices/finalizers
|
||||||
@@ -33,6 +33,8 @@ rules:
|
|||||||
- devicepolicies/finalizers
|
- devicepolicies/finalizers
|
||||||
- remotecommands/status
|
- remotecommands/status
|
||||||
- remotecommands/finalizers
|
- remotecommands/finalizers
|
||||||
|
- desiredstatedocuments/status
|
||||||
|
- desiredstatedocuments/finalizers
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- update
|
- update
|
||||||
|
|||||||
186
apps/fc-devicemgmt/crds.yaml
Normal file
186
apps/fc-devicemgmt/crds.yaml
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
# FlowerCore.DeviceManagement CRDs.
|
||||||
|
#
|
||||||
|
# These CRDs match the current operator annotations:
|
||||||
|
# [KubernetesEntity(Group = "flowercore.io", ApiVersion = "v1alpha1", ...)]
|
||||||
|
# Keep the schemas intentionally permissive until the DeviceManagement operator
|
||||||
|
# grows enforced CRD validation.
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: devices.flowercore.io
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: fc-devicemgmt-operator
|
||||||
|
app.kubernetes.io/component: operator
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
flowercore.io/tenant-id: system
|
||||||
|
flowercore.io/created-by: bluejay-infra
|
||||||
|
spec:
|
||||||
|
group: flowercore.io
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
plural: devices
|
||||||
|
singular: device
|
||||||
|
kind: Device
|
||||||
|
listKind: DeviceList
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: devicegroups.flowercore.io
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: fc-devicemgmt-operator
|
||||||
|
app.kubernetes.io/component: operator
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
flowercore.io/tenant-id: system
|
||||||
|
flowercore.io/created-by: bluejay-infra
|
||||||
|
spec:
|
||||||
|
group: flowercore.io
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
plural: devicegroups
|
||||||
|
singular: devicegroup
|
||||||
|
kind: DeviceGroup
|
||||||
|
listKind: DeviceGroupList
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: devicepolicies.flowercore.io
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: fc-devicemgmt-operator
|
||||||
|
app.kubernetes.io/component: operator
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
flowercore.io/tenant-id: system
|
||||||
|
flowercore.io/created-by: bluejay-infra
|
||||||
|
spec:
|
||||||
|
group: flowercore.io
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
plural: devicepolicies
|
||||||
|
singular: devicepolicy
|
||||||
|
kind: DevicePolicy
|
||||||
|
listKind: DevicePolicyList
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: remotecommands.flowercore.io
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: fc-devicemgmt-operator
|
||||||
|
app.kubernetes.io/component: operator
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
flowercore.io/tenant-id: system
|
||||||
|
flowercore.io/created-by: bluejay-infra
|
||||||
|
spec:
|
||||||
|
group: flowercore.io
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
plural: remotecommands
|
||||||
|
singular: remotecommand
|
||||||
|
kind: RemoteCommand
|
||||||
|
listKind: RemoteCommandList
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: desiredstatedocuments.flowercore.io
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: fc-devicemgmt-operator
|
||||||
|
app.kubernetes.io/component: operator
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
flowercore.io/tenant-id: system
|
||||||
|
flowercore.io/created-by: bluejay-infra
|
||||||
|
spec:
|
||||||
|
group: flowercore.io
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
plural: desiredstatedocuments
|
||||||
|
singular: desiredstatedocument
|
||||||
|
kind: DesiredStateDocument
|
||||||
|
listKind: DesiredStateDocumentList
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
status:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
@@ -5,21 +5,35 @@
|
|||||||
# exist yet; import localhost/fc-devicemgmt-web:<tag> to all schedulable RKE2
|
# exist yet; import localhost/fc-devicemgmt-web:<tag> to all schedulable RKE2
|
||||||
# nodes before letting ArgoCD sync a live rollout.
|
# nodes before letting ArgoCD sync a live rollout.
|
||||||
#
|
#
|
||||||
# SCALED TO 0 — 2026-05-19 morning-routine cleanup.
|
# LIVE — 2026-06-11 DeviceManagement product-host enablement.
|
||||||
# The Web pod cannot start until TWO upstream gaps close:
|
# The current DeviceManagement Web source is SQLite-backed in Program.cs, so
|
||||||
# 1. MySQL DB instance `flowercore_devicemgmt` (user `fc_devicemgmt`) is
|
# Phase 1 production uses a Longhorn RWO PVC at /data/devicemgmt.db. The
|
||||||
# provisioned via fc-mysql Manager. The cluster currently has ZERO
|
# 1Password runtime item stays mounted through env for future MySQL/API-key
|
||||||
# MySqlInstanceCrds and no `mysql.fc-mysql.svc:3306` Service, so the
|
# cutover, but MySQL is not required for this first product-host rollout.
|
||||||
# deployment-web container env `FlowerCore__Database__Host=mysql.fc-mysql.svc`
|
# Image v20260611-healthz is built from FlowerCore.DeviceManagement master
|
||||||
# points at nothing. Provision via the fc-mysql Manager UI/REST/MCP.
|
# 3c15f3b, which adds the /healthz alias required by fleet monitoring.
|
||||||
# 2. 1Password vault item `IAmWorkin/FlowerCore DeviceManagement Runtime`
|
---
|
||||||
# with 5 fields (DB-Password, mtls-ca.pem, mtls-client.crt, mtls-client.key,
|
apiVersion: v1
|
||||||
# mtls-chain.pem) — see apps/fc-devicemgmt/1password-item.yaml. Mint mTLS
|
kind: PersistentVolumeClaim
|
||||||
# from step-ca-agent ClusterIssuer per ADR-126; DB-Password must match the
|
metadata:
|
||||||
# password configured for the MySQL user.
|
name: fc-devicemgmt-web-data
|
||||||
# Re-enable: change replicas back to 2 after both gaps close. The image tag
|
namespace: fc-devicemgmt
|
||||||
# in this file (v20260512-cx5) MAY also need a refresh — it predates the
|
labels:
|
||||||
# Sprint 34 Cl-3 operator fix; Web may have an analogous bug.
|
app: fc-devicemgmt-web
|
||||||
|
app.kubernetes.io/name: fc-devicemgmt-web
|
||||||
|
app.kubernetes.io/component: web
|
||||||
|
app.kubernetes.io/part-of: flowercore
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
flowercore.io/tenant-id: system
|
||||||
|
flowercore.io/created-by: bluejay-infra
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
@@ -36,7 +50,7 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
flowercore.io/traceability-standard: k8s-pod-ownership-and-traceability-standard
|
flowercore.io/traceability-standard: k8s-pod-ownership-and-traceability-standard
|
||||||
spec:
|
spec:
|
||||||
replicas: 0
|
replicas: 1
|
||||||
revisionHistoryLimit: 3
|
revisionHistoryLimit: 3
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
@@ -52,6 +66,8 @@ spec:
|
|||||||
flowercore.io/tenant-id: system
|
flowercore.io/tenant-id: system
|
||||||
flowercore.io/created-by: bluejay-infra
|
flowercore.io/created-by: bluejay-infra
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -62,11 +78,12 @@ spec:
|
|||||||
fsGroupChangePolicy: OnRootMismatch
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
image: localhost/fc-devicemgmt-web:v20260512-cx5
|
image: localhost/fc-devicemgmt-web:v20260611-healthz
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8080
|
containerPort: 8080
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -74,29 +91,21 @@ spec:
|
|||||||
value: "Production"
|
value: "Production"
|
||||||
- name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
|
- name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
|
||||||
value: "false"
|
value: "false"
|
||||||
|
- name: HOME
|
||||||
|
value: "/data"
|
||||||
- name: FlowerCore__Service__Name
|
- name: FlowerCore__Service__Name
|
||||||
value: "FlowerCore.DeviceManagement.Web"
|
value: "FlowerCore.DeviceManagement.Web"
|
||||||
- name: FlowerCore__DeviceManagement__DefaultTenantId
|
- name: FlowerCore__DeviceManagement__DefaultTenantId
|
||||||
value: "system"
|
value: "system"
|
||||||
- name: FlowerCore__Database__Provider
|
- name: FlowerCore__Database__Provider
|
||||||
value: "MySql"
|
value: "Sqlite"
|
||||||
- name: FlowerCore__Database__Host
|
- name: FlowerCore__Database__ConnectionStrings__Sqlite
|
||||||
value: "mysql.fc-mysql.svc"
|
value: "Data Source=/data/devicemgmt.db"
|
||||||
- name: FlowerCore__Database__Database
|
|
||||||
value: "flowercore_devicemgmt"
|
|
||||||
- name: FlowerCore__Database__User
|
|
||||||
value: "fc_devicemgmt"
|
|
||||||
- name: FlowerCore__Database__Password
|
- name: FlowerCore__Database__Password
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: fc-devicemgmt-runtime
|
name: fc-devicemgmt-runtime
|
||||||
key: DB-Password
|
key: DB-Password
|
||||||
- name: FlowerCore__DeviceManagement__AgentMtls__CaPath
|
|
||||||
value: "/secrets/devicemgmt-mtls/mtls-ca.pem"
|
|
||||||
- name: FlowerCore__DeviceManagement__AgentMtls__ClientCertificatePath
|
|
||||||
value: "/secrets/devicemgmt-mtls/mtls-client.crt"
|
|
||||||
- name: FlowerCore__DeviceManagement__AgentMtls__ClientKeyPath
|
|
||||||
value: "/secrets/devicemgmt-mtls/mtls-client.key"
|
|
||||||
- name: FlowerCore__EventBus__Redis__Configuration
|
- name: FlowerCore__EventBus__Redis__Configuration
|
||||||
value: "redis.fc-redis.svc:6379"
|
value: "redis.fc-redis.svc:6379"
|
||||||
resources:
|
resources:
|
||||||
@@ -133,19 +142,17 @@ spec:
|
|||||||
drop:
|
drop:
|
||||||
- ALL
|
- ALL
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
- name: tmp
|
- name: tmp
|
||||||
mountPath: /tmp
|
mountPath: /tmp
|
||||||
- name: logs
|
- name: logs
|
||||||
mountPath: /app/logs
|
mountPath: /app/logs
|
||||||
- name: devicemgmt-mtls
|
|
||||||
mountPath: /secrets/devicemgmt-mtls
|
|
||||||
readOnly: true
|
|
||||||
volumes:
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: fc-devicemgmt-web-data
|
||||||
- name: tmp
|
- name: tmp
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
- name: logs
|
- name: logs
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
- name: devicemgmt-mtls
|
|
||||||
secret:
|
|
||||||
secretName: fc-devicemgmt-runtime
|
|
||||||
defaultMode: 0400
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ spec:
|
|||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
# Synology NFS export `/volume1/kubernetes` ACL only allows rke2-server
|
# Synology NFS export `/volume1/kubernetes` ACL only allows rke2-server
|
||||||
# (10.0.56.11) right now. Until the ACL is widened in DSM (admin only),
|
# (10.0.56.11) right now. Until the ACL is widened in DSM (admin only),
|
||||||
@@ -126,7 +127,7 @@ spec:
|
|||||||
# dotnet.exe publish -c Release -o deploy/app \
|
# dotnet.exe publish -c Release -o deploy/app \
|
||||||
# src/FlowerCore.Distribution.Web/FlowerCore.Distribution.Web.csproj
|
# src/FlowerCore.Distribution.Web/FlowerCore.Distribution.Web.csproj
|
||||||
# podman build -t localhost/fc-distribution:v<tag> -f deploy/Dockerfile.deploy deploy
|
# podman build -t localhost/fc-distribution:v<tag> -f deploy/Dockerfile.deploy deploy
|
||||||
image: localhost/fc-distribution:v20260604-oidc-proper
|
image: localhost/fc-distribution:v20260604-oidc-root-anon
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
|||||||
@@ -30,3 +30,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: dms-web-tls
|
secretName: dms-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose dms-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: dms-web-public
|
||||||
|
# namespace: fc-dms
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`dms.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: dms-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: dms-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ data:
|
|||||||
{
|
{
|
||||||
"FlowerCore": {
|
"FlowerCore": {
|
||||||
"Auth": {
|
"Auth": {
|
||||||
"Enabled": true,
|
"Enabled": false,
|
||||||
"Oidc": {
|
"Oidc": {
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Audience": "dns",
|
"Audience": "dns",
|
||||||
@@ -101,6 +101,7 @@ spec:
|
|||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "5320"
|
prometheus.io/port: "5320"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: dns-web
|
serviceAccountName: dns-web
|
||||||
securityContext:
|
securityContext:
|
||||||
@@ -110,7 +111,7 @@ spec:
|
|||||||
fsGroup: 1654
|
fsGroup: 1654
|
||||||
containers:
|
containers:
|
||||||
- name: dns-web
|
- name: dns-web
|
||||||
image: localhost/fc-dns-web:v20260604-oidc-proper
|
image: localhost/fc-dns-web:v20260612-l4dns-a5d2849
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
securityContext:
|
securityContext:
|
||||||
readOnlyRootFilesystem: true
|
readOnlyRootFilesystem: true
|
||||||
@@ -148,7 +149,7 @@ spec:
|
|||||||
key: client_secret
|
key: client_secret
|
||||||
optional: true
|
optional: true
|
||||||
- name: FlowerCore__Auth__Enabled
|
- name: FlowerCore__Auth__Enabled
|
||||||
value: "true"
|
value: "false"
|
||||||
- name: FlowerCore__Auth__Oidc__Enabled
|
- name: FlowerCore__Auth__Oidc__Enabled
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: FlowerCore__Auth__Oidc__Audience
|
- name: FlowerCore__Auth__Oidc__Audience
|
||||||
@@ -302,7 +303,7 @@ spec:
|
|||||||
fsGroup: 1654
|
fsGroup: 1654
|
||||||
containers:
|
containers:
|
||||||
- name: dns-acme-webhook
|
- name: dns-acme-webhook
|
||||||
image: localhost/fc-dns-acme-webhook:v202604290845
|
image: localhost/fc-dns-acme-webhook:v20260612-l4dns-a5d2849
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
securityContext:
|
securityContext:
|
||||||
readOnlyRootFilesystem: true
|
readOnlyRootFilesystem: true
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/health"
|
||||||
prometheus.io/path: /metrics/prometheus
|
prometheus.io/path: /metrics/prometheus
|
||||||
prometheus.io/port: "5000"
|
prometheus.io/port: "5000"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
@@ -54,6 +56,7 @@ spec:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
- envFrom:
|
- envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: library-web-config
|
name: library-web-config
|
||||||
@@ -167,3 +170,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: library-web-tls
|
secretName: library-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose library-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: library-web-public
|
||||||
|
# namespace: fc-library
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`library.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: library-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: library-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ spec:
|
|||||||
app.kubernetes.io/name: fc-llm-bridge
|
app.kubernetes.io/name: fc-llm-bridge
|
||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -116,6 +118,7 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -281,3 +284,26 @@ spec:
|
|||||||
port: 8080
|
port: 8080
|
||||||
tls:
|
tls:
|
||||||
secretName: fc-llm-bridge-tls
|
secretName: fc-llm-bridge-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose fc-llm-bridge publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: fc-llm-bridge-public
|
||||||
|
# namespace: fc-llm-bridge
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`llm-bridge.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: fc-llm-bridge-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: fc-llm-bridge
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ spec:
|
|||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "5200"
|
prometheus.io/port: "5200"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
kubernetes.io/hostname: rke2-server
|
kubernetes.io/hostname: rke2-server
|
||||||
|
|||||||
@@ -30,3 +30,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: menuboard-web-tls
|
secretName: menuboard-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose menuboard-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: menuboard-web-public
|
||||||
|
# namespace: fc-menuboard
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`menuboard.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: menuboard-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: menuboard-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: messageboard-web
|
app: messageboard-web
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/health"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
@@ -52,6 +54,7 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: messageboard-web-config
|
name: messageboard-web-config
|
||||||
@@ -141,3 +144,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: messageboard-web-tls
|
secretName: messageboard-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose messageboard-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: messageboard-web-public
|
||||||
|
# namespace: fc-messageboard
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`messageboard.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: messageboard-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: messageboard-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -30,3 +30,26 @@ spec:
|
|||||||
port: 5300
|
port: 5300
|
||||||
tls:
|
tls:
|
||||||
secretName: mysql-web-tls
|
secretName: mysql-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose mysql-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: mysql-web-public
|
||||||
|
# namespace: fc-mysql
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`mysql.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: mysql-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: mysql-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -30,3 +30,26 @@ spec:
|
|||||||
port: 5400
|
port: 5400
|
||||||
tls:
|
tls:
|
||||||
secretName: php-web-tls
|
secretName: php-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose php-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: php-web-public
|
||||||
|
# namespace: fc-php
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`php.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: php-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: php-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -30,3 +30,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: presentations-web-tls
|
secretName: presentations-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose presentations-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: presentations-web-public
|
||||||
|
# namespace: fc-presentations
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`presentations.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: presentations-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: presentations-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
kubectl.kubernetes.io/restartedAt: "2026-06-02T01:34:08-05:00"
|
kubectl.kubernetes.io/restartedAt: "2026-06-02T01:34:08-05:00"
|
||||||
prometheus.io/path: /metrics/prometheus
|
prometheus.io/path: /metrics/prometheus
|
||||||
prometheus.io/port: "5000"
|
prometheus.io/port: "5000"
|
||||||
@@ -55,6 +57,7 @@ spec:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
- envFrom:
|
- envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: retail-web-config
|
name: retail-web-config
|
||||||
@@ -168,3 +171,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: retail-web-tls
|
secretName: retail-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose retail-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: retail-web-public
|
||||||
|
# namespace: fc-retail
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`retail.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: retail-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: retail-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -30,3 +30,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: scoreboard-web-tls
|
secretName: scoreboard-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose scoreboard-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: scoreboard-web-public
|
||||||
|
# namespace: fc-scoreboard
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`scoreboard.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: scoreboard-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: scoreboard-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -37,3 +37,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: segmentdisplay-web-tls
|
secretName: segmentdisplay-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose segmentdisplay-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: segmentdisplay-web-public
|
||||||
|
# namespace: fc-segmentdisplay
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`segmentdisplay.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: segmentdisplay-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: segmentdisplay-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -46,3 +46,26 @@ spec:
|
|||||||
services:
|
services:
|
||||||
- name: signage-web
|
- name: signage-web
|
||||||
port: 5190
|
port: 5190
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose signage-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: signage-web-public
|
||||||
|
# namespace: fc-signage
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`signage.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: signage-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: signage-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: piper
|
- name: piper
|
||||||
image: rhasspy/wyoming-piper:latest
|
image: rhasspy/wyoming-piper:latest
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: PYTHONHTTPSVERIFY
|
- name: PYTHONHTTPSVERIFY
|
||||||
value: "0"
|
value: "0"
|
||||||
@@ -523,6 +524,8 @@ spec:
|
|||||||
app.kubernetes.io/name: ttsreader-web
|
app.kubernetes.io/name: ttsreader-web
|
||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/health"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "5217"
|
prometheus.io/port: "5217"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -532,7 +535,7 @@ spec:
|
|||||||
fsGroupChangePolicy: OnRootMismatch
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
image: localhost/fc-ttsreader-web:v20260603-s54cx14-pr29-schema
|
image: localhost/fc-ttsreader-web:v20260612-readalong-corrections
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 5217
|
- containerPort: 5217
|
||||||
@@ -762,3 +765,26 @@ spec:
|
|||||||
port: 5217
|
port: 5217
|
||||||
tls:
|
tls:
|
||||||
secretName: ttsreader-tls
|
secretName: ttsreader-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose ttsreader-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: ttsreader-web-public
|
||||||
|
# namespace: fc-ttsreader
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`ttsreader.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: ttsreader-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: ttsreader-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ spec:
|
|||||||
app: updatecenter-web
|
app: updatecenter-web
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/"
|
||||||
labels:
|
labels:
|
||||||
app: updatecenter-web
|
app: updatecenter-web
|
||||||
spec:
|
spec:
|
||||||
@@ -63,6 +66,7 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: http://+:8080
|
value: http://+:8080
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ All repo-scoped Linux runners use:
|
|||||||
- `ACCESS_TOKEN` from the `github-runner-token` Secret
|
- `ACCESS_TOKEN` from the `github-runner-token` Secret
|
||||||
- `RUN_AS_ROOT=false`
|
- `RUN_AS_ROOT=false`
|
||||||
- `EPHEMERAL=true`
|
- `EPHEMERAL=true`
|
||||||
|
- `DISABLE_AUTO_UPDATE=true` so the runner does not self-update and exit inside
|
||||||
|
the immutable Kubernetes pod
|
||||||
- `LABELS=self-hosted,linux,fc-build-linux`
|
- `LABELS=self-hosted,linux,fc-build-linux`
|
||||||
- writable non-root paths under `/home/runner` for .NET, NuGet, XDG cache, and
|
- writable non-root paths under `/home/runner` for .NET, NuGet, XDG cache, and
|
||||||
Actions tool cache
|
Actions tool cache
|
||||||
@@ -131,3 +133,7 @@ from GitHub Actions and verify it lands on an `rke2-linux-*` runner.
|
|||||||
value does not change.
|
value does not change.
|
||||||
- `Multi-Attach` volume error: only the Common runner uses a RWO PVC and it must
|
- `Multi-Attach` volume error: only the Common runner uses a RWO PVC and it must
|
||||||
stay single-replica. New multi-replica runners use `emptyDir`.
|
stay single-replica. New multi-replica runners use `emptyDir`.
|
||||||
|
- Runner pods repeatedly registering, downloading a newer Actions runner, then
|
||||||
|
exiting with code 4: verify `DISABLE_AUTO_UPDATE=true` is present. The image
|
||||||
|
translates that into `config.sh --disableupdate`; without it, the Deployment
|
||||||
|
controller sees the expected self-update exit as CrashLoopBackOff.
|
||||||
|
|||||||
@@ -195,6 +195,11 @@ spec:
|
|||||||
# fresh registration occurs. Prevents stale runner accumulation.
|
# fresh registration occurs. Prevents stale runner accumulation.
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
# Labels used by workflow files: runs-on: [self-hosted, linux, fc-build-linux]
|
# Labels used by workflow files: runs-on: [self-hosted, linux, fc-build-linux]
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
@@ -366,6 +371,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -504,6 +514,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -636,6 +651,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -768,6 +788,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -900,6 +925,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1035,6 +1065,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1167,6 +1202,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1299,6 +1339,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1431,6 +1476,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1565,6 +1615,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1699,6 +1754,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1838,6 +1898,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -1972,6 +2037,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2106,6 +2176,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2240,6 +2315,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2373,6 +2453,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2507,6 +2592,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2640,6 +2730,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2773,6 +2868,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -2906,6 +3006,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3039,6 +3144,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3172,6 +3282,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3306,6 +3421,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3440,6 +3560,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3574,6 +3699,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3708,6 +3838,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3842,6 +3977,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -3975,6 +4115,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -4109,6 +4254,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -4247,6 +4397,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -4386,6 +4541,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
@@ -4521,6 +4681,11 @@ spec:
|
|||||||
value: "/tmp/runner/work"
|
value: "/tmp/runner/work"
|
||||||
- name: EPHEMERAL
|
- name: EPHEMERAL
|
||||||
value: "true"
|
value: "true"
|
||||||
|
# The runner image must not self-update inside an immutable
|
||||||
|
# Kubernetes pod. Without this, GitHub runner auto-update exits
|
||||||
|
# with code 4 and the Deployment falls into CrashLoopBackOff.
|
||||||
|
- name: DISABLE_AUTO_UPDATE
|
||||||
|
value: "true"
|
||||||
- name: LABELS
|
- name: LABELS
|
||||||
value: "self-hosted,linux,fc-build-linux"
|
value: "self-hosted,linux,fc-build-linux"
|
||||||
- name: HOME
|
- name: HOME
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: intranet-web
|
- name: intranet-web
|
||||||
image: localhost/fc-intranet-web:v20260531-ttsreader-bridge
|
image: localhost/fc-intranet-web:v20260612-screenshot-metadata
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 5300
|
- containerPort: 5300
|
||||||
@@ -60,14 +60,17 @@ spec:
|
|||||||
# ≈ 9 hours. BLUEJAY-WS GPU (R9700, 32GB VRAM) does the same work
|
# ≈ 9 hours. BLUEJAY-WS GPU (R9700, 32GB VRAM) does the same work
|
||||||
# in minutes. Memory: feedback_pi5_nomic_embed_slow.
|
# in minutes. Memory: feedback_pi5_nomic_embed_slow.
|
||||||
- name: IntranetSearch__OllamaBaseUrl
|
- name: IntranetSearch__OllamaBaseUrl
|
||||||
value: "http://10.0.56.20:11434"
|
value: "http://edge1.iamworkin.lan:11434"
|
||||||
# Sprint E Phase 2α — JSON-file-backed PageReadingOverride persistence
|
# External Notes corpus roots are not mounted in the live pod today.
|
||||||
# on the writable PVC at /data. Without this env var the
|
# Keep the curated/workflow docs directory active without logging
|
||||||
# intranet falls back to the in-memory store (loses state on
|
# repeated /srv/flowercore-notes missing-root warnings.
|
||||||
# pod restart). Master's PageReadingOverrideOptions binds
|
- name: IntranetSearch__Enabled
|
||||||
# PageReadingOverrides:FilePath.
|
value: "false"
|
||||||
- name: PageReadingOverrides__FilePath
|
# Page-reading override SQLite persistence on the writable PVC at
|
||||||
value: "/data/page-reading-overrides.json"
|
# /data. This backs pronunciation, notes, corrections, and
|
||||||
|
# page-profile metadata across pod restarts.
|
||||||
|
- name: PageReadingOverrides__DatabasePath
|
||||||
|
value: "/data/page-reading-overrides.db"
|
||||||
- name: KnowledgeFleetSearch__BaseUrl
|
- name: KnowledgeFleetSearch__BaseUrl
|
||||||
value: "https://knowledge.iamworkin.lan"
|
value: "https://knowledge.iamworkin.lan"
|
||||||
- name: KnowledgeFleetSearch__ApiKey
|
- name: KnowledgeFleetSearch__ApiKey
|
||||||
|
|||||||
@@ -90,9 +90,12 @@ spec:
|
|||||||
app.kubernetes.io/name: knowledge-web
|
app.kubernetes.io/name: knowledge-web
|
||||||
app.kubernetes.io/part-of: bluejay-infra
|
app.kubernetes.io/part-of: bluejay-infra
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
@@ -116,6 +119,7 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -123,9 +127,9 @@ spec:
|
|||||||
value: "Production"
|
value: "Production"
|
||||||
- name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
|
- name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
|
||||||
value: "false"
|
value: "false"
|
||||||
# AuthentiK/OIDC is wired but not enforced until the
|
# AuthentiK/OIDC is enforced. /healthz stays anonymous by contract;
|
||||||
# knowledge-oidc-client Secret is provisioned and
|
# see flowercore.io/healthz-auth-policy above and the Sprint 58
|
||||||
# FlowerCore__Auth__Enabled is flipped to true.
|
# OIDC readiness probe audit.
|
||||||
- name: FlowerCore__Auth__Enabled
|
- name: FlowerCore__Auth__Enabled
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: FlowerCore__Auth__Oidc__Enabled
|
- name: FlowerCore__Auth__Oidc__Enabled
|
||||||
@@ -285,3 +289,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: knowledge-tls
|
secretName: knowledge-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose knowledge-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: knowledge-web-public
|
||||||
|
# namespace: knowledge
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`knowledge.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: knowledge-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: knowledge-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -216,19 +216,24 @@ data:
|
|||||||
- job_name: "pimanager-app"
|
- job_name: "pimanager-app"
|
||||||
scrape_interval: 15s
|
scrape_interval: 15s
|
||||||
metrics_path: /metrics
|
metrics_path: /metrics
|
||||||
|
scheme: https
|
||||||
|
tls_config:
|
||||||
|
insecure_skip_verify: true
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ["10.0.58.25:5000"]
|
- targets: ["piez.iamworkin.lan"]
|
||||||
labels:
|
labels:
|
||||||
instance: "piez"
|
instance: "piez"
|
||||||
service: "pimanager"
|
service: "signalcontrol"
|
||||||
vlan: "home"
|
vlan: "home"
|
||||||
device: "pi4-ezconnect"
|
device: "pi4-ezconnect"
|
||||||
- targets: ["10.0.58.113:5200"]
|
rig: "signal-b"
|
||||||
|
- targets: ["pirelay.iamworkin.lan"]
|
||||||
labels:
|
labels:
|
||||||
instance: "pirelay"
|
instance: "pirelay"
|
||||||
service: "pimanager"
|
service: "signalcontrol"
|
||||||
vlan: "home"
|
vlan: "home"
|
||||||
device: "pi3-ks0212"
|
device: "pi3-ks0212"
|
||||||
|
rig: "signal-a"
|
||||||
|
|
||||||
# Epson ET-3750 EcoTank Printer SNMP
|
# Epson ET-3750 EcoTank Printer SNMP
|
||||||
- job_name: "snmp-printer"
|
- job_name: "snmp-printer"
|
||||||
@@ -481,22 +486,31 @@ data:
|
|||||||
- "https://intranet.iamworkin.lan/"
|
- "https://intranet.iamworkin.lan/"
|
||||||
- "https://signage.iamworkin.lan/healthz" # root 401 auth-gated 2026-06-01; /healthz anon 200
|
- "https://signage.iamworkin.lan/healthz" # root 401 auth-gated 2026-06-01; /healthz anon 200
|
||||||
- "https://kiosk.iamworkin.lan/"
|
- "https://kiosk.iamworkin.lan/"
|
||||||
- "https://media.iamworkin.lan/healthz" # root auth-gated by OIDC; /healthz anon 200
|
- "https://media.iamworkin.lan/healthz" # root auth-gated by OIDC; /healthz anonymous 200
|
||||||
- "https://mysql.iamworkin.lan/healthz" # root 401 auth-gated 2026-06-01; /healthz anon 200
|
- "https://mysql.iamworkin.lan/healthz" # root 401 auth-gated 2026-06-01; /healthz anon 200
|
||||||
- "https://php.iamworkin.lan/healthz" # root 401 auth-gated 2026-06-01; /healthz anon 200
|
- "https://php.iamworkin.lan/healthz" # root 401 auth-gated 2026-06-01; /healthz anon 200
|
||||||
- "https://zabbix.iamworkin.lan/"
|
- "https://zabbix.iamworkin.lan/"
|
||||||
- "https://desktop.iamworkin.lan/"
|
- "https://desktop.iamworkin.lan/"
|
||||||
- "https://print.iamworkin.lan/"
|
- "https://print.iamworkin.lan/healthz" # root 401 behind API key auth; /healthz anonymous 200
|
||||||
- "https://dns.iamworkin.lan/healthz" # root auth-gated by OIDC; /healthz anon 200
|
- "https://dns.iamworkin.lan/healthz" # root auth-gated by OIDC; /healthz anonymous 200
|
||||||
- "https://chat.iamworkin.lan/"
|
- "https://signalcontrol.iamworkin.lan/health" # FlowerCore.SignalControl Pi control plane
|
||||||
- "https://dist.iamworkin.lan/healthz" # root/admin auth-gated by OIDC; /healthz anon 200
|
- "https://flowercore.iamworkin.lan/healthz" # FlowerCore landing
|
||||||
- "https://dms.iamworkin.lan/"
|
- "https://replay.iamworkin.lan/healthz" # FlowerCore.Signage replay surface
|
||||||
|
- "https://worldbuilder.iamworkin.lan/healthz" # FlowerCore.WorldBuilder
|
||||||
|
- "https://updates.iamworkin.lan/api/v1/manifests/_schema" # UpdateCenter plural LAN alias
|
||||||
|
- "https://updatecenter-internal.iamworkin.lan/api/v1/manifests/_schema" # internal UC schema route
|
||||||
|
- "https://chat.iamworkin.lan/healthz" # OIDC staged; keep blackbox off root before enforcement flips
|
||||||
|
- "https://dist.iamworkin.lan/healthz" # root/admin auth-gated by OIDC; /healthz anonymous 200
|
||||||
|
- "https://dms.iamworkin.lan/healthz" # future OIDC posture; health route is already anonymous/live
|
||||||
- "https://menuboard.iamworkin.lan/"
|
- "https://menuboard.iamworkin.lan/"
|
||||||
- "https://messageboard.iamworkin.lan/"
|
- "https://messageboard.iamworkin.lan/"
|
||||||
- "https://presentations.iamworkin.lan/"
|
- "https://presentations.iamworkin.lan/"
|
||||||
- "https://retail.iamworkin.lan/"
|
- "https://retail.iamworkin.lan/"
|
||||||
- "https://ttsreader.iamworkin.lan/"
|
- "https://ttsreader.iamworkin.lan/"
|
||||||
# Explicit healthcheck paths
|
# Explicit healthcheck paths
|
||||||
|
- "https://library.iamworkin.lan/health"
|
||||||
|
- "https://aistation.iamworkin.lan/healthz"
|
||||||
|
- "https://knowledge.iamworkin.lan/healthz"
|
||||||
- "https://fc-llm-bridge.iamworkin.lan/healthz"
|
- "https://fc-llm-bridge.iamworkin.lan/healthz"
|
||||||
- "https://acme.iamworkin.lan/health"
|
- "https://acme.iamworkin.lan/health"
|
||||||
# NOTE: services intentionally NOT in this probe surface
|
# NOTE: services intentionally NOT in this probe surface
|
||||||
@@ -908,12 +922,13 @@ data:
|
|||||||
# of idle and SNMP times out, so 5m for: would page nightly. A
|
# of idle and SNMP times out, so 5m for: would page nightly. A
|
||||||
# genuine printer outage (jam, disconnected) lasts well over 30m.
|
# genuine printer outage (jam, disconnected) lasts well over 30m.
|
||||||
- alert: EpsonPrinterDown
|
- alert: EpsonPrinterDown
|
||||||
expr: up{job="snmp-printer"} == 0
|
expr: (max_over_time(up{job="snmp-printer"}[35m]) == bool 0) == 1 and (hour() >= 13 or hour() < 1)
|
||||||
for: 30m
|
for: 30m
|
||||||
labels:
|
labels:
|
||||||
severity: warning
|
severity: info
|
||||||
|
alert_channel: irc
|
||||||
annotations:
|
annotations:
|
||||||
summary: "Epson ET-3750 SNMP unreachable for >30m (likely actual fault, not sleep)"
|
summary: "Epson ET-3750 SNMP unreachable during waking hours (30m)"
|
||||||
|
|
||||||
- alert: SynologyDiskLow
|
- alert: SynologyDiskLow
|
||||||
expr: hrStorageUsed{job="snmp-nas"} / hrStorageSize{job="snmp-nas"} * 100 > 85
|
expr: hrStorageUsed{job="snmp-nas"} / hrStorageSize{job="snmp-nas"} * 100 > 85
|
||||||
@@ -1020,7 +1035,12 @@ data:
|
|||||||
- name: kubernetes-state
|
- name: kubernetes-state
|
||||||
rules:
|
rules:
|
||||||
- alert: KubeContainerRestartingFrequently
|
- alert: KubeContainerRestartingFrequently
|
||||||
expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
|
# Exclude github-runner: ephemeral runners register, run one job,
|
||||||
|
# exit cleanly, and restart by design. Also require kube_pod_info so
|
||||||
|
# deleted rollout pods do not keep firing from retained restart series.
|
||||||
|
expr: |
|
||||||
|
increase(kube_pod_container_status_restarts_total{namespace!="github-runner"}[1h]) > 5
|
||||||
|
and on(namespace, pod) kube_pod_info
|
||||||
for: 15m
|
for: 15m
|
||||||
labels:
|
labels:
|
||||||
severity: warning
|
severity: warning
|
||||||
@@ -1029,7 +1049,12 @@ data:
|
|||||||
description: "Container {{ $labels.container }} in pod {{ $labels.namespace }}/{{ $labels.pod }} has restarted {{ $value | printf \"%.0f\" }} times in the last hour. Check 'kubectl describe pod' + last-state termination reason."
|
description: "Container {{ $labels.container }} in pod {{ $labels.namespace }}/{{ $labels.pod }} has restarted {{ $value | printf \"%.0f\" }} times in the last hour. Check 'kubectl describe pod' + last-state termination reason."
|
||||||
|
|
||||||
- alert: KubeContainerCrashLooping
|
- alert: KubeContainerCrashLooping
|
||||||
expr: increase(kube_pod_container_status_restarts_total[15m]) > 3
|
# Same github-runner/delete-retention exclusions as the hourly
|
||||||
|
# restart rule above; real runner failures are covered by the
|
||||||
|
# dedicated LinuxRunnerOffline/MacMiniRunnerOffline alerts.
|
||||||
|
expr: |
|
||||||
|
increase(kube_pod_container_status_restarts_total{namespace!="github-runner"}[15m]) > 3
|
||||||
|
and on(namespace, pod) kube_pod_info
|
||||||
for: 5m
|
for: 5m
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
@@ -1057,7 +1082,10 @@ data:
|
|||||||
description: "Pod can't pull image. Check the image ref (often a stale tag or unreachable registry) and clean up if it's an orphan."
|
description: "Pod can't pull image. Check the image ref (often a stale tag or unreachable registry) and clean up if it's an orphan."
|
||||||
|
|
||||||
- alert: KubeDeploymentReplicasMismatch
|
- alert: KubeDeploymentReplicasMismatch
|
||||||
expr: kube_deployment_spec_replicas != kube_deployment_status_replicas_available
|
# github-runner has explicit runner-offline alerts; the generic
|
||||||
|
# replica-mismatch rule should not page on intentionally ephemeral
|
||||||
|
# 0/1 runner churn between CI jobs.
|
||||||
|
expr: kube_deployment_spec_replicas{namespace!="github-runner"} != kube_deployment_status_replicas_available{namespace!="github-runner"}
|
||||||
for: 15m
|
for: 15m
|
||||||
labels:
|
labels:
|
||||||
severity: warning
|
severity: warning
|
||||||
|
|||||||
@@ -114,6 +114,9 @@ spec:
|
|||||||
app: telephony-web
|
app: telephony-web
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/health"
|
||||||
labels:
|
labels:
|
||||||
app: telephony-web
|
app: telephony-web
|
||||||
spec:
|
spec:
|
||||||
@@ -161,6 +164,7 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 5100
|
- containerPort: 5100
|
||||||
name: http
|
name: http
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: Telephony__Twilio__AccountSid
|
- name: Telephony__Twilio__AccountSid
|
||||||
valueFrom:
|
valueFrom:
|
||||||
@@ -387,4 +391,3 @@ spec:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,28 +12,27 @@ Source: `D:\git\FlowerCore\FlowerCore.WorldBuilder` (master)
|
|||||||
in pfSense Unbound before this manifest is applied, or cert-manager
|
in pfSense Unbound before this manifest is applied, or cert-manager
|
||||||
HTTP-01 silently exponential-backs-off ~2h.
|
HTTP-01 silently exponential-backs-off ~2h.
|
||||||
Memory: `feedback_pfsense_dns_required_for_acme`.
|
Memory: `feedback_pfsense_dns_required_for_acme`.
|
||||||
2. **Image import to ALL RKE2 nodes** — pod can schedule to any of
|
2. **Image import to ALL Ready RKE2 nodes** — pod can currently schedule to
|
||||||
`rke2-server` (10.0.56.11), `rke2-agent1` (10.0.56.12),
|
`rke2-server` (10.0.56.11) and `rke2-agent1` (10.0.56.12). Build with:
|
||||||
`rke2-agent2` (10.0.56.13). Build with:
|
|
||||||
```bash
|
```bash
|
||||||
bash deploy/build.sh # in FlowerCore.WorldBuilder repo
|
bash deploy/build.sh # in FlowerCore.WorldBuilder repo
|
||||||
podman save localhost/fc-worldbuilder:v<TAG> -o /tmp/fc-worldbuilder-v<TAG>.tar
|
mkdir -p artifacts/deploy
|
||||||
for h in 10.0.56.11 10.0.56.12 10.0.56.13; do
|
podman save localhost/fc-worldbuilder:v<TAG> -o artifacts/deploy/fc-worldbuilder-v<TAG>.tar
|
||||||
scp /tmp/fc-worldbuilder-v<TAG>.tar fcadmin@$h:/tmp/
|
for h in 10.0.56.11 10.0.56.12; do
|
||||||
|
ssh fcadmin@$h "mkdir -p /home/fcadmin/.fcv"
|
||||||
|
scp artifacts/deploy/fc-worldbuilder-v<TAG>.tar fcadmin@$h:/home/fcadmin/.fcv/
|
||||||
ssh fcadmin@$h \
|
ssh fcadmin@$h \
|
||||||
"sudo /var/lib/rancher/rke2/bin/ctr -a /run/k3s/containerd/containerd.sock \
|
"sudo /var/lib/rancher/rke2/bin/ctr -a /run/k3s/containerd/containerd.sock \
|
||||||
-n k8s.io images import /tmp/fc-worldbuilder-v<TAG>.tar"
|
-n k8s.io images import /home/fcadmin/.fcv/fc-worldbuilder-v<TAG>.tar"
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
Memory: `feedback_rke2_image_import_per_node_scp`.
|
Memory: `feedback_rke2_image_import_per_node_scp`.
|
||||||
3. **Bump image tag** in `worldbuilder.yaml` and git push.
|
3. **Bump image tag** in `worldbuilder.yaml` and git push.
|
||||||
ArgoCD ApplicationSet picks up within ~3 minutes.
|
ArgoCD ApplicationSet picks up within ~3 minutes.
|
||||||
4. **First production render** — open
|
4. **First production render** — verify
|
||||||
`https://worldbuilder.iamworkin.lan/studio/c32e0000-0000-4000-8000-000000000004`
|
`https://worldbuilder.iamworkin.lan/healthz`, open
|
||||||
and confirm the Cyberpunk Blue Jay demo prompt loads with five seeded fake
|
`https://worldbuilder.iamworkin.lan/settings`, and confirm the image backend
|
||||||
generated images. This Sprint 32 visitor-safe profile uses
|
reports ComfyUI before running an operator-owned render lane.
|
||||||
`ClientMode=fake`; switch the image-generation env vars back to ComfyUI only
|
|
||||||
for an operator-owned GPU render lane.
|
|
||||||
|
|
||||||
## Health probes
|
## Health probes
|
||||||
|
|
||||||
@@ -56,13 +55,8 @@ Source: `D:\git\FlowerCore\FlowerCore.WorldBuilder` (master)
|
|||||||
|
|
||||||
## Image generation backend
|
## Image generation backend
|
||||||
|
|
||||||
Sprint 32 pins the Kubernetes profile to
|
The live internal profile now uses
|
||||||
`FlowerCore:WorldBuilder:ImageGeneration:ClientMode=fake` with
|
`FlowerCore:WorldBuilder:ImageGeneration:ClientMode=comfyui` with
|
||||||
`BaseUrl=http://127.0.0.1:1`. That keeps the public/internal visitor demo
|
`BaseUrl=http://10.0.56.20:8188` on BLUEJAY-WS (R9700 / gfx1201 / ROCm 7.2).
|
||||||
deterministic, avoids GPU exposure, and still exercises the studio/gallery
|
Keep the public host pre-staging disabled unless the five safe-to-expose gates
|
||||||
surface with persisted generated-image metadata.
|
are rechecked; the live GPU lane is operator-owned and internal-only.
|
||||||
|
|
||||||
The previous ComfyUI backend target was `http://10.0.56.20:8188` on
|
|
||||||
BLUEJAY-WS (R9700 / gfx1201 / ROCm 7.2.1). Re-enable it only in an
|
|
||||||
operator-owned follow-up that also verifies workstation reachability and image
|
|
||||||
import freshness.
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
#
|
#
|
||||||
# Image build (BLUEJAY-WS):
|
# Image build (BLUEJAY-WS):
|
||||||
# bash deploy/build.sh # in FlowerCore.WorldBuilder repo
|
# bash deploy/build.sh # in FlowerCore.WorldBuilder repo
|
||||||
# podman save localhost/fc-worldbuilder:v<TAG> -o /tmp/fc-worldbuilder-v<TAG>.tar
|
# podman save localhost/fc-worldbuilder:v<TAG> -o artifacts/deploy/fc-worldbuilder-v<TAG>.tar
|
||||||
# for h in 10.0.56.11 10.0.56.12 10.0.56.13; do
|
# for h in 10.0.56.11 10.0.56.12; do
|
||||||
# scp /tmp/fc-worldbuilder-v<TAG>.tar fcadmin@$h:/tmp/
|
# scp artifacts/deploy/fc-worldbuilder-v<TAG>.tar fcadmin@$h:/home/fcadmin/.fcv/
|
||||||
# ssh fcadmin@$h "sudo /var/lib/rancher/rke2/bin/ctr -a /run/k3s/containerd/containerd.sock -n k8s.io images import /tmp/fc-worldbuilder-v<TAG>.tar"
|
# ssh fcadmin@$h "sudo /var/lib/rancher/rke2/bin/ctr -a /run/k3s/containerd/containerd.sock -n k8s.io images import /home/fcadmin/.fcv/fc-worldbuilder-v<TAG>.tar"
|
||||||
# done
|
# done
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -77,6 +77,8 @@ spec:
|
|||||||
flowercore.io/tenant-id: system
|
flowercore.io/tenant-id: system
|
||||||
flowercore.io/created-by: bluejay-infra
|
flowercore.io/created-by: bluejay-infra
|
||||||
annotations:
|
annotations:
|
||||||
|
fc.flowercore.io/healthz-anon: "true"
|
||||||
|
fc.flowercore.io/probe-path: "/healthz"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
@@ -88,11 +90,12 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
# Bump tag for each rebuild. Initial deploy: v202605062048
|
# Bump tag for each rebuild. Initial deploy: v202605062048
|
||||||
image: localhost/fc-worldbuilder:v202605062048
|
image: localhost/fc-worldbuilder:v202606121657-35aaa2c-gpu
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
|
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -114,14 +117,16 @@ spec:
|
|||||||
value: "/data/gallery"
|
value: "/data/gallery"
|
||||||
- name: FlowerCore__WorldBuilder__Export__RootPath
|
- name: FlowerCore__WorldBuilder__Export__RootPath
|
||||||
value: "/data/exports"
|
value: "/data/exports"
|
||||||
# Visitor-safe Sprint 32 profile: fake backend keeps public demo
|
# Operator-approved live GPU lane. Internal-only host targets
|
||||||
# rendering deterministic and avoids exposing BLUEJAY-WS GPU.
|
# BLUEJAY-WS ComfyUI; keep public host pre-staging disabled below.
|
||||||
- name: FlowerCore__WorldBuilder__ImageGeneration__BaseUrl
|
- name: FlowerCore__WorldBuilder__ImageGeneration__BaseUrl
|
||||||
value: "http://127.0.0.1:1"
|
value: "http://10.0.56.20:8188"
|
||||||
- name: FlowerCore__WorldBuilder__ImageGeneration__ClientMode
|
- name: FlowerCore__WorldBuilder__ImageGeneration__ClientMode
|
||||||
value: "fake"
|
value: "comfyui"
|
||||||
- name: FlowerCore__WorldBuilder__ImageGeneration__BackendId
|
- name: FlowerCore__WorldBuilder__ImageGeneration__BackendId
|
||||||
value: "fake"
|
value: "comfyui"
|
||||||
|
- name: FlowerCore__WorldBuilder__ImageGeneration__VisitorSafe
|
||||||
|
value: "false"
|
||||||
resources:
|
resources:
|
||||||
# Cluster CPU-request budget runs hot (99% on all 3 nodes at deploy
|
# Cluster CPU-request budget runs hot (99% on all 3 nodes at deploy
|
||||||
# time) while actual CPU usage is well below capacity. Idle Blazor
|
# time) while actual CPU usage is well below capacity. Idle Blazor
|
||||||
@@ -254,3 +259,26 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: worldbuilder-web-tls
|
secretName: worldbuilder-web-tls
|
||||||
|
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
||||||
|
# When the operator decides to expose worldbuilder-web publicly, uncomment + update the host,
|
||||||
|
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
||||||
|
#
|
||||||
|
# --- IngressRoute ---
|
||||||
|
# apiVersion: traefik.io/v1alpha1
|
||||||
|
# kind: IngressRoute
|
||||||
|
# metadata:
|
||||||
|
# name: worldbuilder-web-public
|
||||||
|
# namespace: worldbuilder
|
||||||
|
# spec:
|
||||||
|
# entryPoints: [websecure]
|
||||||
|
# routes:
|
||||||
|
# - match: Host(`worldbuilder.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
|
# kind: Rule
|
||||||
|
# middlewares:
|
||||||
|
# - name: worldbuilder-web-public-profile-header # injects entitlement profile
|
||||||
|
# services:
|
||||||
|
# - name: worldbuilder-web
|
||||||
|
# port: 80
|
||||||
|
# tls: {}
|
||||||
|
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
||||||
|
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
||||||
|
|||||||
@@ -17,21 +17,17 @@ public sealed class FleetManifestLintTests
|
|||||||
"dist.flowercore.io",
|
"dist.flowercore.io",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public hosts that allow a tightly bounded write surface in addition to
|
// Hosts that allow a tightly bounded write surface in addition to GET/HEAD.
|
||||||
// GET/HEAD. updatecenter.iamworkin.lan accepts POST /api/v1/checkin/{id}
|
// updatecenter.iamworkin.lan accepts POST /api/v1/checkin/{id}
|
||||||
// (bootstrap-JWT) so its allowlist is GET||HEAD||POST||OPTIONS — but
|
// (bootstrap-JWT) so its allowlist is GET||HEAD||POST||OPTIONS — but
|
||||||
// PUT/PATCH/DELETE must still 404 at the route. Anything wider than this
|
// PUT/PATCH/DELETE must still 404 at the route. Public
|
||||||
// set should fail this lint.
|
// update.flowercore.io remains a GET/HEAD download surface in the
|
||||||
//
|
// FlowerCore.Updater sibling manifest and is covered by the general
|
||||||
// PUB-1 (2026-05-06): update.flowercore.io / updates.flowercore.io were
|
// public-method allowlist lint instead of this write-surface rule.
|
||||||
// added for the Cloudflare-proxied public Update Center edge. They use the
|
|
||||||
// same bounded read-write allowlist as the LAN pair.
|
|
||||||
private static readonly HashSet<string> PublicReadWriteAllowlistHosts = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> PublicReadWriteAllowlistHosts = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
"updatecenter.iamworkin.lan",
|
"updatecenter.iamworkin.lan",
|
||||||
"updates.iamworkin.lan",
|
"updates.iamworkin.lan",
|
||||||
"update.flowercore.io",
|
|
||||||
"updates.flowercore.io",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly HashSet<string> ApiKeyProtectedDeployments = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> ApiKeyProtectedDeployments = new(StringComparer.Ordinal)
|
||||||
@@ -69,7 +65,7 @@ public sealed class FleetManifestLintTests
|
|||||||
["github-runner-updater"] = "https://github.com/astoltz/FlowerCore.Updater",
|
["github-runner-updater"] = "https://github.com/astoltz/FlowerCore.Updater",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly HashSet<string> ScaledLinuxRunnerDeployments = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> RepoScopedLinuxRunnerDeployments = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
"github-runner-sharedpos",
|
"github-runner-sharedpos",
|
||||||
"github-runner-puppet",
|
"github-runner-puppet",
|
||||||
@@ -83,6 +79,44 @@ public sealed class FleetManifestLintTests
|
|||||||
"github-runner-updater",
|
"github-runner-updater",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, (string Deployment, string ProbePath)> BroaderHardeningDeployments =
|
||||||
|
new Dictionary<string, (string Deployment, string ProbePath)>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["fc-aistation"] = ("aistation-web", "/healthz"),
|
||||||
|
["fc-chat"] = ("chat-web", "/healthz"),
|
||||||
|
["fc-devicemgmt"] = ("fc-devicemgmt-web", "/healthz"),
|
||||||
|
["fc-library"] = ("library-web", "/health"),
|
||||||
|
["fc-llm-bridge"] = ("fc-llm-bridge", "/healthz"),
|
||||||
|
["fc-messageboard"] = ("messageboard-web", "/health"),
|
||||||
|
["fc-retail"] = ("retail-web", "/healthz"),
|
||||||
|
["fc-ttsreader"] = ("ttsreader-web", "/health"),
|
||||||
|
["fc-updater"] = ("updatecenter-web", "/"),
|
||||||
|
["knowledge"] = ("knowledge-web", "/healthz"),
|
||||||
|
["telephony"] = ("telephony-web", "/health"),
|
||||||
|
["worldbuilder"] = ("worldbuilder-web", "/healthz"),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly HashSet<string> BroaderHardeningInternalPrestageApps = new(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
"fc-aistation",
|
||||||
|
"fc-desktop",
|
||||||
|
"fc-dms",
|
||||||
|
"fc-library",
|
||||||
|
"fc-llm-bridge",
|
||||||
|
"fc-menuboard",
|
||||||
|
"fc-messageboard",
|
||||||
|
"fc-mysql",
|
||||||
|
"fc-php",
|
||||||
|
"fc-presentations",
|
||||||
|
"fc-retail",
|
||||||
|
"fc-scoreboard",
|
||||||
|
"fc-segmentdisplay",
|
||||||
|
"fc-signage",
|
||||||
|
"fc-ttsreader",
|
||||||
|
"knowledge",
|
||||||
|
"worldbuilder",
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly IReadOnlyDictionary<string, string> WritableRunnerEnv = new Dictionary<string, string>(StringComparer.Ordinal)
|
private static readonly IReadOnlyDictionary<string, string> WritableRunnerEnv = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
["HOME"] = "/home/runner",
|
["HOME"] = "/home/runner",
|
||||||
@@ -238,6 +272,7 @@ public sealed class FleetManifestLintTests
|
|||||||
var container = deployments[expectedRunner.Key].MainContainerMappings().Should().ContainSingle().Subject;
|
var container = deployments[expectedRunner.Key].MainContainerMappings().Should().ContainSingle().Subject;
|
||||||
EnvValue(container, "REPO_URL").Should().Be(expectedRunner.Value);
|
EnvValue(container, "REPO_URL").Should().Be(expectedRunner.Value);
|
||||||
EnvValue(container, "EPHEMERAL").Should().Be("true");
|
EnvValue(container, "EPHEMERAL").Should().Be("true");
|
||||||
|
EnvValue(container, "DISABLE_AUTO_UPDATE").Should().Be("true", $"{expectedRunner.Key} must not self-update inside immutable Kubernetes runner pods");
|
||||||
EnvValue(container, "LABELS").Should().Be("self-hosted,linux,fc-build-linux");
|
EnvValue(container, "LABELS").Should().Be("self-hosted,linux,fc-build-linux");
|
||||||
EnvValue(container, "RUN_AS_ROOT").Should().Be("false");
|
EnvValue(container, "RUN_AS_ROOT").Should().Be("false");
|
||||||
EnvValue(container, "ACCESS_TOKEN").Should().BeNull("ACCESS_TOKEN must come from github-runner-token Secret, not a literal");
|
EnvValue(container, "ACCESS_TOKEN").Should().BeNull("ACCESS_TOKEN must come from github-runner-token Secret, not a literal");
|
||||||
@@ -271,17 +306,17 @@ public sealed class FleetManifestLintTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GitHubRunnerFleet_MustAvoidRwoMultiAttachForScaledDeployments()
|
public void GitHubRunnerFleet_MustAvoidRwoMultiAttachForRepoScopedDeployments()
|
||||||
{
|
{
|
||||||
var deployments = GitHubRunnerDeployments();
|
var deployments = GitHubRunnerDeployments();
|
||||||
|
|
||||||
foreach (var deploymentName in ScaledLinuxRunnerDeployments)
|
foreach (var deploymentName in RepoScopedLinuxRunnerDeployments)
|
||||||
{
|
{
|
||||||
var deployment = deployments[deploymentName];
|
var deployment = deployments[deploymentName];
|
||||||
// Scaled runners must have >= 2 replicas (avoid single-pod bottleneck).
|
// Sprint 34 ops trimmed runner load while the cluster was degraded
|
||||||
// Individual deployments may be tuned upward per CI activity — see
|
// to two healthy nodes. Repo-scoped runners can be tuned back above
|
||||||
// "runners: right-size replica counts per 14d CI activity (#24)".
|
// one replica, but they must stay RWO-safe before that happens.
|
||||||
ReplicaCount(deployment).Should().BeGreaterOrEqualTo(2, $"{deploymentName} is in the scaled set and must run with at least 2 replicas");
|
ReplicaCount(deployment).Should().BeGreaterOrEqualTo(1, $"{deploymentName} must keep at least one repo-scoped runner online");
|
||||||
|
|
||||||
var volumes = deployment.MappingSequence("spec", "template", "spec", "volumes");
|
var volumes = deployment.MappingSequence("spec", "template", "spec", "volumes");
|
||||||
var claimNames = volumes
|
var claimNames = volumes
|
||||||
@@ -289,7 +324,7 @@ public sealed class FleetManifestLintTests
|
|||||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
claimNames.Should().BeEmpty($"{deploymentName} is scaled and must not share a RWO PVC");
|
claimNames.Should().BeEmpty($"{deploymentName} must remain ready for safe multi-replica scaling without sharing a RWO PVC");
|
||||||
volumes.Should().Contain(volume =>
|
volumes.Should().Contain(volume =>
|
||||||
string.Equals(ManifestNodeExtensions.Scalar(volume, "name"), "nuget-cache", StringComparison.Ordinal)
|
string.Equals(ManifestNodeExtensions.Scalar(volume, "name"), "nuget-cache", StringComparison.Ordinal)
|
||||||
&& ManifestNodeExtensions.Mapping(volume, "emptyDir") != null);
|
&& ManifestNodeExtensions.Mapping(volume, "emptyDir") != null);
|
||||||
@@ -423,6 +458,82 @@ public sealed class FleetManifestLintTests
|
|||||||
monitoring.Should().Contain("alert_channel: irc");
|
monitoring.Should().Contain("alert_channel: irc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Monitoring_GenericKubernetesAlerts_MustExcludeEphemeralGithubRunnerNamespace()
|
||||||
|
{
|
||||||
|
var monitoring = File.ReadAllText(Path.Combine(Inventory.BluejayRoot, "apps", "monitoring", "noc-monitoring.yaml"));
|
||||||
|
|
||||||
|
monitoring.Should().Contain("kube_pod_container_status_restarts_total{namespace!=\"github-runner\"}");
|
||||||
|
monitoring.Should().Contain("and on(namespace, pod) kube_pod_info");
|
||||||
|
monitoring.Should().Contain("kube_deployment_spec_replicas{namespace!=\"github-runner\"} != kube_deployment_status_replicas_available{namespace!=\"github-runner\"}");
|
||||||
|
monitoring.Should().Contain("dedicated LinuxRunnerOffline/MacMiniRunnerOffline alerts");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Monitoring_BlackboxTargetsForOidcSensitiveServices_MustUseAnonymousHealthRoutesWhenAvailable()
|
||||||
|
{
|
||||||
|
var monitoring = File.ReadAllText(Path.Combine(Inventory.BluejayRoot, "apps", "monitoring", "noc-monitoring.yaml"));
|
||||||
|
|
||||||
|
monitoring.Should().Contain("https://chat.iamworkin.lan/healthz");
|
||||||
|
monitoring.Should().Contain("https://dist.iamworkin.lan/healthz");
|
||||||
|
monitoring.Should().Contain("https://dms.iamworkin.lan/healthz");
|
||||||
|
monitoring.Should().Contain("https://print.iamworkin.lan/healthz");
|
||||||
|
monitoring.Should().Contain("https://knowledge.iamworkin.lan/healthz");
|
||||||
|
monitoring.Should().Contain("https://library.iamworkin.lan/health");
|
||||||
|
monitoring.Should().Contain("https://aistation.iamworkin.lan/healthz");
|
||||||
|
monitoring.Should().NotContain("https://print.iamworkin.lan/\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OidcEnforcedDeployments_WithHttpHealthzProbes_MustDeclareAnonymousHealthzContract()
|
||||||
|
{
|
||||||
|
var violations = Inventory.Documents
|
||||||
|
.Where(document => document.Kind == "Deployment")
|
||||||
|
.SelectMany(document => document.MainContainerMappings()
|
||||||
|
.Where(container => string.Equals(EnvValue(container, "FlowerCore__Auth__Enabled"), "true", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Where(container => string.Equals(EnvValue(container, "FlowerCore__Auth__Oidc__Enabled"), "true", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Where(container => ProbeHttpGetPath(container, "readinessProbe") == "/healthz"
|
||||||
|
|| ProbeHttpGetPath(container, "startupProbe") == "/healthz")
|
||||||
|
.Where(_ => !string.Equals(
|
||||||
|
PodAnnotation(document, "flowercore.io/healthz-auth-policy"),
|
||||||
|
"allow-anonymous",
|
||||||
|
StringComparison.Ordinal))
|
||||||
|
.Select(container =>
|
||||||
|
{
|
||||||
|
var containerName = ManifestNodeExtensions.Scalar(container, "name") ?? "<unnamed>";
|
||||||
|
return $"{document.Descriptor} container '{containerName}' enforces OIDC while probing /healthz but lacks flowercore.io/healthz-auth-policy: allow-anonymous.";
|
||||||
|
}))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
violations.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Knowledge_OidcEnforcement_MustKeepHealthzAnonymousContractVisibleInManifest()
|
||||||
|
{
|
||||||
|
var knowledge = Inventory.Documents
|
||||||
|
.Single(document => document.Kind == "Deployment" && document.Namespace == "knowledge" && document.Name == "knowledge-web");
|
||||||
|
var container = knowledge.MainContainerMappings().Should().ContainSingle().Subject;
|
||||||
|
|
||||||
|
EnvValue(container, "FlowerCore__Auth__Enabled").Should().Be("true");
|
||||||
|
EnvValue(container, "FlowerCore__Auth__Oidc__Enabled").Should().Be("true");
|
||||||
|
ProbeHttpGetPath(container, "readinessProbe").Should().Be("/healthz");
|
||||||
|
PodAnnotation(knowledge, "flowercore.io/healthz-auth-policy").Should().Be("allow-anonymous");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Distribution_OidcEnforcement_MustKeepHealthzAnonymousContractVisibleInManifest()
|
||||||
|
{
|
||||||
|
var distribution = Inventory.Documents
|
||||||
|
.Single(document => document.Kind == "Deployment" && document.Namespace == "fc-distribution" && document.Name == "fc-distribution");
|
||||||
|
var container = distribution.MainContainerMappings().Should().ContainSingle().Subject;
|
||||||
|
|
||||||
|
EnvValue(container, "FlowerCore__Auth__Oidc__Enabled").Should().Be("true");
|
||||||
|
EnvValue(container, "FlowerCore__Auth__Enabled").Should().Be("true");
|
||||||
|
ProbeHttpGetPath(container, "readinessProbe").Should().Be("/healthz");
|
||||||
|
PodAnnotation(distribution, "flowercore.io/healthz-auth-policy").Should().Be("allow-anonymous");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StatefulSets_WithVolumeClaimTemplates_MustDeclareFilesystemDefaults()
|
public void StatefulSets_WithVolumeClaimTemplates_MustDeclareFilesystemDefaults()
|
||||||
{
|
{
|
||||||
@@ -536,10 +647,10 @@ public sealed class FleetManifestLintTests
|
|||||||
var expectedFiles = new[]
|
var expectedFiles = new[]
|
||||||
{
|
{
|
||||||
"1password-item.yaml",
|
"1password-item.yaml",
|
||||||
"argocd-application.yaml",
|
|
||||||
"certificate-web.yaml",
|
"certificate-web.yaml",
|
||||||
"clusterrole-operator.yaml",
|
"clusterrole-operator.yaml",
|
||||||
"clusterrolebinding-operator.yaml",
|
"clusterrolebinding-operator.yaml",
|
||||||
|
"crds.yaml",
|
||||||
"deployment-operator.yaml",
|
"deployment-operator.yaml",
|
||||||
"deployment-web.yaml",
|
"deployment-web.yaml",
|
||||||
"ingressroute-web.yaml",
|
"ingressroute-web.yaml",
|
||||||
@@ -629,7 +740,8 @@ public sealed class FleetManifestLintTests
|
|||||||
.Single(document => document.Kind == "ClusterRole" && document.Name == "fc-devicemgmt-operator");
|
.Single(document => document.Kind == "ClusterRole" && document.Name == "fc-devicemgmt-operator");
|
||||||
var allScalars = clusterRole.AllScalars().ToList();
|
var allScalars = clusterRole.AllScalars().ToList();
|
||||||
|
|
||||||
allScalars.Should().Contain("devices.flowercore.io");
|
allScalars.Should().Contain("flowercore.io");
|
||||||
|
allScalars.Should().NotContain("devices.flowercore.io");
|
||||||
allScalars.Should().Contain("*");
|
allScalars.Should().Contain("*");
|
||||||
allScalars.Should().Contain("deployments");
|
allScalars.Should().Contain("deployments");
|
||||||
allScalars.Should().Contain("get");
|
allScalars.Should().Contain("get");
|
||||||
@@ -658,7 +770,7 @@ public sealed class FleetManifestLintTests
|
|||||||
|
|
||||||
FcDeviceManagementDocuments().Should().NotContain(document => document.Kind == "Secret");
|
FcDeviceManagementDocuments().Should().NotContain(document => document.Kind == "Secret");
|
||||||
appText.Should().Contain("secretKeyRef:");
|
appText.Should().Contain("secretKeyRef:");
|
||||||
appText.Should().Contain("secretName: fc-devicemgmt-runtime");
|
appText.Should().Contain("name: fc-devicemgmt-runtime");
|
||||||
appText.Should().NotContain("stringData:");
|
appText.Should().NotContain("stringData:");
|
||||||
appText.Should().NotContain("from-literal");
|
appText.Should().NotContain("from-literal");
|
||||||
appText.Should().NotContain("tls.key:");
|
appText.Should().NotContain("tls.key:");
|
||||||
@@ -692,17 +804,62 @@ public sealed class FleetManifestLintTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FcDeviceManagement_ArgocdApplicationMustMatchApplicationSetDiscoveryConventions()
|
public void FcDeviceManagement_MustRelyOnApplicationSetDiscovery()
|
||||||
{
|
{
|
||||||
var application = FcDeviceManagementDocuments()
|
var documents = FcDeviceManagementDocuments();
|
||||||
.Single(document => document.Kind == "Application" && document.Name == "infra-fc-devicemgmt");
|
|
||||||
|
|
||||||
application.Namespace.Should().Be("argocd");
|
documents.Should().NotContain(document => document.Kind == "Application");
|
||||||
application.Scalar("spec", "source", "repoURL")
|
|
||||||
.Should()
|
var ns = documents.Single(document => document.Kind == "Namespace" && document.Name == "fc-devicemgmt");
|
||||||
.Be("http://gitea-clusterip.gitea.svc.cluster.local:3000/bluejay/bluejay-infra.git");
|
ns.FileText.Should().Contain("ArgoCD discovers this directory as Application `infra-fc-devicemgmt`.");
|
||||||
application.Scalar("spec", "source", "path").Should().Be("apps/fc-devicemgmt");
|
}
|
||||||
application.Scalar("spec", "destination", "namespace").Should().Be("fc-devicemgmt");
|
|
||||||
|
[Fact]
|
||||||
|
public void BroaderHardeningDeployments_MustAnnotateAnonymousHealthProbeIntent()
|
||||||
|
{
|
||||||
|
foreach (var expected in BroaderHardeningDeployments)
|
||||||
|
{
|
||||||
|
var deployment = AppDocuments(expected.Key)
|
||||||
|
.Single(document => document.Kind == "Deployment" && document.Name == expected.Value.Deployment);
|
||||||
|
|
||||||
|
PodAnnotation(deployment, "fc.flowercore.io/healthz-anon").Should().Be("true");
|
||||||
|
PodAnnotation(deployment, "fc.flowercore.io/probe-path").Should().Be(expected.Value.ProbePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BroaderHardeningDeployments_MustDocumentForwardedProtoAuthPosture()
|
||||||
|
{
|
||||||
|
foreach (var expected in BroaderHardeningDeployments)
|
||||||
|
{
|
||||||
|
var deployment = AppDocuments(expected.Key)
|
||||||
|
.Single(document => document.Kind == "Deployment" && document.Name == expected.Value.Deployment);
|
||||||
|
|
||||||
|
deployment.FileText.Should().Contain(
|
||||||
|
"fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BroaderHardeningInternalApps_MustOnlyPrestageCommentedPublicMethodAllowlist()
|
||||||
|
{
|
||||||
|
foreach (var app in BroaderHardeningInternalPrestageApps)
|
||||||
|
{
|
||||||
|
var documents = AppDocuments(app);
|
||||||
|
var text = string.Join(Environment.NewLine, documents.Select(document => document.FileText));
|
||||||
|
|
||||||
|
text.Should().Contain("PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only)");
|
||||||
|
text.Should().Contain("# - match: Host(`");
|
||||||
|
text.Should().Contain("Method(`GET`) || Method(`HEAD`)");
|
||||||
|
|
||||||
|
documents
|
||||||
|
.Where(document => document.Kind == "IngressRoute")
|
||||||
|
.SelectMany(document => document.MappingSequence("spec", "routes"))
|
||||||
|
.Select(route => ManifestNodeExtensions.Scalar(route, "match") ?? string.Empty)
|
||||||
|
.Should()
|
||||||
|
.NotContain(match => match.Contains(".flowercore.io", StringComparison.Ordinal),
|
||||||
|
"Sprint 61 broader hardening only pre-stages commented public hosts for internal-only apps");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -710,9 +867,9 @@ public sealed class FleetManifestLintTests
|
|||||||
{
|
{
|
||||||
var deployments = new[]
|
var deployments = new[]
|
||||||
{
|
{
|
||||||
(App: "fc-dns", Name: "dns-web", Slug: "dns", Secret: "dns-oidc-client"),
|
(App: "fc-dns", Name: "dns-web", Slug: "dns", Secret: "dns-oidc-client", AuthEnabled: "false"),
|
||||||
(App: "fc-media", Name: "fc-media-web", Slug: "media", Secret: "media-oidc-client"),
|
(App: "fc-media", Name: "fc-media-web", Slug: "media", Secret: "media-oidc-client", AuthEnabled: "true"),
|
||||||
(App: "fc-distribution", Name: "fc-distribution", Slug: "distribution", Secret: "distribution-oidc-client"),
|
(App: "fc-distribution", Name: "fc-distribution", Slug: "distribution", Secret: "distribution-oidc-client", AuthEnabled: "true"),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var expected in deployments)
|
foreach (var expected in deployments)
|
||||||
@@ -721,7 +878,7 @@ public sealed class FleetManifestLintTests
|
|||||||
.Single(document => document.Kind == "Deployment" && document.Name == expected.Name);
|
.Single(document => document.Kind == "Deployment" && document.Name == expected.Name);
|
||||||
var container = deployment.MainContainerMappings().Should().ContainSingle().Subject;
|
var container = deployment.MainContainerMappings().Should().ContainSingle().Subject;
|
||||||
|
|
||||||
EnvValue(container, "FlowerCore__Auth__Enabled").Should().Be("true");
|
EnvValue(container, "FlowerCore__Auth__Enabled").Should().Be(expected.AuthEnabled);
|
||||||
EnvValue(container, "FlowerCore__Auth__Oidc__Enabled").Should().Be("true");
|
EnvValue(container, "FlowerCore__Auth__Oidc__Enabled").Should().Be("true");
|
||||||
(EnvValue(container, "FlowerCore__Auth__Oidc__Audience") ?? EnvValue(container, "FlowerCore__Auth__Oidc__ClientId"))
|
(EnvValue(container, "FlowerCore__Auth__Oidc__Audience") ?? EnvValue(container, "FlowerCore__Auth__Oidc__ClientId"))
|
||||||
.Should()
|
.Should()
|
||||||
@@ -770,7 +927,7 @@ public sealed class FleetManifestLintTests
|
|||||||
var dnsPvc = AppDocuments("fc-dns")
|
var dnsPvc = AppDocuments("fc-dns")
|
||||||
.Single(document => document.Kind == "PersistentVolumeClaim" && document.Name == "dns-web-data");
|
.Single(document => document.Kind == "PersistentVolumeClaim" && document.Name == "dns-web-data");
|
||||||
|
|
||||||
ManifestNodeExtensions.Scalar(dnsContainer, "image").Should().Be("localhost/fc-dns-web:v20260604-oidc-proper");
|
ManifestNodeExtensions.Scalar(dnsContainer, "image").Should().Be("localhost/fc-dns-web:v20260612-l4dns-a5d2849");
|
||||||
dnsPvc.Scalar("spec", "storageClassName").Should().Be("longhorn");
|
dnsPvc.Scalar("spec", "storageClassName").Should().Be("longhorn");
|
||||||
dnsPvc.Scalar("spec", "resources", "requests", "storage").Should().Be("1Gi");
|
dnsPvc.Scalar("spec", "resources", "requests", "storage").Should().Be("1Gi");
|
||||||
|
|
||||||
@@ -795,7 +952,7 @@ public sealed class FleetManifestLintTests
|
|||||||
.Single(document => document.Kind == "Deployment" && document.Name == "fc-distribution");
|
.Single(document => document.Kind == "Deployment" && document.Name == "fc-distribution");
|
||||||
var distributionContainer = distributionDeployment.MainContainerMappings().Should().ContainSingle().Subject;
|
var distributionContainer = distributionDeployment.MainContainerMappings().Should().ContainSingle().Subject;
|
||||||
|
|
||||||
ManifestNodeExtensions.Scalar(distributionContainer, "image").Should().Be("localhost/fc-distribution:v20260604-oidc-proper");
|
ManifestNodeExtensions.Scalar(distributionContainer, "image").Should().Be("localhost/fc-distribution:v20260604-oidc-root-anon");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -926,6 +1083,19 @@ public sealed class FleetManifestLintTests
|
|||||||
.SingleOrDefault(env => string.Equals(ManifestNodeExtensions.Scalar(env, "name"), name, StringComparison.Ordinal));
|
.SingleOrDefault(env => string.Equals(ManifestNodeExtensions.Scalar(env, "name"), name, StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string? PodAnnotation(ManifestDocument document, string name)
|
||||||
|
{
|
||||||
|
return document.Scalar("spec", "template", "metadata", "annotations", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ProbeHttpGetPath(YamlMappingNode container, string probeKey)
|
||||||
|
{
|
||||||
|
return ManifestNodeExtensions.TryGetMapping(container, probeKey, out var probe)
|
||||||
|
&& ManifestNodeExtensions.TryGetMapping(probe, "httpGet", out var httpGet)
|
||||||
|
? ManifestNodeExtensions.Scalar(httpGet, "path")
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<ManifestDocument> FcDeviceManagementDocuments()
|
private static IReadOnlyList<ManifestDocument> FcDeviceManagementDocuments()
|
||||||
{
|
{
|
||||||
return Inventory.Documents
|
return Inventory.Documents
|
||||||
|
|||||||
Reference in New Issue
Block a user