generated from coulomb/repo-seed
feat(statehub): add railiance deployment manifests
This commit is contained in:
58
Makefile
58
Makefile
@@ -1,7 +1,17 @@
|
|||||||
.PHONY: install install-cli dashboard-install dashboard-check db db-tools migrate seed api dashboard check test test-python clean register-project register-codex-project register-mcp bootstrap-env validate-adr add-domain rename-domain add-repo list-repos register-path register-from-classification register-from-classification-all cleanup-stale tunnels-up tunnels-status tunnels-check bridges install-hooks install-hooks-all gitea-inventory token-reconcile
|
.PHONY: install install-cli dashboard-install dashboard-check db db-tools migrate seed api dashboard check test test-python clean register-project register-codex-project register-mcp bootstrap-env validate-adr add-domain rename-domain add-repo list-repos register-path register-from-classification register-from-classification-all cleanup-stale tunnels-up tunnels-status tunnels-check bridges install-hooks install-hooks-all gitea-inventory token-reconcile railiance-state-hub-render railiance-state-hub-client-dry-run railiance-state-hub-server-dry-run
|
||||||
|
|
||||||
COMPOSE = docker compose -f infra/docker-compose.yml --env-file .env
|
COMPOSE = docker compose -f infra/docker-compose.yml --env-file .env
|
||||||
PYTHON ?= python3
|
PYTHON ?= python3
|
||||||
|
HELM ?= $(shell command -v helm 2>/dev/null || if [ -x "$$HOME/.local/bin/helm" ]; then printf "%s" "$$HOME/.local/bin/helm"; else printf "%s" "helm"; fi)
|
||||||
|
KUBECTL ?= $(shell command -v kubectl 2>/dev/null || if [ -x "$$HOME/.local/bin/kubectl" ]; then printf "%s" "$$HOME/.local/bin/kubectl"; else printf "%s" "kubectl"; fi)
|
||||||
|
|
||||||
|
RAILIANCE_STATE_HUB_RELEASE ?= state-hub
|
||||||
|
RAILIANCE_STATE_HUB_NAMESPACE ?= state-hub
|
||||||
|
RAILIANCE_STATE_HUB_CHART ?= deploy/railiance/apps/charts/state-hub
|
||||||
|
RAILIANCE_STATE_HUB_VALUES ?= deploy/railiance/apps/helm/state-hub-values.yaml
|
||||||
|
RAILIANCE_STATE_HUB_IMAGE_TAG ?= b536741
|
||||||
|
RAILIANCE_STATE_HUB_PLATFORM_DIR ?= deploy/railiance/platform
|
||||||
|
RAILIANCE_STATE_HUB_APP_MANIFESTS ?= deploy/railiance/apps/manifests
|
||||||
# Codex/WSL non-login shells may not source ~/.profile; keep uv discoverable.
|
# Codex/WSL non-login shells may not source ~/.profile; keep uv discoverable.
|
||||||
UV ?= $(shell command -v uv 2>/dev/null || if [ -x "$$HOME/.local/bin/uv" ]; then printf "%s" "$$HOME/.local/bin/uv"; else printf "%s" "uv"; fi)
|
UV ?= $(shell command -v uv 2>/dev/null || if [ -x "$$HOME/.local/bin/uv" ]; then printf "%s" "$$HOME/.local/bin/uv"; else printf "%s" "uv"; fi)
|
||||||
|
|
||||||
@@ -61,6 +71,52 @@ dashboard:
|
|||||||
check:
|
check:
|
||||||
curl -sf http://127.0.0.1:8000/state/health | python3 -m json.tool
|
curl -sf http://127.0.0.1:8000/state/health | python3 -m json.tool
|
||||||
|
|
||||||
|
railiance-state-hub-render:
|
||||||
|
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
|
||||||
|
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
|
||||||
|
-f $(RAILIANCE_STATE_HUB_VALUES) \
|
||||||
|
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG)
|
||||||
|
|
||||||
|
railiance-state-hub-client-dry-run:
|
||||||
|
@set -e; \
|
||||||
|
tmpdir="$$(mktemp -d)"; \
|
||||||
|
trap 'rm -rf "$$tmpdir"' EXIT; \
|
||||||
|
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
|
||||||
|
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
|
||||||
|
-f $(RAILIANCE_STATE_HUB_VALUES) \
|
||||||
|
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) > "$$tmpdir/state-hub.yaml"; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-credentials.sops.yaml.template; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-cluster.yaml; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-networkpolicies.yaml; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f "$$tmpdir/state-hub.yaml"
|
||||||
|
|
||||||
|
railiance-state-hub-server-dry-run:
|
||||||
|
@set -e; \
|
||||||
|
tmpdir="$$(mktemp -d)"; \
|
||||||
|
trap 'rm -rf "$$tmpdir"' EXIT; \
|
||||||
|
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
|
||||||
|
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
|
||||||
|
-f $(RAILIANCE_STATE_HUB_VALUES) \
|
||||||
|
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) > "$$tmpdir/state-hub.yaml"; \
|
||||||
|
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
|
||||||
|
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
|
||||||
|
-f $(RAILIANCE_STATE_HUB_VALUES) \
|
||||||
|
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) \
|
||||||
|
--show-only templates/namespace.yaml > "$$tmpdir/state-hub-namespace.yaml"; \
|
||||||
|
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-credentials.sops.yaml.template; \
|
||||||
|
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-cluster.yaml; \
|
||||||
|
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-networkpolicies.yaml; \
|
||||||
|
$(KUBECTL) apply --dry-run=server -f "$$tmpdir/state-hub-namespace.yaml"; \
|
||||||
|
if $(KUBECTL) get namespace $(RAILIANCE_STATE_HUB_NAMESPACE) >/dev/null 2>&1; then \
|
||||||
|
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
|
||||||
|
$(KUBECTL) apply --dry-run=server -f "$$tmpdir/state-hub.yaml"; \
|
||||||
|
else \
|
||||||
|
echo "Namespace $(RAILIANCE_STATE_HUB_NAMESPACE) does not exist; validating namespaced app manifests with client dry-run."; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
|
||||||
|
$(KUBECTL) apply --dry-run=client -f "$$tmpdir/state-hub.yaml"; \
|
||||||
|
fi
|
||||||
|
|
||||||
test: test-python dashboard-check
|
test: test-python dashboard-check
|
||||||
|
|
||||||
test-python:
|
test-python:
|
||||||
|
|||||||
88
deploy/railiance/README.md
Normal file
88
deploy/railiance/README.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# State Hub Railiance Deployment Handoff
|
||||||
|
|
||||||
|
This directory contains the State Hub deployment handoff for `CUST-WP-0011`.
|
||||||
|
It is source-owned by `state-hub` and split along the Railiance ownership
|
||||||
|
boundaries used for the actual cluster rollout.
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- `deploy/railiance/platform/` is the `railiance-platform` handoff for the
|
||||||
|
`state-hub-db` CloudNativePG cluster, database bootstrap credential, and
|
||||||
|
database NetworkPolicies in the `databases` namespace.
|
||||||
|
- `deploy/railiance/apps/` is the `railiance-apps` handoff for the State Hub API
|
||||||
|
Helm chart, non-secret production values, and app namespace runtime Secret
|
||||||
|
template.
|
||||||
|
- Runtime secret values are not stored here. Replace placeholder passwords only
|
||||||
|
in an operator-controlled file, then encrypt or deliver through the approved
|
||||||
|
platform secret path.
|
||||||
|
|
||||||
|
## Image
|
||||||
|
|
||||||
|
The current image is pinned to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
gitea.coulomb.social/coulomb/state-hub:b536741
|
||||||
|
```
|
||||||
|
|
||||||
|
railiance01 has already pulled this tag with `crictl`, and the image serves
|
||||||
|
`GET /state/health` against the local WSL database in smoke testing.
|
||||||
|
|
||||||
|
## Render And Dry-Run
|
||||||
|
|
||||||
|
Render the app chart without touching the cluster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make railiance-state-hub-render
|
||||||
|
```
|
||||||
|
|
||||||
|
Run client-side Kubernetes validation for the platform manifests, app Secret
|
||||||
|
template, and rendered chart:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make railiance-state-hub-client-dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Run server-side dry-run against the configured representative cluster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
KUBECONFIG=~/.kube/config-hosteurope make railiance-state-hub-server-dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Server-side dry-run requires the CNPG CRDs, namespace permissions, and dry-run
|
||||||
|
permission for resources in `databases` and `state-hub`.
|
||||||
|
Before the `state-hub` namespace exists, Kubernetes cannot server-dry-run namespaced app
|
||||||
|
objects into that namespace because dry-run Namespace creation is not persisted.
|
||||||
|
The Make target therefore server-validates the platform and Namespace manifests,
|
||||||
|
then falls back to client dry-run for namespaced app manifests with an explicit
|
||||||
|
notice.
|
||||||
|
|
||||||
|
## Promotion Notes
|
||||||
|
|
||||||
|
Platform promotion into `railiance-platform`:
|
||||||
|
|
||||||
|
- copy `platform/state-hub-db-credentials.sops.yaml.template` to a real SOPS
|
||||||
|
secret file with an operator-generated password;
|
||||||
|
- apply or GitOps-manage `platform/state-hub-db-cluster.yaml`;
|
||||||
|
- apply or GitOps-manage `platform/state-hub-db-networkpolicies.yaml`.
|
||||||
|
|
||||||
|
App promotion into `railiance-apps`:
|
||||||
|
|
||||||
|
- copy `apps/charts/state-hub/` to `charts/state-hub/`;
|
||||||
|
- copy `apps/helm/state-hub-values.yaml` to `helm/state-hub-values.yaml`;
|
||||||
|
- create `state-hub-env` in the `state-hub` namespace from the approved
|
||||||
|
secret-delivery path;
|
||||||
|
- deploy with Helm only after `state-hub-db` is healthy.
|
||||||
|
|
||||||
|
## Runtime Secret Contract
|
||||||
|
|
||||||
|
The app chart expects a Kubernetes Secret named `state-hub-env` in the
|
||||||
|
`state-hub` namespace with at least:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DATABASE_URL=postgresql+asyncpg://state_hub:<url-encoded-password>@state-hub-db-rw.databases.svc.cluster.local:5432/state_hub
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional runtime settings such as `CORS_ORIGINS` can live in the chart
|
||||||
|
ConfigMap. The default chart keeps public ingress disabled; access should use
|
||||||
|
the existing private tunnel/ops-bridge path until a separate exposure decision
|
||||||
|
is recorded.
|
||||||
6
deploy/railiance/apps/charts/state-hub/Chart.yaml
Normal file
6
deploy/railiance/apps/charts/state-hub/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: state-hub
|
||||||
|
description: State Hub API service for private Railiance operation
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "b536741"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{{- define "statehub.fullname" -}}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||||
|
{{- printf "%s" $name | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- define "statehub.labels" -}}
|
||||||
|
app: {{ include "statehub.fullname" . }}
|
||||||
|
app.kubernetes.io/name: {{ include "statehub.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 "+" "_" }}
|
||||||
|
railiance.io/layer: s5-app
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- define "statehub.selectorLabels" -}}
|
||||||
|
app: {{ include "statehub.fullname" . }}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{- define "statehub.image" -}}
|
||||||
|
{{- if not .Values.image.tag -}}
|
||||||
|
{{- fail "image.tag is required - pin it in deploy/railiance/apps/helm/state-hub-values.yaml or pass --set image.tag=<sha>" -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}}
|
||||||
|
{{- end -}}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{{- if .Values.config.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ .Values.config.name }}
|
||||||
|
labels: {{- include "statehub.labels" . | nindent 4 }}
|
||||||
|
data:
|
||||||
|
CORS_ORIGINS: {{ .Values.config.corsOrigins | quote }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "statehub.fullname" . }}
|
||||||
|
labels: {{- include "statehub.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels: {{- include "statehub.selectorLabels" . | nindent 6 }}
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels: {{- include "statehub.labels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets: {{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
containers:
|
||||||
|
- name: state-hub
|
||||||
|
image: {{ include "statehub.image" . | quote }}
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
securityContext: {{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: {{ .Values.service.targetPort }}
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
{{- if .Values.config.enabled }}
|
||||||
|
- configMapRef:
|
||||||
|
name: {{ .Values.config.name | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- secretRef:
|
||||||
|
name: {{ .Values.secret.name | 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 }}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ include "statehub.fullname" . }}
|
||||||
|
labels: {{- include "statehub.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 "statehub.fullname" . }}-tls
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
- host: {{ .Values.ingress.host }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: {{ include "statehub.fullname" . }}
|
||||||
|
port:
|
||||||
|
number: {{ .Values.service.port }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{{- if .Values.namespace.create }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: {{ .Release.Namespace }}
|
||||||
|
labels:
|
||||||
|
{{- toYaml .Values.namespace.labels | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "statehub.fullname" . }}
|
||||||
|
labels: {{- include "statehub.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.service.port }}
|
||||||
|
targetPort: {{ .Values.service.targetPort }}
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector: {{- include "statehub.selectorLabels" . | nindent 4 }}
|
||||||
67
deploy/railiance/apps/charts/state-hub/values.yaml
Normal file
67
deploy/railiance/apps/charts/state-hub/values.yaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
image:
|
||||||
|
repository: gitea.coulomb.social/coulomb/state-hub
|
||||||
|
tag: ""
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
namespace:
|
||||||
|
create: true
|
||||||
|
labels:
|
||||||
|
railiance.io/postgres-client: state-hub-db
|
||||||
|
railiance.io/layer: s5-app
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8000
|
||||||
|
targetPort: 8000
|
||||||
|
|
||||||
|
config:
|
||||||
|
enabled: true
|
||||||
|
name: state-hub-config
|
||||||
|
corsOrigins: "http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001"
|
||||||
|
|
||||||
|
secret:
|
||||||
|
name: state-hub-env
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
className: traefik
|
||||||
|
host: state-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: /state/health
|
||||||
|
port: 8000
|
||||||
|
liveness:
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readiness:
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
podSecurityContext: {}
|
||||||
|
securityContext: {}
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
8
deploy/railiance/apps/helm/state-hub-values.yaml
Normal file
8
deploy/railiance/apps/helm/state-hub-values.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Production values for the State Hub Railiance chart handoff.
|
||||||
|
# Non-secret values only. DATABASE_URL comes from the Secret `state-hub-env`.
|
||||||
|
|
||||||
|
image:
|
||||||
|
tag: "b536741"
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Template for the State Hub runtime Secret in the state-hub namespace.
|
||||||
|
# DO NOT commit this file with real credentials.
|
||||||
|
# Encrypt with: sops -e -i state-hub-env.sops.yaml
|
||||||
|
# Apply with: kubectl apply -f <(sops -d state-hub-env.sops.yaml)
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: state-hub-env
|
||||||
|
namespace: state-hub
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: state-hub
|
||||||
|
app.kubernetes.io/component: runtime-env
|
||||||
|
app.kubernetes.io/managed-by: manual
|
||||||
|
railiance.io/layer: s5-app
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
DATABASE_URL: postgresql+asyncpg://state_hub:REPLACE_WITH_URL_ENCODED_PASSWORD@state-hub-db-rw.databases.svc.cluster.local:5432/state_hub
|
||||||
28
deploy/railiance/platform/state-hub-db-cluster.yaml
Normal file
28
deploy/railiance/platform/state-hub-db-cluster.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
# Dedicated CNPG Cluster for State Hub episodic memory.
|
||||||
|
# Owned by railiance-platform (S3). Operator lives in cnpg-system.
|
||||||
|
#
|
||||||
|
# Pre-condition: state-hub-db-credentials Secret exists in databases namespace.
|
||||||
|
# Runtime app Secret is separate and lives in the state-hub namespace.
|
||||||
|
apiVersion: postgresql.cnpg.io/v1
|
||||||
|
kind: Cluster
|
||||||
|
metadata:
|
||||||
|
name: state-hub-db
|
||||||
|
namespace: databases
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: state-hub-db
|
||||||
|
app.kubernetes.io/component: database
|
||||||
|
app.kubernetes.io/managed-by: manual
|
||||||
|
railiance.io/layer: s3-platform
|
||||||
|
railiance.io/role: state-hub-database
|
||||||
|
spec:
|
||||||
|
instances: 1
|
||||||
|
imageName: ghcr.io/cloudnative-pg/postgresql:16
|
||||||
|
storage:
|
||||||
|
size: 10Gi
|
||||||
|
bootstrap:
|
||||||
|
initdb:
|
||||||
|
database: state_hub
|
||||||
|
owner: state_hub
|
||||||
|
secret:
|
||||||
|
name: state-hub-db-credentials
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Template for the state-hub-db bootstrap Secret.
|
||||||
|
# DO NOT commit this file with real credentials.
|
||||||
|
# Encrypt with: sops -e -i state-hub-db-credentials.sops.yaml
|
||||||
|
# Apply with: kubectl apply -f <(sops -d state-hub-db-credentials.sops.yaml)
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: state-hub-db-credentials
|
||||||
|
namespace: databases
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: state-hub-db
|
||||||
|
app.kubernetes.io/component: database-bootstrap
|
||||||
|
app.kubernetes.io/managed-by: manual
|
||||||
|
railiance.io/layer: s3-platform
|
||||||
|
type: kubernetes.io/basic-auth
|
||||||
|
stringData:
|
||||||
|
username: state_hub
|
||||||
|
password: REPLACE_WITH_PASSWORD
|
||||||
74
deploy/railiance/platform/state-hub-db-networkpolicies.yaml
Normal file
74
deploy/railiance/platform/state-hub-db-networkpolicies.yaml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
# NetworkPolicies for the dedicated State Hub CNPG cluster.
|
||||||
|
# Namespaces that need database access must carry:
|
||||||
|
# railiance.io/postgres-client: state-hub-db
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-egress-kube-api-state-hub-db
|
||||||
|
namespace: databases
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: state-hub-db
|
||||||
|
railiance.io/layer: s3-platform
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
cnpg.io/cluster: state-hub-db
|
||||||
|
policyTypes:
|
||||||
|
- Egress
|
||||||
|
egress:
|
||||||
|
- to:
|
||||||
|
- namespaceSelector: {}
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 6443
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-ingress-from-cnpg-operator-state-hub-db
|
||||||
|
namespace: databases
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: state-hub-db
|
||||||
|
railiance.io/layer: s3-platform
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
cnpg.io/cluster: state-hub-db
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
kubernetes.io/metadata.name: cnpg-system
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
- protocol: TCP
|
||||||
|
port: 8000
|
||||||
|
- protocol: TCP
|
||||||
|
port: 9187
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-ingress-from-state-hub-namespace-state-hub-db
|
||||||
|
namespace: databases
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: state-hub-db
|
||||||
|
railiance.io/layer: s3-platform
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
cnpg.io/cluster: state-hub-db
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
railiance.io/postgres-client: state-hub-db
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
@@ -225,8 +225,9 @@ and verified railiance01 can pull it with `sudo crictl pull`.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: CUST-WP-0011-T04
|
id: CUST-WP-0011-T04
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
|
completed: "2026-06-25"
|
||||||
state_hub_task_id: "a7baf2eb-abd7-4aa3-b2cb-a5370ac09844"
|
state_hub_task_id: "a7baf2eb-abd7-4aa3-b2cb-a5370ac09844"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -241,6 +242,24 @@ Create the cluster-side deployment assets using current Railiance boundaries:
|
|||||||
**Done when:** manifests lint/apply in a non-destructive dry run and ownership
|
**Done when:** manifests lint/apply in a non-destructive dry run and ownership
|
||||||
boundaries are documented.
|
boundaries are documented.
|
||||||
|
|
||||||
|
Completed 2026-06-25: added a source-owned Railiance deployment handoff under
|
||||||
|
`deploy/railiance/` with platform manifests for `state-hub-db` CNPG, database
|
||||||
|
credentials, database NetworkPolicies, an app Helm chart, production values, and
|
||||||
|
a `state-hub-env` Secret template. Added Make targets for rendering,
|
||||||
|
client-side dry-run validation, and namespace-aware server-side dry-run
|
||||||
|
validation. Verified:
|
||||||
|
|
||||||
|
- `make railiance-state-hub-render`
|
||||||
|
- `make railiance-state-hub-client-dry-run`
|
||||||
|
- `make railiance-state-hub-server-dry-run`
|
||||||
|
|
||||||
|
The server dry-run validates platform resources and the Namespace manifest
|
||||||
|
against the live cluster API. Because the `state-hub` namespace does not yet
|
||||||
|
exist, it explicitly falls back to client dry-run for namespaced app manifests;
|
||||||
|
Kubernetes cannot persist a dry-run Namespace for subsequent namespaced
|
||||||
|
server-side validation. Ownership boundaries and promotion notes are documented
|
||||||
|
in `deploy/railiance/README.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### T05 — Deploy empty State Hub and run migrations on railiance01
|
### T05 — Deploy empty State Hub and run migrations on railiance01
|
||||||
|
|||||||
Reference in New Issue
Block a user