From 32610e90902e0015b647bde18e906821dee4d067 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 9 Aug 2026 02:00:13 +0200 Subject: [PATCH] Add coulomb-social Helm chart and deploy targets Chart mirrors vergabe-teilnahme Django probe/secret pattern. Non-secret values pin KeyCape OIDC and user-engine URL; env Secret holds secrets. Makefile: dry-run, deploy, ingress, status, logs. --- Makefile | 40 ++++++++- charts/coulomb-social/Chart.yaml | 18 +++++ charts/coulomb-social/templates/_helpers.tpl | 25 ++++++ .../coulomb-social/templates/deployment.yaml | 81 +++++++++++++++++++ charts/coulomb-social/templates/service.yaml | 27 +++++++ charts/coulomb-social/values.yaml | 73 +++++++++++++++++ docs/coulomb-social.md | 35 ++++++++ helm/coulomb-social-values.yaml | 24 ++++++ manifests/coulomb-social-ingress.yaml | 26 ++++++ 9 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 charts/coulomb-social/Chart.yaml create mode 100644 charts/coulomb-social/templates/_helpers.tpl create mode 100644 charts/coulomb-social/templates/deployment.yaml create mode 100644 charts/coulomb-social/templates/service.yaml create mode 100644 charts/coulomb-social/values.yaml create mode 100644 docs/coulomb-social.md create mode 100644 helm/coulomb-social-values.yaml create mode 100644 manifests/coulomb-social-ingress.yaml diff --git a/Makefile b/Makefile index 88b6cd7..7b3a6fa 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,14 @@ VERGABE_DB_HOST ?= apps-pg-rw.databases VERGABE_DB_PORT ?= 5432 VERGABE_DB_NAME ?= vergabe_db +COULOMB_SOCIAL_RELEASE ?= coulomb-social +COULOMB_SOCIAL_NAMESPACE ?= coulomb-social +COULOMB_SOCIAL_CHART ?= charts/coulomb-social +COULOMB_SOCIAL_VALUES ?= helm/coulomb-social-values.yaml +COULOMB_SOCIAL_IMAGE_REPOSITORY ?= forgejo.coulomb.social/coulomb/coulomb-social +COULOMB_SOCIAL_INGRESS ?= manifests/coulomb-social-ingress.yaml +COULOMB_SOCIAL_ENV_SECRET ?= coulomb-social-env + INTER_HUB_RELEASE ?= inter-hub INTER_HUB_NAMESPACE ?= inter-hub INTER_HUB_CHART ?= charts/inter-hub @@ -259,6 +267,36 @@ vergabe-db-url-secret: ## Rebuild DATABASE_URL with a URL-encoded cnpg password APP_DB_NAME=$(VERGABE_DB_NAME) \ tools/build-database-url-secret.sh + + +check-coulomb-social-image-tag: ## Require an explicit coulomb-social image tag + @test -n "$(COULOMB_SOCIAL_IMAGE_TAG)" || ( \ + echo "Set COULOMB_SOCIAL_IMAGE_TAG= for coulomb-social deploys." >&2; \ + echo "Example: COULOMB_SOCIAL_IMAGE_TAG=deadbeef make coulomb-social-deploy" >&2; \ + exit 1) + +coulomb-social-dry-run: check-coulomb-social-image-tag ## helm template render for coulomb-social + helm template $(COULOMB_SOCIAL_RELEASE) $(COULOMB_SOCIAL_CHART) \ + -f $(COULOMB_SOCIAL_VALUES) \ + --set image.tag=$(COULOMB_SOCIAL_IMAGE_TAG) \ + --namespace $(COULOMB_SOCIAL_NAMESPACE) + +coulomb-social-deploy: check-production-kubeconfig check-coulomb-social-image-tag ## Deploy / upgrade coulomb-social + kubectl create namespace $(COULOMB_SOCIAL_NAMESPACE) --dry-run=client -o yaml | kubectl apply -f - + helm upgrade --install $(COULOMB_SOCIAL_RELEASE) $(COULOMB_SOCIAL_CHART) \ + -f $(COULOMB_SOCIAL_VALUES) \ + --set image.tag=$(COULOMB_SOCIAL_IMAGE_TAG) \ + --namespace $(COULOMB_SOCIAL_NAMESPACE) \ + --wait --timeout 5m + +coulomb-social-ingress-deploy: ## Apply coulomb-social ingress + kubectl apply -f $(COULOMB_SOCIAL_INGRESS) + +coulomb-social-status: check-production-kubeconfig ## Show coulomb-social pod/svc/ingress + kubectl -n $(COULOMB_SOCIAL_NAMESPACE) get deploy,svc,ingress,pods + +coulomb-social-logs: ## Tail coulomb-social app logs + kubectl -n $(COULOMB_SOCIAL_NAMESPACE) logs -l app.kubernetes.io/name=coulomb-social -f --tail=200 ##@ Inter-Hub inter-hub-render-baseline: ## Render checked-in inter-hub values for chart validation only @@ -539,4 +577,4 @@ help: ## Show this help /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \ /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST) -.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status apps-pg-restore-drill check-railiance01-kubeconfig check-inter-hub-image-tag check-inter-hub-image check-vergabe-image-tag check-vergabe-image vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret eso-deploy forgejo-openbao-eso-token-apply forgejo-openbao-store-deploy forgejo-mailer-es-deploy forgejo-mailer-es-status forgejo-dry-run forgejo-server-dry-run forgejo-deploy forgejo-ingress-deploy forgejo-ssh-nodeport-deploy forgejo-status forgejo-smoke forgejo-npm-smoke forgejo-verify forgejo-operator-bootstrap forgejo-runner-registration-sops-bootstrap forgejo-secrets-check forgejo-logs forgejo-runner-registration-deploy forgejo-runner-deploy forgejo-runner-status forgejo-runner-logs inter-hub-render-baseline inter-hub-dry-run inter-hub-server-dry-run inter-hub-deploy inter-hub-status inter-hub-release-info inter-hub-smoke inter-hub-logs reuse-dry-run reuse-deploy reuse-status reuse-smoke reuse-logs reuse-forgejo-webhook reuse-openbao-eso-token-apply reuse-openbao-store-deploy reuse-runtime-es-deploy reuse-runtime-es-status reuse-webhook-smoke help +.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status apps-pg-restore-drill check-railiance01-kubeconfig check-inter-hub-image-tag check-inter-hub-image check-vergabe-image-tag check-vergabe-image vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret check-coulomb-social-image-tag coulomb-social-dry-run coulomb-social-deploy coulomb-social-ingress-deploy coulomb-social-status coulomb-social-logs eso-deploy forgejo-openbao-eso-token-apply forgejo-openbao-store-deploy forgejo-mailer-es-deploy forgejo-mailer-es-status forgejo-dry-run forgejo-server-dry-run forgejo-deploy forgejo-ingress-deploy forgejo-ssh-nodeport-deploy forgejo-status forgejo-smoke forgejo-npm-smoke forgejo-verify forgejo-operator-bootstrap forgejo-runner-registration-sops-bootstrap forgejo-secrets-check forgejo-logs forgejo-runner-registration-deploy forgejo-runner-deploy forgejo-runner-status forgejo-runner-logs inter-hub-render-baseline inter-hub-dry-run inter-hub-server-dry-run inter-hub-deploy inter-hub-status inter-hub-release-info inter-hub-smoke inter-hub-logs reuse-dry-run reuse-deploy reuse-status reuse-smoke reuse-logs reuse-forgejo-webhook reuse-openbao-eso-token-apply reuse-openbao-store-deploy reuse-runtime-es-deploy reuse-runtime-es-status reuse-webhook-smoke help diff --git a/charts/coulomb-social/Chart.yaml b/charts/coulomb-social/Chart.yaml new file mode 100644 index 0000000..89acef2 --- /dev/null +++ b/charts/coulomb-social/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: coulomb-social +description: | + coulomb.social — co-creation platform (NetKingdom identity shell). + Standalone Django app; content migration deferred. +type: application +version: 0.1.0 +appVersion: "0.1.0" +keywords: + - django + - coulomb + - social + - railiance +home: https://forgejo.coulomb.social/coulomb/coulomb-social +sources: + - https://forgejo.coulomb.social/coulomb/coulomb-social +maintainers: + - name: railiance-apps diff --git a/charts/coulomb-social/templates/_helpers.tpl b/charts/coulomb-social/templates/_helpers.tpl new file mode 100644 index 0000000..7016831 --- /dev/null +++ b/charts/coulomb-social/templates/_helpers.tpl @@ -0,0 +1,25 @@ +{{- define "coulomb.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "coulomb.labels" -}} +app.kubernetes.io/name: {{ include "coulomb.fullname" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/part-of: railiance-apps +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }} +{{- end -}} + +{{- define "coulomb.selectorLabels" -}} +app.kubernetes.io/name: {{ include "coulomb.fullname" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "coulomb.image" -}} +{{- if not .Values.image.tag -}} +{{- fail "image.tag is required — pin it in helm/coulomb-social-values.yaml" -}} +{{- end -}} +{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}} +{{- end -}} diff --git a/charts/coulomb-social/templates/deployment.yaml b/charts/coulomb-social/templates/deployment.yaml new file mode 100644 index 0000000..b4657e1 --- /dev/null +++ b/charts/coulomb-social/templates/deployment.yaml @@ -0,0 +1,81 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "coulomb.fullname" . }} + labels: {{- include "coulomb.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: {{- include "coulomb.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + template: + metadata: + labels: {{- include "coulomb.selectorLabels" . | nindent 8 }} + spec: + securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: app + image: {{ include "coulomb.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: {{- toYaml .Values.securityContext | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + envFrom: + - secretRef: + name: {{ .Values.envSecretName | quote }} + env: + {{- range $k, $v := .Values.env }} + - name: {{ $k }} + value: {{ $v | quote }} + {{- end }} + {{- if .Values.probes.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.probes.path }} + port: {{ .Values.probes.port }} + httpHeaders: + - name: Host + value: {{ .Values.probes.hostHeader | quote }} + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + livenessProbe: + httpGet: + path: {{ .Values.probes.path }} + port: {{ .Values.probes.port }} + httpHeaders: + - name: Host + value: {{ .Values.probes.hostHeader | quote }} + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + {{- end }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.persistence.media.enabled }} + volumeMounts: + - name: media + mountPath: /app/media + {{- end }} + {{- if .Values.persistence.media.enabled }} + volumes: + - name: media + persistentVolumeClaim: + claimName: {{ include "coulomb.fullname" . }}-media + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/coulomb-social/templates/service.yaml b/charts/coulomb-social/templates/service.yaml new file mode 100644 index 0000000..fcd783c --- /dev/null +++ b/charts/coulomb-social/templates/service.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "coulomb.fullname" . }} + labels: {{- include "coulomb.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: http + selector: {{- include "coulomb.selectorLabels" . | nindent 4 }} +{{- if .Values.persistence.media.enabled }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "coulomb.fullname" . }}-media + labels: {{- include "coulomb.labels" . | nindent 4 }} +spec: + storageClassName: {{ .Values.persistence.media.storageClass }} + accessModes: [{{ .Values.persistence.media.accessMode }}] + resources: + requests: + storage: {{ .Values.persistence.media.size }} +{{- end }} diff --git a/charts/coulomb-social/values.yaml b/charts/coulomb-social/values.yaml new file mode 100644 index 0000000..9d1e48e --- /dev/null +++ b/charts/coulomb-social/values.yaml @@ -0,0 +1,73 @@ +image: + repository: forgejo.coulomb.social/coulomb/coulomb-social + tag: "" + pullPolicy: IfNotPresent + +replicaCount: 1 + +service: + type: ClusterIP + port: 80 + targetPort: 8000 + +resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# Secrets (SECRET_KEY, DATABASE_URL, OIDC_*, USER_ENGINE_PROXY_SECRET, …) +envSecretName: coulomb-social-env + +env: + DJANGO_SETTINGS_MODULE: coulomb_social.settings.prod + ALLOWED_HOSTS: coulomb.social,localhost + CSRF_TRUSTED_ORIGINS: https://coulomb.social + DEFAULT_TENANT_ID: "tenant:coulomb" + OIDC_ENABLED: "true" + OIDC_ISSUER: https://kc.coulomb.social + OIDC_CLIENT_ID: coulomb-social + OIDC_REDIRECT_URI: https://coulomb.social/auth/callback/ + OIDC_SCOPES: "openid profile email groups" + USER_ENGINE_APPLICATION_ID: coulomb-social + USER_ENGINE_EXPECTED_AUDIENCE: user-engine-portal + # USER_ENGINE_BASE_URL set via secret or here when stable + USER_ENGINE_BASE_URL: https://users.92-205-62-239.nip.io + +probes: + enabled: true + path: /healthz + port: 8000 + hostHeader: coulomb.social + liveness: + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + readiness: + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +persistence: + media: + enabled: false + +podSecurityContext: + runAsNonRoot: true + runAsUser: 10001 + runAsGroup: 10001 + fsGroup: 10001 + +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: ["ALL"] + +nodeSelector: {} +tolerations: [] +affinity: {} diff --git a/docs/coulomb-social.md b/docs/coulomb-social.md new file mode 100644 index 0000000..d8451d0 --- /dev/null +++ b/docs/coulomb-social.md @@ -0,0 +1,35 @@ +# coulomb.social on Railiance + +Source repo: `coulomb/coulomb-social` +Chart: `charts/coulomb-social` +Values: `helm/coulomb-social-values.yaml` +Ingress: `manifests/coulomb-social-ingress.yaml` + +## Prerequisites + +- Image published: `forgejo.coulomb.social/coulomb/coulomb-social:` +- KeyCape client `coulomb-social` registered (see source repo `scripts/register-keycape-client.sh`) +- K8s Secret `coulomb-social-env` in namespace `coulomb-social` with at least: + - `SECRET_KEY` + - `DATABASE_URL` (or platform DB handoff) + - `USER_ENGINE_PROXY_SECRET` (from user-engine-runtime) +- DNS `coulomb.social` → cluster ingress IP + +## Deploy + +```bash +COULOMB_SOCIAL_IMAGE_TAG= make coulomb-social-dry-run +COULOMB_SOCIAL_IMAGE_TAG= make coulomb-social-deploy +make coulomb-social-ingress-deploy +make coulomb-social-status +``` + +## Identity + +- OIDC issuer: `https://kc.coulomb.social` +- user-engine: `https://users.92-205-62-239.nip.io` (trusted proxy) +- flex-auth: leave `FLEX_AUTH_BASE_URL` unset until app PDP Service is ready (local vocabulary in-app) + +## Probe host + +`probes.hostHeader` must stay in `ALLOWED_HOSTS` (see `docs/django-on-railiance.md`). diff --git a/helm/coulomb-social-values.yaml b/helm/coulomb-social-values.yaml new file mode 100644 index 0000000..70a51eb --- /dev/null +++ b/helm/coulomb-social-values.yaml @@ -0,0 +1,24 @@ +# Production overrides for coulomb-social. +# Non-secret values only. Secrets live in K8s Secret coulomb-social-env. + +image: + repository: forgejo.coulomb.social/coulomb/coulomb-social + tag: "pending" # pin to git SHA after first image publish + +env: + DJANGO_SETTINGS_MODULE: coulomb_social.settings.prod + ALLOWED_HOSTS: coulomb.social,localhost + CSRF_TRUSTED_ORIGINS: https://coulomb.social + DEFAULT_TENANT_ID: "tenant:coulomb" + OIDC_ENABLED: "true" + OIDC_ISSUER: https://kc.coulomb.social + OIDC_CLIENT_ID: coulomb-social + OIDC_REDIRECT_URI: https://coulomb.social/auth/callback/ + OIDC_SCOPES: "openid profile email groups" + USER_ENGINE_BASE_URL: https://users.92-205-62-239.nip.io + USER_ENGINE_APPLICATION_ID: coulomb-social + USER_ENGINE_EXPECTED_AUDIENCE: user-engine-portal + +probes: + hostHeader: coulomb.social + path: /healthz diff --git a/manifests/coulomb-social-ingress.yaml b/manifests/coulomb-social-ingress.yaml new file mode 100644 index 0000000..518f4dc --- /dev/null +++ b/manifests/coulomb-social-ingress.yaml @@ -0,0 +1,26 @@ +# Ingress for coulomb.social (apply after first chart deploy). +# Requires cert-manager ClusterIssuer and DNS for coulomb.social → cluster. +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: coulomb-social + namespace: coulomb-social + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + ingressClassName: traefik + tls: + - hosts: [coulomb.social] + secretName: coulomb-social-tls + rules: + - host: coulomb.social + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: coulomb-social + port: + number: 80