From 3dfa75d789a368a0994988cb2ee8f67e63af98aa Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 15 Jun 2026 08:48:16 +0200 Subject: [PATCH] Add RAILIANCE-WP-0007: reuse-surface hub Helm chart on railiance01 Companion to reuse-surface REUSE-WP-0011. Scaffold charts/reuse-surface-hub with PVC, ingress template, values file, and Makefile deploy targets. --- Makefile | 25 ++- charts/reuse-surface-hub/Chart.yaml | 17 +++ .../reuse-surface-hub/templates/_helpers.tpl | 25 +++ .../templates/deployment.yaml | 75 +++++++++ .../reuse-surface-hub/templates/ingress.yaml | 28 ++++ charts/reuse-surface-hub/templates/pvc.yaml | 16 ++ .../reuse-surface-hub/templates/service.yaml | 13 ++ charts/reuse-surface-hub/values.yaml | 59 ++++++++ helm/reuse-surface-hub-values.yaml | 5 + ...P-0007-reuse-surface-hub-on-railiance01.md | 142 ++++++++++++++++++ 10 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 charts/reuse-surface-hub/Chart.yaml create mode 100644 charts/reuse-surface-hub/templates/_helpers.tpl create mode 100644 charts/reuse-surface-hub/templates/deployment.yaml create mode 100644 charts/reuse-surface-hub/templates/ingress.yaml create mode 100644 charts/reuse-surface-hub/templates/pvc.yaml create mode 100644 charts/reuse-surface-hub/templates/service.yaml create mode 100644 charts/reuse-surface-hub/values.yaml create mode 100644 helm/reuse-surface-hub-values.yaml create mode 100644 workplans/RAILIANCE-WP-0007-reuse-surface-hub-on-railiance01.md diff --git a/Makefile b/Makefile index 27fbe32..a6f524a 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,11 @@ INTER_HUB_NAMESPACE ?= inter-hub INTER_HUB_CHART ?= charts/inter-hub INTER_HUB_VALUES ?= helm/inter-hub-values.yaml +REUSE_HUB_RELEASE ?= reuse-surface-hub +REUSE_HUB_NAMESPACE ?= reuse-surface-hub +REUSE_HUB_CHART ?= charts/reuse-surface-hub +REUSE_HUB_VALUES ?= helm/reuse-surface-hub-values.yaml + SOPS_SENTINEL ?= DRY_RUN_CREATE_NAMESPACES ?= false @@ -100,6 +105,24 @@ inter-hub-status: ## Show inter-hub pod / svc / ingress / cert state inter-hub-logs: ## Tail inter-hub app logs kubectl logs -n $(INTER_HUB_NAMESPACE) -l app.kubernetes.io/instance=$(INTER_HUB_RELEASE) -f --tail=50 +##@ Reuse Surface Hub + +reuse-hub-dry-run: ## helm template render (no apply) for reuse-surface-hub + helm template $(REUSE_HUB_RELEASE) $(REUSE_HUB_CHART) \ + --namespace $(REUSE_HUB_NAMESPACE) \ + -f $(REUSE_HUB_VALUES) + +reuse-hub-deploy: ## Deploy / upgrade reuse-surface-hub Helm release + helm upgrade --install $(REUSE_HUB_RELEASE) $(REUSE_HUB_CHART) \ + --namespace $(REUSE_HUB_NAMESPACE) --create-namespace \ + -f $(REUSE_HUB_VALUES) --wait --timeout 5m + +reuse-hub-status: ## Show reuse-surface-hub pod / svc / ingress / cert state + kubectl get pods,svc,ingress,pvc,certificate -n $(REUSE_HUB_NAMESPACE) -l app.kubernetes.io/instance=$(REUSE_HUB_RELEASE) --ignore-not-found + +reuse-hub-logs: ## Tail reuse-surface-hub logs + kubectl logs -n $(REUSE_HUB_NAMESPACE) -l app.kubernetes.io/instance=$(REUSE_HUB_RELEASE) -f --tail=50 + ##@ Help help: ## Show this help @@ -107,4 +130,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 vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret inter-hub-dry-run inter-hub-deploy inter-hub-status inter-hub-logs help +.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret inter-hub-dry-run inter-hub-deploy inter-hub-status inter-hub-logs reuse-hub-dry-run reuse-hub-deploy reuse-hub-status reuse-hub-logs help diff --git a/charts/reuse-surface-hub/Chart.yaml b/charts/reuse-surface-hub/Chart.yaml new file mode 100644 index 0000000..c098e8e --- /dev/null +++ b/charts/reuse-surface-hub/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: reuse-surface-hub +description: | + Federation hub for helix_forge capability registry coordination on Railiance01. +type: application +version: 0.1.0 +appVersion: "0.1.0" +keywords: + - reuse-surface + - federation + - helix-forge + - railiance +home: https://gitea.coulomb.social/coulomb/reuse-surface +sources: + - https://gitea.coulomb.social/coulomb/reuse-surface +maintainers: + - name: railiance-apps \ No newline at end of file diff --git a/charts/reuse-surface-hub/templates/_helpers.tpl b/charts/reuse-surface-hub/templates/_helpers.tpl new file mode 100644 index 0000000..3dcb2e2 --- /dev/null +++ b/charts/reuse-surface-hub/templates/_helpers.tpl @@ -0,0 +1,25 @@ +{{- define "reusehub.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "reusehub.labels" -}} +app.kubernetes.io/name: {{ include "reusehub.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 "reusehub.selectorLabels" -}} +app.kubernetes.io/name: {{ include "reusehub.fullname" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "reusehub.image" -}} +{{- if not .Values.image.tag -}} +{{- fail "image.tag is required - pin it in helm/reuse-surface-hub-values.yaml" -}} +{{- end -}} +{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}} +{{- end -}} \ No newline at end of file diff --git a/charts/reuse-surface-hub/templates/deployment.yaml b/charts/reuse-surface-hub/templates/deployment.yaml new file mode 100644 index 0000000..31466d3 --- /dev/null +++ b/charts/reuse-surface-hub/templates/deployment.yaml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "reusehub.fullname" . }} + labels: {{- include "reusehub.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: {{- include "reusehub.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + template: + metadata: + labels: {{- include "reusehub.selectorLabels" . | nindent 8 }} + spec: + securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: reuse-surface-hub + image: {{ include "reusehub.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: + - name: REUSE_SURFACE_HUB_DB + value: {{ printf "%s/hub.db" .Values.persistence.mountPath | quote }} + - name: REUSE_SURFACE_HUB_CACHE_DIR + value: {{ printf "%s/cache" .Values.persistence.mountPath | quote }} + {{- if .Values.persistence.enabled }} + volumeMounts: + - name: data + mountPath: {{ .Values.persistence.mountPath }} + {{- end }} + {{- if .Values.probes.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.probes.path }} + port: {{ .Values.probes.port }} + 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 }} + 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.enabled }} + volumes: + - name: data + persistentVolumeClaim: + claimName: {{ include "reusehub.fullname" . }}-data + {{- 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 }} \ No newline at end of file diff --git a/charts/reuse-surface-hub/templates/ingress.yaml b/charts/reuse-surface-hub/templates/ingress.yaml new file mode 100644 index 0000000..a307835 --- /dev/null +++ b/charts/reuse-surface-hub/templates/ingress.yaml @@ -0,0 +1,28 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "reusehub.fullname" . }} + labels: {{- include "reusehub.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.ingress.annotations | nindent 4 }} +spec: + ingressClassName: {{ .Values.ingress.className }} + {{- if .Values.ingress.tls }} + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ include "reusehub.fullname" . }}-tls + {{- end }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "reusehub.fullname" . }} + port: + number: {{ .Values.service.port }} +{{- end }} \ No newline at end of file diff --git a/charts/reuse-surface-hub/templates/pvc.yaml b/charts/reuse-surface-hub/templates/pvc.yaml new file mode 100644 index 0000000..8a4f3af --- /dev/null +++ b/charts/reuse-surface-hub/templates/pvc.yaml @@ -0,0 +1,16 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "reusehub.fullname" . }}-data + labels: {{- include "reusehub.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName | quote }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/reuse-surface-hub/templates/service.yaml b/charts/reuse-surface-hub/templates/service.yaml new file mode 100644 index 0000000..5f2388c --- /dev/null +++ b/charts/reuse-surface-hub/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "reusehub.fullname" . }} + labels: {{- include "reusehub.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: {{- include "reusehub.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP \ No newline at end of file diff --git a/charts/reuse-surface-hub/values.yaml b/charts/reuse-surface-hub/values.yaml new file mode 100644 index 0000000..7072c7b --- /dev/null +++ b/charts/reuse-surface-hub/values.yaml @@ -0,0 +1,59 @@ +image: + repository: gitea.coulomb.social/coulomb/reuse-surface-hub + tag: "" + pullPolicy: IfNotPresent + +replicaCount: 1 + +service: + type: ClusterIP + port: 8000 + targetPort: 8000 + +persistence: + enabled: true + size: 1Gi + mountPath: /data + storageClassName: "" + +resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + +envSecretName: reuse-surface-hub-env + +ingress: + enabled: false + className: traefik + host: reuse-hub.whywhynot.de + tls: true + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: letsencrypt-prod + +probes: + enabled: true + path: /health + port: 8000 + liveness: + initialDelaySeconds: 15 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + readiness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +podSecurityContext: {} +securityContext: {} + +nodeSelector: {} +tolerations: [] +affinity: {} \ No newline at end of file diff --git a/helm/reuse-surface-hub-values.yaml b/helm/reuse-surface-hub-values.yaml new file mode 100644 index 0000000..715c62f --- /dev/null +++ b/helm/reuse-surface-hub-values.yaml @@ -0,0 +1,5 @@ +# Production overrides for reuse-surface federation hub. +# REUSE_SURFACE_HUB_TOKEN is supplied via Secret reuse-surface-hub-env. + +image: + tag: "pending-first-build" \ No newline at end of file diff --git a/workplans/RAILIANCE-WP-0007-reuse-surface-hub-on-railiance01.md b/workplans/RAILIANCE-WP-0007-reuse-surface-hub-on-railiance01.md new file mode 100644 index 0000000..54028e7 --- /dev/null +++ b/workplans/RAILIANCE-WP-0007-reuse-surface-hub-on-railiance01.md @@ -0,0 +1,142 @@ +--- +id: RAILIANCE-WP-0007 +type: workplan +title: "Deploy reuse-surface federation hub on railiance01" +domain: railiance +repo: railiance-apps +status: active +owner: codex +topic_slug: railiance +created: "2026-06-15" +updated: "2026-06-15" +--- + +# Deploy reuse-surface federation hub on railiance01 + +Companion to **`reuse-surface` REUSE-WP-0011**. Own the S5 Helm release, +ingress, and operator targets for the federation hub service on production +cluster node `railiance01` (`92.205.130.254`). + +## Goal + +Expose the helix_forge federation hub API at a stable TLS endpoint so repos can +register capability index URLs via `reuse-surface hub` without per-machine +`sources.yaml` maintenance. + +**Default hostname (confirm with operator):** `https://reuse-hub.whywhynot.de` + +## Upstream dependency + +| Upstream | Workplan | Required artifact | +|---|---|---| +| Hub service + image | `reuse-surface` REUSE-WP-0011 | Container image `gitea.coulomb.social/coulomb/reuse-surface-hub:`, `/health` probe path | + +Do not deploy until REUSE-WP-0011-T04 publishes a buildable image and documents +the required environment variables. + +## Placement + +Follow the established `inter-hub` pattern in this repo: + +- `charts/reuse-surface-hub/` — Helm chart (Deployment, Service, Ingress, PVC) +- `helm/reuse-surface-hub-values.yaml` — non-secret overrides (image tag, host) +- SOPS secret handoff for `REUSE_SURFACE_HUB_TOKEN` (write token) +- `Makefile` targets: `reuse-hub-dry-run`, `reuse-hub-deploy`, `reuse-hub-status`, `reuse-hub-logs` + +Cross-repo coordination: + +| Concern | Owner | +|---|---| +| Application image and API | `reuse-surface` | +| Helm release and ingress | `railiance-apps` (this workplan) | +| OCI registry push | `railiance-forge` guidance + `reuse-surface` CI/docs | +| DNS A record | DNS owner of `whywhynot.de` | +| Traefik / cert-manager | `railiance-cluster` / `railiance-platform` (reuse) | + +## Safety contract + +- Do not commit decrypted SOPS values or hub write tokens. +- Pin image tags in `helm/reuse-surface-hub-values.yaml`; no `:latest` in production. +- Use a dedicated namespace (default `reuse-surface-hub`). +- PVC for SQLite data; document backup expectation in runbook. + +--- + +## Scaffold Helm Chart For reuse-surface-hub + +```task +id: RAILIANCE-WP-0007-T01 +status: done +priority: high +``` + +Create `charts/reuse-surface-hub/` modeled on `charts/inter-hub/` with: + +- Deployment exposing port `8000` +- ClusterIP Service +- Optional PVC mount at `/data` for SQLite persistence +- Ingress (Traefik + cert-manager) disabled by default until hostname confirmed +- Probes targeting `GET /health` +- `envSecretName` for hub token and optional config + +## Add Values, SOPS Template, And Makefile Targets + +```task +id: RAILIANCE-WP-0007-T02 +status: done +priority: high +``` + +Add: + +- `helm/reuse-surface-hub-values.yaml` with image repository + `gitea.coulomb.social/coulomb/reuse-surface-hub` and placeholder tag +- Documented SOPS secret template path (mirror `inter-hub-env` pattern) +- Makefile variables and targets: `reuse-hub-dry-run`, `reuse-hub-deploy`, + `reuse-hub-status`, `reuse-hub-logs` + +## Configure Ingress And Hostname + +```task +id: RAILIANCE-WP-0007-T03 +status: wait +priority: medium +``` + +Enable ingress in values with: + +- `ingress.host: reuse-hub.whywhynot.de` (or operator-confirmed host) +- `cert-manager.io/cluster-issuer: letsencrypt-prod` +- Traefik annotations matching `vergabe-teilnahme` / `inter-hub` + +**Blocked on:** DNS A record and hostname approval. + +## Deploy Release To railiance01 + +```task +id: RAILIANCE-WP-0007-T04 +status: wait +priority: medium +``` + +When REUSE-WP-0011-T04 image is available: + +1. `make reuse-hub-dry-run` — inspect rendered manifests +2. Apply SOPS secret for hub token +3. `make reuse-hub-deploy` +4. Confirm certificate issued and `/health` returns 200 + +## Post-Deploy Verification And Runbook + +```task +id: RAILIANCE-WP-0007-T05 +status: todo +priority: low +``` + +Add `docs/reuse-surface-hub-on-railiance01.md` with: + +- Namespace, release name, image promotion steps +- Secret rotation notes +- Smoke checks: `reuse-surface hub status --hub-url https://reuse-hub.whywhynot.de` +- Link back to `reuse-surface/docs/RegistryFederation.md` \ No newline at end of file