diff --git a/Makefile b/Makefile index 56ed59c..27fbe32 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,11 @@ VERGABE_DB_HOST ?= apps-pg-rw.databases VERGABE_DB_PORT ?= 5432 VERGABE_DB_NAME ?= vergabe_db +INTER_HUB_RELEASE ?= inter-hub +INTER_HUB_NAMESPACE ?= inter-hub +INTER_HUB_CHART ?= charts/inter-hub +INTER_HUB_VALUES ?= helm/inter-hub-values.yaml + SOPS_SENTINEL ?= DRY_RUN_CREATE_NAMESPACES ?= false @@ -77,6 +82,24 @@ 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 +##@ Inter-Hub + +inter-hub-dry-run: ## helm template render (no apply) for inter-hub + helm template $(INTER_HUB_RELEASE) $(INTER_HUB_CHART) \ + --namespace $(INTER_HUB_NAMESPACE) \ + -f $(INTER_HUB_VALUES) + +inter-hub-deploy: ## Deploy / upgrade inter-hub Helm release + helm upgrade --install $(INTER_HUB_RELEASE) $(INTER_HUB_CHART) \ + --namespace $(INTER_HUB_NAMESPACE) --create-namespace \ + -f $(INTER_HUB_VALUES) --wait --timeout 5m + +inter-hub-status: ## Show inter-hub pod / svc / ingress / cert state + kubectl get pods,svc,ingress,certificate -n $(INTER_HUB_NAMESPACE) -l app.kubernetes.io/instance=$(INTER_HUB_RELEASE) --ignore-not-found + +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 + ##@ Help help: ## Show this help @@ -84,4 +107,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 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 help diff --git a/charts/inter-hub/Chart.yaml b/charts/inter-hub/Chart.yaml new file mode 100644 index 0000000..4823d98 --- /dev/null +++ b/charts/inter-hub/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: inter-hub +description: | + Interaction Hub Framework reference deployment for Railiance01. +type: application +version: 0.1.0 +appVersion: "0.2.0-alpha.1" +keywords: + - ihf + - inter-hub + - railiance +home: https://gitea.coulomb.social/coulomb/inter-hub +sources: + - https://gitea.coulomb.social/coulomb/inter-hub +maintainers: + - name: railiance-apps diff --git a/charts/inter-hub/templates/_helpers.tpl b/charts/inter-hub/templates/_helpers.tpl new file mode 100644 index 0000000..6bcae89 --- /dev/null +++ b/charts/inter-hub/templates/_helpers.tpl @@ -0,0 +1,28 @@ +{{/* +Chart name + release name produce a unique resource name. +*/}} +{{- define "interhub.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "interhub.labels" -}} +app.kubernetes.io/name: {{ include "interhub.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 "interhub.selectorLabels" -}} +app.kubernetes.io/name: {{ include "interhub.fullname" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "interhub.image" -}} +{{- if not .Values.image.tag -}} +{{- fail "image.tag is required - pin it in helm/inter-hub-values.yaml or pass --set image.tag=" -}} +{{- end -}} +{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}} +{{- end -}} diff --git a/charts/inter-hub/templates/deployment.yaml b/charts/inter-hub/templates/deployment.yaml new file mode 100644 index 0000000..fad6062 --- /dev/null +++ b/charts/inter-hub/templates/deployment.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "interhub.fullname" . }} + labels: {{- include "interhub.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: {{- include "interhub.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + template: + metadata: + labels: {{- include "interhub.selectorLabels" . | nindent 8 }} + spec: + securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: inter-hub + image: {{ include "interhub.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 }} + {{- 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 }} + {{- 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/inter-hub/templates/ingress.yaml b/charts/inter-hub/templates/ingress.yaml new file mode 100644 index 0000000..959b42e --- /dev/null +++ b/charts/inter-hub/templates/ingress.yaml @@ -0,0 +1,28 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "interhub.fullname" . }} + labels: {{- include "interhub.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 "interhub.fullname" . }}-tls + {{- end }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "interhub.fullname" . }} + port: + number: {{ .Values.service.port }} +{{- end }} diff --git a/charts/inter-hub/templates/service.yaml b/charts/inter-hub/templates/service.yaml new file mode 100644 index 0000000..dd8eac4 --- /dev/null +++ b/charts/inter-hub/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "interhub.fullname" . }} + labels: {{- include "interhub.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: http + selector: {{- include "interhub.selectorLabels" . | nindent 4 }} diff --git a/charts/inter-hub/values.yaml b/charts/inter-hub/values.yaml new file mode 100644 index 0000000..e33e355 --- /dev/null +++ b/charts/inter-hub/values.yaml @@ -0,0 +1,55 @@ +image: + repository: gitea.coulomb.social/coulomb/inter-hub + tag: "" # required; pinned via helm/inter-hub-values.yaml or --set image.tag= + pullPolicy: IfNotPresent + +replicaCount: 1 + +service: + type: ClusterIP + port: 8000 + targetPort: 8000 + +resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 1000m + memory: 2Gi + +# Env from the K8s Secret created from inter-hub's SOPS handoff file: +# deploy/railiance/secrets/inter-hub.env.sops.yaml +envSecretName: inter-hub-env + +ingress: + enabled: true + className: traefik + host: hub.coulomb.social + 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: / + port: 8000 + liveness: + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + readiness: + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +podSecurityContext: {} +securityContext: {} + +nodeSelector: {} +tolerations: [] +affinity: {} diff --git a/helm/inter-hub-values.yaml b/helm/inter-hub-values.yaml new file mode 100644 index 0000000..7034e02 --- /dev/null +++ b/helm/inter-hub-values.yaml @@ -0,0 +1,6 @@ +# Production overrides for the inter-hub Helm chart. +# Non-secret values only; DATABASE_URL and IHP_SESSION_SECRET come from the +# Secret 'inter-hub-env' in the inter-hub namespace. + +image: + tag: "fde5525" # Current verified deployment (gitea.coulomb.social/coulomb/inter-hub) diff --git a/tools/k8s-server-dry-run.sh b/tools/k8s-server-dry-run.sh index fef016c..bbe54ed 100755 --- a/tools/k8s-server-dry-run.sh +++ b/tools/k8s-server-dry-run.sh @@ -8,6 +8,10 @@ VERGABE_RELEASE="${VERGABE_RELEASE:-vergabe-teilnahme}" VERGABE_NAMESPACE="${VERGABE_NAMESPACE:-vergabe-teilnahme}" VERGABE_CHART="${VERGABE_CHART:-charts/vergabe-teilnahme}" VERGABE_VALUES="${VERGABE_VALUES:-helm/vergabe-teilnahme-values.yaml}" +INTER_HUB_RELEASE="${INTER_HUB_RELEASE:-inter-hub}" +INTER_HUB_NAMESPACE="${INTER_HUB_NAMESPACE:-inter-hub}" +INTER_HUB_CHART="${INTER_HUB_CHART:-charts/inter-hub}" +INTER_HUB_VALUES="${INTER_HUB_VALUES:-helm/inter-hub-values.yaml}" DRY_RUN_CREATE_NAMESPACES="${DRY_RUN_CREATE_NAMESPACES:-false}" for cmd in kubectl helm; do @@ -28,6 +32,8 @@ fi if [[ "$DRY_RUN_CREATE_NAMESPACES" == "true" ]]; then echo "server dry-run: ensuring namespace $VERGABE_NAMESPACE exists" kubectl create namespace "$VERGABE_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - + echo "server dry-run: ensuring namespace $INTER_HUB_NAMESPACE exists" + kubectl create namespace "$INTER_HUB_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - fi tmpdir="$(mktemp -d)" @@ -38,8 +44,16 @@ helm template "$VERGABE_RELEASE" "$VERGABE_CHART" \ -f "$VERGABE_VALUES" \ > "$tmpdir/vergabe-teilnahme.yaml" +helm template "$INTER_HUB_RELEASE" "$INTER_HUB_CHART" \ + --namespace "$INTER_HUB_NAMESPACE" \ + -f "$INTER_HUB_VALUES" \ + > "$tmpdir/inter-hub.yaml" + echo "server dry-run: committed manifests" kubectl apply --dry-run=server -f manifests echo "server dry-run: rendered $VERGABE_RELEASE chart" kubectl apply --dry-run=server -n "$VERGABE_NAMESPACE" -f "$tmpdir/vergabe-teilnahme.yaml" + +echo "server dry-run: rendered $INTER_HUB_RELEASE chart" +kubectl apply --dry-run=server -n "$INTER_HUB_NAMESPACE" -f "$tmpdir/inter-hub.yaml" diff --git a/workplans/ADHOC-2026-06-14.md b/workplans/ADHOC-2026-06-14.md new file mode 100644 index 0000000..334a619 --- /dev/null +++ b/workplans/ADHOC-2026-06-14.md @@ -0,0 +1,27 @@ +--- +id: ADHOC-2026-06-14 +type: workplan +title: "Ad hoc Railiance app handoff for inter-hub" +domain: railiance +repo: railiance-apps +status: finished +owner: codex +topic_slug: railiance +created: "2026-06-14" +updated: "2026-06-14" +--- + +# ADHOC-2026-06-14 - Ad hoc Railiance app handoff for inter-hub + +## Add inter-hub chart handoff + +```task +id: ADHOC-2026-06-14-T01 +status: done +priority: medium +``` + +Added `charts/inter-hub` and `helm/inter-hub-values.yaml` so IHUB-WP-0018 has +a concrete Railiance app handoff chart matching the existing railiance-apps +layout. The source Inter-Hub repo remains the owner of the runtime app code, +workflow, and SOPS secret handoff.