From a2fae9dd0cf6acc4da6b0e5879604016f3a02c83 Mon Sep 17 00:00:00 2001 From: Sten Date: Thu, 28 May 2026 16:47:38 +0200 Subject: [PATCH 01/10] feat(apps): forte-drop web + mcp ArgoCD applications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two ArgoCD apps from the same forte-drop image: - forte-drop (web): admin + public drops, sidecar in oidc mode, ingress drop-k8s.hackathon.forteapps.net. - forte-drop-mcp (mcp): MCP-over-HTTP, sidecar in mcp mode, ingress mcp.drop-k8s.hackathon.forteapps.net. Plus two labeled Keycloak client config Secrets — the registrar creates the OIDC clients in the forte realm within ~2 min. Sealed secrets (forte-drop-secrets + auth-oidc) added in a follow-up commit by the maintainer: cd /Users/sten/dev/work/forte_k8/launchpad kubeseal --format=yaml \ --controller-name=sealed-secrets-controller \ --controller-namespace=kube-system \ < private/forte-drop-secrets.yaml \ > apps/base/forte-drop/forte-drop-secrets-sealed.yaml # auth-oidc: wait for registrar, copy client-secret into private/, # then seal as apps/base/forte-drop/auth-oidc-sealed.yaml. # (mcp deployment is sidecar type=mcp — no auth-oidc Secret needed; # only the web deployment requires it.) --- apps/base/forte-drop-mcp/forte-drop-mcp.yaml | 37 +++++++++++++++++++ .../keycloak-client-forte-drop-mcp.yaml | 27 ++++++++++++++ apps/base/forte-drop-mcp/kustomization.yaml | 6 +++ apps/base/forte-drop/forte-drop.yaml | 37 +++++++++++++++++++ .../keycloak-client-forte-drop.yaml | 28 ++++++++++++++ apps/base/forte-drop/kustomization.yaml | 7 ++++ apps/base/kustomization.yaml | 2 + 7 files changed, 144 insertions(+) create mode 100644 apps/base/forte-drop-mcp/forte-drop-mcp.yaml create mode 100644 apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml create mode 100644 apps/base/forte-drop-mcp/kustomization.yaml create mode 100644 apps/base/forte-drop/forte-drop.yaml create mode 100644 apps/base/forte-drop/keycloak-client-forte-drop.yaml create mode 100644 apps/base/forte-drop/kustomization.yaml diff --git a/apps/base/forte-drop-mcp/forte-drop-mcp.yaml b/apps/base/forte-drop-mcp/forte-drop-mcp.yaml new file mode 100644 index 0000000..0465bdb --- /dev/null +++ b/apps/base/forte-drop-mcp/forte-drop-mcp.yaml @@ -0,0 +1,37 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: forte-drop-mcp + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "1" + notifications.argoproj.io/subscribe.on-sync-succeeded.slack: "" + notifications.argoproj.io/subscribe.on-sync-failed.slack: "" + notifications.argoproj.io/subscribe.on-degraded.slack: "" + labels: + app.kubernetes.io/name: forte-drop-mcp + app.kubernetes.io/part-of: apps + app.kubernetes.io/managed-by: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + sources: + - repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git + path: forteapp + targetRevision: HEAD + helm: + valueFiles: + - $values/forte-drop-mcp/values.yaml + - repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git + targetRevision: HEAD + ref: values + destination: + server: https://kubernetes.default.svc + namespace: forte-drop + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml b/apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml new file mode 100644 index 0000000..228062a --- /dev/null +++ b/apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml @@ -0,0 +1,27 @@ +# MCP audience client. RFC 7591 dynamic-registration capable MCP clients (e.g., +# Claude Desktop) discover this via /.well-known/oauth-protected-resource and +# request tokens with aud=https://mcp.drop-k8s.hackathon.forteapps.net/mcp. +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-client-forte-drop-mcp + namespace: forte-drop + labels: + keycloak.forteapps.net/client-config: "true" +stringData: + client.json: | + { + "clientId": "forte-drop-mcp", + "name": "Forte Drop (MCP)", + "enabled": true, + "protocol": "openid-connect", + "clientAuthenticatorType": "client-secret", + "standardFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "defaultClientScopes": ["openid","profile","email"], + "attributes": { + "access.token.lifespan": "3600" + } + } diff --git a/apps/base/forte-drop-mcp/kustomization.yaml b/apps/base/forte-drop-mcp/kustomization.yaml new file mode 100644 index 0000000..9d5338b --- /dev/null +++ b/apps/base/forte-drop-mcp/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- forte-drop-mcp.yaml +- keycloak-client-forte-drop-mcp.yaml +# - auth-oidc-sealed.yaml # added in follow-up commit diff --git a/apps/base/forte-drop/forte-drop.yaml b/apps/base/forte-drop/forte-drop.yaml new file mode 100644 index 0000000..08610ba --- /dev/null +++ b/apps/base/forte-drop/forte-drop.yaml @@ -0,0 +1,37 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: forte-drop + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "1" + notifications.argoproj.io/subscribe.on-sync-succeeded.slack: "" + notifications.argoproj.io/subscribe.on-sync-failed.slack: "" + notifications.argoproj.io/subscribe.on-degraded.slack: "" + labels: + app.kubernetes.io/name: forte-drop + app.kubernetes.io/part-of: apps + app.kubernetes.io/managed-by: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + sources: + - repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git + path: forteapp + targetRevision: HEAD + helm: + valueFiles: + - $values/forte-drop/values.yaml + - repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git + targetRevision: HEAD + ref: values + destination: + server: https://kubernetes.default.svc + namespace: forte-drop + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/apps/base/forte-drop/keycloak-client-forte-drop.yaml b/apps/base/forte-drop/keycloak-client-forte-drop.yaml new file mode 100644 index 0000000..b7d6e85 --- /dev/null +++ b/apps/base/forte-drop/keycloak-client-forte-drop.yaml @@ -0,0 +1,28 @@ +# Labeled config Secret read by the Keycloak Client Registrar. The registrar will +# create the OIDC client in the forte realm and write the resulting credentials +# back into forte-drop-oidc-credentials Secret in this namespace within ~2 min. +# That client-secret then gets manually copied into the auth-oidc SealedSecret +# (one-time per cluster; see PR description). +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-client-forte-drop + namespace: forte-drop + labels: + keycloak.forteapps.net/client-config: "true" +stringData: + client.json: | + { + "clientId": "forte-drop", + "name": "Forte Drop (web)", + "enabled": true, + "protocol": "openid-connect", + "clientAuthenticatorType": "client-secret", + "standardFlowEnabled": true, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "redirectUris": ["https://drop-k8s.hackathon.forteapps.net/auth/callback"], + "webOrigins": ["https://drop-k8s.hackathon.forteapps.net"], + "defaultClientScopes": ["openid","email","profile"] + } diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/base/forte-drop/kustomization.yaml new file mode 100644 index 0000000..feda2e9 --- /dev/null +++ b/apps/base/forte-drop/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- forte-drop.yaml +- keycloak-client-forte-drop.yaml +# - forte-drop-secrets-sealed.yaml # added in follow-up commit +# - auth-oidc-sealed.yaml # added in follow-up commit (after Keycloak registrar creates client_secret) diff --git a/apps/base/kustomization.yaml b/apps/base/kustomization.yaml index a501fbf..b494209 100644 --- a/apps/base/kustomization.yaml +++ b/apps/base/kustomization.yaml @@ -6,3 +6,5 @@ resources: - musicman - ts-mcp - argo-mcp +- forte-drop +- forte-drop-mcp From dbe67a4d5607e07b3d68d595f01b771bcbfcd239 Mon Sep 17 00:00:00 2001 From: Sten Date: Thu, 28 May 2026 16:51:04 +0200 Subject: [PATCH 02/10] docs(apps): clarify mcp deployment needs no auth-oidc secret --- apps/base/forte-drop-mcp/kustomization.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/base/forte-drop-mcp/kustomization.yaml b/apps/base/forte-drop-mcp/kustomization.yaml index 9d5338b..e91a747 100644 --- a/apps/base/forte-drop-mcp/kustomization.yaml +++ b/apps/base/forte-drop-mcp/kustomization.yaml @@ -3,4 +3,7 @@ kind: Kustomization resources: - forte-drop-mcp.yaml - keycloak-client-forte-drop-mcp.yaml -# - auth-oidc-sealed.yaml # added in follow-up commit +# Note: no auth-oidc Secret needed for type: mcp. The MCP sidecar only validates +# tokens against the OIDC issuer (RFC 9728 resource server) and never authenticates +# itself, so it doesn't read a client-secret. forte-drop-secrets (shared with the +# web deployment) covers PG + S3 creds. From 5f6fb9b15281591f9d19815d8c2ffb9425bd544a Mon Sep 17 00:00:00 2001 From: Sten Date: Thu, 28 May 2026 18:28:51 +0200 Subject: [PATCH 03/10] fix(apps): scope forte-drop to upc-dev only, not via base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit forte-drop and forte-drop-mcp have hackathon-domain values hardcoded (drop-k8s.hackathon.forteapps.net). Listing them in apps/base/ syncs them to both upc-dev and upc-prod overlays — prod sync would create broken Applications pointing at non-existent prod ingress. Move references to apps/overlays/upc-dev/ only. Co-Authored-By: Claude Opus 4.7 --- apps/base/kustomization.yaml | 2 -- apps/overlays/upc-dev/kustomization.yaml | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/base/kustomization.yaml b/apps/base/kustomization.yaml index b494209..a501fbf 100644 --- a/apps/base/kustomization.yaml +++ b/apps/base/kustomization.yaml @@ -6,5 +6,3 @@ resources: - musicman - ts-mcp - argo-mcp -- forte-drop -- forte-drop-mcp diff --git a/apps/overlays/upc-dev/kustomization.yaml b/apps/overlays/upc-dev/kustomization.yaml index 98e6a19..22bf6ad 100644 --- a/apps/overlays/upc-dev/kustomization.yaml +++ b/apps/overlays/upc-dev/kustomization.yaml @@ -2,7 +2,11 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../../base +- ../../base/forte-drop +- ../../base/forte-drop-mcp - dbunk-demo # No patches needed — base already has "upc-dev" paths # upc-dev is the default/base cluster +# forte-drop and forte-drop-mcp are scoped to upc-dev only — they have +# hackathon-domain hardcoded values and must not sync to upc-prod. From 6bc5bd29b3efb0033770cbbc5b94e30d163d71bd Mon Sep 17 00:00:00 2001 From: Sten Date: Fri, 29 May 2026 09:31:16 +0200 Subject: [PATCH 04/10] feat(apps): PodDisruptionBudget for forte-drop web (minAvailable 1) --- apps/base/forte-drop/forte-drop-pdb.yaml | 24 ++++++++++++++++++++++++ apps/base/forte-drop/kustomization.yaml | 1 + 2 files changed, 25 insertions(+) create mode 100644 apps/base/forte-drop/forte-drop-pdb.yaml diff --git a/apps/base/forte-drop/forte-drop-pdb.yaml b/apps/base/forte-drop/forte-drop-pdb.yaml new file mode 100644 index 0000000..85c07e3 --- /dev/null +++ b/apps/base/forte-drop/forte-drop-pdb.yaml @@ -0,0 +1,24 @@ +# Keep at least 1 web pod up during voluntary disruptions (node drain, upgrade). +# Pairs with replicaCount: 2 so a drain can evict one pod while the other serves. +# +# Selector verified against live forteapp-chart deployments (mcp10x, argocd-mcp): +# the chart's pod selector is {app.kubernetes.io/instance, app.kubernetes.io/name, +# component: app} where instance/name == the ArgoCD Application (Helm release) name. +# Using all three labels also disambiguates the web pods from the forte-drop-mcp +# deployment that shares the forte-drop namespace (its instance/name == forte-drop-mcp). +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: forte-drop-web + namespace: forte-drop + labels: + app.kubernetes.io/name: forte-drop + app.kubernetes.io/part-of: apps + app.kubernetes.io/managed-by: argocd +spec: + minAvailable: 1 + selector: + matchLabels: + app.kubernetes.io/instance: forte-drop + app.kubernetes.io/name: forte-drop + component: app diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/base/forte-drop/kustomization.yaml index feda2e9..99bffbb 100644 --- a/apps/base/forte-drop/kustomization.yaml +++ b/apps/base/forte-drop/kustomization.yaml @@ -3,5 +3,6 @@ kind: Kustomization resources: - forte-drop.yaml - keycloak-client-forte-drop.yaml +- forte-drop-pdb.yaml # - forte-drop-secrets-sealed.yaml # added in follow-up commit # - auth-oidc-sealed.yaml # added in follow-up commit (after Keycloak registrar creates client_secret) From c4b7167f9e3ce5446c8456365e023c8b2b6a8950 Mon Sep 17 00:00:00 2001 From: Sten Date: Fri, 29 May 2026 10:03:57 +0200 Subject: [PATCH 05/10] feat(apps): add forte-drop-secrets sealed secret Sealed forte-drop-secrets with the real UpCloud Managed Object Storage creds (existing drops bucket), PG creds matching the deployed forte-drop-pg-creds, and PASSWORD_GATE_SECRET. Consumed by both web + mcp deployments (envSecretName) and the pg-backup CronJob (S3 creds). --- .../forte-drop/forte-drop-secrets-sealed.yaml | 18 ++++++++++++++++++ apps/base/forte-drop/kustomization.yaml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 apps/base/forte-drop/forte-drop-secrets-sealed.yaml diff --git a/apps/base/forte-drop/forte-drop-secrets-sealed.yaml b/apps/base/forte-drop/forte-drop-secrets-sealed.yaml new file mode 100644 index 0000000..fd6613b --- /dev/null +++ b/apps/base/forte-drop/forte-drop-secrets-sealed.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: forte-drop-secrets + namespace: forte-drop +spec: + encryptedData: + PASSWORD_GATE_SECRET: AgAlZOSURqrQo7dqylFYetr50dWEgWNWTtBEjwgmIp/aFbr2X29xSD0gHpANIF+eXuZ1zGvTX9j2r4YoXgmcb/MjKMd85mYG7FTa0PFdxSxighvKOS3BWQwb7chgNFHMwiZNQAAMNUD6NjlfLZXS0V2SMI+5zz2siYjt8OfgLKfSx5v8+zDFb+pGrCUuUZlMaqFF2ZmOryYNMYYq69iE2EW8UQcP49tmP4aaPx5wIGq6nIo+Qc/c6L4K83nK5ijHEipkgEs0Z0vDTDT4ZBbV0Qyif+Uc1j0eG9+vvIkHBCrGC0WbLOCXiSwmzYF5U2uTRKUbkiPWfohW1Ds2hqveIwg6oSOhK6B+O9s17QagKenYBEER2WAXLq/BLbFpwwijZcGaVnIYsFLUjcheQVPEdaxIyOPh3/qhDjEBX7PevwDzEyOGB6W54TAndx/Go6+Mnvq4aefTOsD+Jy9Iqc3SI3mDoTCSuDMrgnE4QUlImm+Qtk+qPC35n36B9s7yqsHZISbC4zBGAwk7MFfhwTZ3t4z3WXBjevT1zFfs/NeQaRPlcRtO7656C62qroOITty0rT4JAbz93fanqHlqFmiMrYNUIu3wlbr/NGgMlZ1kRVnwIryVup5vljZmvhRtF+TGMwIA+3zbO9U3NasjScnh5sraOauIf7kVsBYTU/2oKb37HvroyILXd8fbpPq+lZ1ejcvVseC+heOI6hvvH/DYztgFn5s9egzcfauicCijWtk1jn0ym1RzOYBotP0VFhXX5bF1HHoGGwQWC32fgqFWwxJ8 + PGPASSWORD: AgC5TnoWNEQK13fJHfxM8/ZHRlX85Kd11d8Ig+PgMGcLkxab7fvZJuzg7VX0iksK2UG/c1w1q8fRigMl1WWxmZvW2zM4/BsF45WR99QGt4Z1dhTFHdL5/7OZq2jDMYY5x/7gzCccb2pMAZS7p4rjnJM0EPd7YuMlhXZwIjXcck9Qu8GDpyEu1gg2A1tIfUzjvD+HeTbs6NLy16WHYpMG7Wv4WBNNPO+0k5Z0OwwXmIFSJvSMUHWBg/u2JCJ/1DL0zLyRCdXXOB8VVGok3TFbNscGQqFtFkaaVd0rA4Ao6abrsezcsfZFkcSsfVhATAw+M8ORYRC642BFJjzbhGSbDBsYq7aYyT86MVoqnRJgXK7/K2qzIeT9agM3sJm3ZuRjVCBDGdp/Xo2Rbtely8PH4GwEZI+vRFhTFAj5maduHMt0ZmScusOKuGvr0kFFXKw6XthBT3Arg5Q40upiBtPPrpWhWJYtgT3EfFuXNoc1c1Do7UZ5cmfe51sOBkMZjYk7+8B8a2IejKRflynBcCDQ7r2u76SF18dS5/R7jdOlreyL0yuEiULicKpX8BXPa9dN6Xja8OVPfIHvk0+TwnaYx7YaZDEXlyG0GKv9R3i0wykL1RdDCii6eRSJAp1UFmnCKZJJd9Y6djw7XBAd9G/ux8Vz45XkP+f2Kfgx32HmZsqPq2xaW5DoAeM33uDMfi3Ist+FVRtffuSrTQwBNp76VyhI7YY9MSB5JR1sBUGxGXtAGg== + PGUSER: AgAEyIU4lFCV6A7PyRyc2dTuE3HJW/2KkcDWxj1gOe7mtmmnDS6XhOIW0SMuIYOExNR+pUAiwadMmoGGUXj+k0r4VsnMEiG3HplAXr6FERNyxFtW8XnaXFA7++xOH5751fC6W5BIM5MLzt8YQQ6C8mrp4JXnUTm5K1iBjRH6WtggE5WS/tYV/Y5HryQ18fvyU6saHLzwzn1vJzxrFmCU0zsdglkBTfg+WBZh3f4ufOXFVrOQfm4AKjJclTEbMnqO0vupkbj6VRSKh5timPoNpqKuoA7PsD5YOCyiFzgFUnwAybg8hQAPJ8gMimvuEUxArbpd3bQDhQqBi0j9IZ7Z6ht1+DMp2niZfJ+B1S9R31OmERt/DtXjnMeN7/VDCpABmRUvJ7HJ2DVFUCE2S38V4QDXNRUjvmRgT3mEjMYNsoYnuFf9THqHc3Wzvq96hGnDSVEnLdBRp7WoPO++0WVRvBTM21nhqx2zGvw1HN718xpndhJGrDdRtod6DCnWzKsVHi2ndxo9ZsxCn6yLYn8aRjk9ElHlKFOQ8z6LDqMc4Yt7XbPCWysqH4pu0xyJoz7XmM5y6/y1mrXKhMTNdZm+kE3SiXtVJl8lJioQUo4gOjJs8LfOFM5dfU1IPkKW14tEv/DNvQqqZmAzK9qjXcARgaFulvBvMO4xn+PbYRHm0VwOVo0iwrBS0FHNP/+kVWmZ7TZ+BI5LCw== + S3_ENDPOINT: AgAT2f9jZ8NcpO4S/CE+Arsb0+UsIb1DT4iqVYPxssSip9b/Bdb2mZ2IbzzHHEUtDOSdTIzpHoCaflD1CYrrUevHwu6/ufb44GWG7GaOwHfXUTKOrWUFb/fhXQpXklgEL5txuu48ud2/DekbY2iz3yvKtxffs2otn42nGcPQ/rCcV+AUE0z0q47F1dcicX/enVKeQ/UHszxXU2l7G3Yt3Onxy+ILIc3o/lGuaI8uVX0Zv0wdKUiXpVTy1alm9lJQklNd08/BElPUhKS+2zdIooFvcjz0YRdZrl4gdOzqdw3/2aWsFfkSyE6VhmbQ4pzD5t4H376DCrXFeES/5emvHazZYh4f2RZuiDE1gyHWzC5WMH1ftvHL3Qv6dav9nY8Q/D61Qzwr5dge2Vr69harbvDCMUNDlmu5uxj61lvNkavsGIhmQc1QbHiz5/1RdGi4F8BvBRKyWW1UKDga9QthgUy9JJW0Y9klegWoZIXcTR4BMELExtQCHcNpQhfNXvQzTtNoTNdOF97j1Sat3cOVfH9DveOgOSVkB5U1twGc+LoXF9Vh2aSolk2TNoew3lhHaEcKXrDtuOLWlvSDYOz65JnQUOpQ+ozNfgAKWzoISnS2VcZ0mly1JRmGnHtJMMDGsWepcSRafB7srcxyQaZ4k1u5HQI2Qg8/H7kKgAgYcbEQ2HSYKwjEEaSWIomkBbmmZaq7JXayAJrdsNKQdgtVNZMxFTBR/Krmpi5Z+I26Rob62A== + S3_KEY: AgAUxDq3sxrD9iUNiOfNE+C6TIPMVmVQYchPEW00EyaFhm7ysd9NII2LWHWDcH2zdDYovvGrOmhXnk1O66taKbM9bs1QdTsx2s/sesihPXPQNcTyHbhhSahhRv7/7+V/iNKtVb+6YhRrzCLDNbMLREGrClMo6rJy/IQM5zo8We02yYTETJcVV0+N1MjClkU4yPJy+A+HA5otVWzRfDmwu7rwDUH/PQbzpiB0AaEubXIpRbgrR6j3GKATKr8X4EBjgeUEzG6doIS/9+ZLw4DVzHRhk06RrXW02fu6dg0nPZjcgWYGyfY2vPTQ89G/0lIuZctFI6VcKeMJodY6pDqmGn2SMCA6teR93xFoxmGANGQMa7lqanY2KZc7hXWT2saF5e34nUKJtuvTWZhoEEN2MT8CCQlBVYriBQTjVH1NE0GrHgXJ1MpYF98LaJMl0L0xw47Pk1NlbPAyoamA+prQY83qe6g9PITlqr1xzVhamc02/Yn1xoMXzvfhBX0PFetTdlH1wxaDtpIAsKupdOIuf7NNfvySFToM03ocxvNzXVdqey09wPzAE19hPTfFLMeFjgg8hCTPW0q2yhHwJEd+uH1MhJYwalpTebGGQ7RO3Cs7lNW3Jhe+Ji4haR9NKcfH+O2jioUNz4tNB2UBQIHW9jYEsFFV9fB9j7mauvOa/UhJjYzo/yaUV8NQ7uAVC4CwEiZtuyXciT4pL+zViOPVET6uC1nx+w== + S3_SECRET: AgAvWoqSF/qo6FNEcIaYQ75MJjetpjkMfnviZMcQxxCBbnrO2NPs0bqlcyKG0QSarOnBdbsKBsflxCOBqMweX8XpduKZWCAa0CbF8/PQ+TTcjHGeY9jU0/8bMhG4YnaKSy9XbzHKIMQ+pQvwV5/IF+2FSkgEaFxvMrGDAYoT/mmqT6k/NTIO9IsirDAMaOCkmb56DhN37yFNa94eaFN7jKeqwEsGeS6AB2IV8IWlyUpul4BQfS1BUfhYGPg+4betYTdvuvKSEGZ6fv9wMcUl86B1YD13Fs9D8rLenpsOjSgpflWZDOpq05OIqoeeht87N6kbMF7fKVJwfSCo3FDeEGq4Rp+T8ufYl661ac7Gh7weg4T2JCKjW4aJN/Nv3y4s57XqQXzlXW5zQOHBC6l5hH0XbnwByFnpfuyjG4G0Ssza3kdfDq5rXFnnSBMHCeVQgOEtfrC9gxOUYdCYPnz7o8sTYkP5mGITcnk9I7jn/aSc2hCIF8JPPRuOh415hwhqX4c/rp1IjQhV9dD/0YVyv9JBkn5aGq/HYDZMTn406gFd0f+LQiMZfnVChXBr151+JYPgZhEoDxwZ2+kU5YWLRKthSgOyzZTRIOk5wJKYxNBJVxU5GlsZe1uDzeertwfE8jOKpd3z0gNIzmVTvOuQUwyXDkTPxvpl3ITFHTNAmHw4sugsOu4IpRQOEQUitud3gkYUzAJQ4buUo3h4CV+VN/Of/jZ0t3OuhwDt/4t/w6nKRFJkd2tlkAp3 + template: + metadata: + name: forte-drop-secrets + namespace: forte-drop diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/base/forte-drop/kustomization.yaml index 99bffbb..f79762a 100644 --- a/apps/base/forte-drop/kustomization.yaml +++ b/apps/base/forte-drop/kustomization.yaml @@ -4,5 +4,5 @@ resources: - forte-drop.yaml - keycloak-client-forte-drop.yaml - forte-drop-pdb.yaml -# - forte-drop-secrets-sealed.yaml # added in follow-up commit +- forte-drop-secrets-sealed.yaml # - auth-oidc-sealed.yaml # added in follow-up commit (after Keycloak registrar creates client_secret) From d6a97a22df23d0e1d76754ee968bc2a256f8b49c Mon Sep 17 00:00:00 2001 From: Sten Date: Fri, 29 May 2026 10:25:37 +0200 Subject: [PATCH 06/10] fix(apps): explicit forte-drop namespace (sync-wave -1, Prune=false) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex review: the apps overlay applies namespaced resources (keycloak-client Secrets, forte-drop-secrets, PDB) to forte-drop, but no base created the namespace — first sync on a fresh cluster raced ahead of the Applications' CreateNamespace and failed with 'namespaces forte-drop not found' until a retry. Add an explicit Namespace at sync-wave -1 so it exists before the wave-0 namespaced resources (covers both web + mcp bases via the shared parent). Prune=false keeps removing a base from cascade- deleting the namespace + postgres data + the other deployment. --- apps/base/forte-drop/kustomization.yaml | 1 + apps/base/forte-drop/namespace.yaml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 apps/base/forte-drop/namespace.yaml diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/base/forte-drop/kustomization.yaml index f79762a..af947b2 100644 --- a/apps/base/forte-drop/kustomization.yaml +++ b/apps/base/forte-drop/kustomization.yaml @@ -1,6 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: +- namespace.yaml - forte-drop.yaml - keycloak-client-forte-drop.yaml - forte-drop-pdb.yaml diff --git a/apps/base/forte-drop/namespace.yaml b/apps/base/forte-drop/namespace.yaml new file mode 100644 index 0000000..dd91428 --- /dev/null +++ b/apps/base/forte-drop/namespace.yaml @@ -0,0 +1,17 @@ +# Owns the forte-drop namespace shared by the web + mcp deployments and the +# postgres StatefulSet (infra overlay). sync-wave -1 ensures the namespace exists +# before the namespaced Secrets/PDB in this base apply (avoids a first-sync +# "namespaces forte-drop not found" race when the business-apps parent syncs). +# Prune=false so removing this base never cascade-deletes the namespace (and with +# it postgres data + the mcp deployment) — matches the earlier decision to keep +# namespace ownership decoupled from any single workload. +apiVersion: v1 +kind: Namespace +metadata: + name: forte-drop + annotations: + argocd.argoproj.io/sync-wave: "-1" + argocd.argoproj.io/sync-options: Prune=false + labels: + app.kubernetes.io/managed-by: argocd + app.kubernetes.io/part-of: apps From 96db244e039256c36461f95e984fc74e1cae9e42 Mon Sep 17 00:00:00 2001 From: Sten Date: Fri, 29 May 2026 10:38:51 +0200 Subject: [PATCH 07/10] refactor(apps): move forte-drop postgres from infra to apps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per reviewer (danijel): forte-drop's DB deployment belongs in apps/, not infra/. Straight relocation — same structure (Application + resources/ subdir), source.path updated to apps/base/forte-drop-postgresql/resources, wired into apps/overlays/upc-dev. Backup CronJob + RESTORE.md + sealed pg creds move with it. Consolidates the whole forte-drop deployment (postgres + web + mcp) under apps/. The infra PR (#17) is now superseded by this. --- apps/base/forte-drop-postgresql/RESTORE.md | 143 ++++++++++++++++++ .../forte-drop-postgresql.yaml | 40 +++++ .../forte-drop-postgresql/kustomization.yaml | 4 + .../resources/forte-drop-pg-creds-sealed.yaml | 14 ++ .../resources/kustomization.yaml | 6 + .../resources/pg-backup-cronjob.yaml | 93 ++++++++++++ .../resources/postgresql.yaml | 105 +++++++++++++ apps/overlays/upc-dev/kustomization.yaml | 1 + 8 files changed, 406 insertions(+) create mode 100644 apps/base/forte-drop-postgresql/RESTORE.md create mode 100644 apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml create mode 100644 apps/base/forte-drop-postgresql/kustomization.yaml create mode 100644 apps/base/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml create mode 100644 apps/base/forte-drop-postgresql/resources/kustomization.yaml create mode 100644 apps/base/forte-drop-postgresql/resources/pg-backup-cronjob.yaml create mode 100644 apps/base/forte-drop-postgresql/resources/postgresql.yaml diff --git a/apps/base/forte-drop-postgresql/RESTORE.md b/apps/base/forte-drop-postgresql/RESTORE.md new file mode 100644 index 0000000..028bd6b --- /dev/null +++ b/apps/base/forte-drop-postgresql/RESTORE.md @@ -0,0 +1,143 @@ +# forte-drop Postgres — backup & restore runbook + +## What gets backed up + +A CronJob (`forte-drop-pg-backup`, namespace `forte-drop`) runs nightly at **02:00 UTC**: + +1. `pg_dump` of the `drops` database → gzip. +2. Upload to **UpCloud Managed Object Storage**: `s3://drops/_pgbackups/forte-drop-.sql.gz` + (the `_pgbackups/` prefix is collision-proof: app slugs match `/^[a-z0-9][a-z0-9-]{0,62}$/` + and can never start with `_`). +3. Retention: dumps older than **30 days** are pruned. + +S3 creds come from the `forte-drop-secrets` Secret (`S3_ENDPOINT` / `S3_KEY` / `S3_SECRET`). +Postgres creds from `forte-drop-pg-creds` (`pgusername` / `pgpassword`). + +> **Object storage is the durable tier.** App data + DB backups both live in UpCloud +> Managed Object Storage (replicated by UpCloud). The in-cluster Postgres PVC is the +> live working copy; the nightly dump is the recovery point. The PVC carries +> `Prune=false,Delete=false` so ArgoCD never deletes it. + +## Prerequisites + +```bash +export KUBECONFIG=~/Downloads/dev-fd-no-svg1_kubeconfig.yaml +# Confirm the namespace + DB pod are up: +kubectl -n forte-drop get pods -l app.kubernetes.io/name=postgresql +``` + +## List available backups + +```bash +# Run an ephemeral mc pod with the app's S3 creds: +kubectl -n forte-drop run mc-list --rm -it --restart=Never \ + --image=quay.io/minio/mc:RELEASE.2024-11-21T17-21-54Z \ + --overrides='{"spec":{"containers":[{"name":"mc","image":"quay.io/minio/mc:RELEASE.2024-11-21T17-21-54Z","command":["sh","-c","mc alias set obj \"$S3_ENDPOINT\" \"$S3_KEY\" \"$S3_SECRET\" >/dev/null && mc ls obj/drops/_pgbackups/"],"envFrom":[{"secretRef":{"name":"forte-drop-secrets"}}]}]}}' +``` + +## Manually trigger a backup (before risky changes) + +```bash +kubectl -n forte-drop create job --from=cronjob/forte-drop-pg-backup pg-backup-manual-$(date +%s) +# Watch: +kubectl -n forte-drop get jobs -l app.kubernetes.io/component=backup +kubectl -n forte-drop logs -l app.kubernetes.io/component=backup --tail=40 +``` + +## Restore a dump + +> **Destructive.** This overwrites the live `drops` database. Take a fresh manual +> backup first (above) and confirm with whoever owns the data before proceeding. + +### 1. Pick the dump to restore + +List backups (above), choose `forte-drop-.sql.gz`. + +### 2. Run a restore pod that pulls the dump and pipes it into Postgres + +```bash +DUMP="forte-drop-20260530T020000Z.sql.gz" # <-- set to the chosen file + +kubectl -n forte-drop run pg-restore --rm -it --restart=Never \ + --image=postgres:16-alpine \ + --overrides='{ + "spec": { + "containers": [{ + "name": "restore", + "image": "postgres:16-alpine", + "command": ["sh","-c","set -euo pipefail; \ + apk add --no-cache curl >/dev/null; \ + # download via mc is simpler — use a 2-step instead (see note). \ + echo placeholder"], + "envFrom": [ + {"secretRef":{"name":"forte-drop-pg-creds"}}, + {"secretRef":{"name":"forte-drop-secrets"}} + ] + }] + } + }' +``` + +**Simpler 2-pod approach (recommended — avoids cramming mc + psql in one image):** + +```bash +DUMP="forte-drop-20260530T020000Z.sql.gz" + +# (a) Download the dump from object storage to a local file: +kubectl -n forte-drop run mc-get --rm -it --restart=Never \ + --image=quay.io/minio/mc:RELEASE.2024-11-21T17-21-54Z \ + --overrides='{"spec":{"containers":[{"name":"mc","image":"quay.io/minio/mc:RELEASE.2024-11-21T17-21-54Z","command":["sh","-c","mc alias set obj \"$S3_ENDPOINT\" \"$S3_KEY\" \"$S3_SECRET\" >/dev/null && mc cat obj/drops/_pgbackups/'"$DUMP"'"],"envFrom":[{"secretRef":{"name":"forte-drop-secrets"}}]}]}}' \ + > /tmp/$DUMP + +# (b) Pipe it into the live Postgres via the service: +gunzip -c /tmp/$DUMP | kubectl -n forte-drop run pg-restore --rm -i --restart=Never \ + --image=postgres:16-alpine \ + --overrides='{"spec":{"containers":[{"name":"psql","image":"postgres:16-alpine","stdin":true,"command":["sh","-c","PGPASSWORD=\"$pgpassword\" psql -h forte-drop-postgresql.forte-drop.svc -U \"$pgusername\" -d drops"],"env":[{"name":"pgusername","valueFrom":{"secretKeyRef":{"name":"forte-drop-pg-creds","key":"pgusername"}}},{"name":"pgpassword","valueFrom":{"secretKeyRef":{"name":"forte-drop-pg-creds","key":"pgpassword"}}}]}]}}' +``` + +> The app's schema is created idempotently on boot (`CREATE TABLE IF NOT EXISTS` + +> `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` in `src/repo/pg.ts`), and `pg_dump` +> output includes the data. For a clean restore into a fresh DB this just works. +> To restore over an existing DB with conflicting rows, drop/recreate the `drops` +> database first (coordinate downtime — scale the web Deployment to 0 during the +> restore so the app isn't writing). + +### 3. Verify + +```bash +kubectl -n forte-drop run pg-check --rm -it --restart=Never \ + --image=postgres:16-alpine \ + --env="PGPASSWORD=$(kubectl -n forte-drop get secret forte-drop-pg-creds -o jsonpath='{.data.pgpassword}' | base64 -d)" \ + --command -- psql -h forte-drop-postgresql.forte-drop.svc -U drops -d drops \ + -c "SELECT count(*) AS drops FROM drops;" -c "SELECT count(*) AS view_hits FROM view_hits;" +``` + +### 4. Bring the app back + +```bash +# If you scaled web to 0 for the restore: +kubectl -n forte-drop scale deploy/forte-drop --replicas=2 +``` + +## Object data (uploaded drop files) + +Drop files live in `s3://drops//...` in the same managed bucket. They are +**not** part of the pg backup (the dump only holds metadata). Object storage is +UpCloud-managed/replicated, so no separate file backup is configured. If a +file-level backup is later required, mirror the bucket to a second bucket/region: + +```bash +mc mirror --overwrite obj/drops/ backup-target/drops-mirror/ +``` + +(Exclude `_pgbackups/` from the app-data mirror if you split them.) + +## Disaster scenarios + +| Scenario | Recovery | +|---|---| +| Postgres pod crash / reschedule | StatefulSet reattaches the PVC; ~1–2 min downtime; no data loss. | +| PVC lost / corrupted | Recreate StatefulSet, restore latest nightly dump (above). Data since last dump is lost. | +| Accidental `drops` table data loss | Restore latest dump; or `pg_restore` a single table from a dump. | +| Namespace deleted | PVC has `Prune=false,Delete=false`; recreate Applications, PVC re-binds, app recovers. Backups in object storage are independent. | +| Object storage bucket lost | UpCloud-managed (replicated). If the IAM key is rotated, update `forte-drop-secrets` (re-seal). | diff --git a/apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml b/apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml new file mode 100644 index 0000000..313fc0c --- /dev/null +++ b/apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml @@ -0,0 +1,40 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: forte-drop-postgresql + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + labels: + app.kubernetes.io/name: forte-drop-postgresql + app.kubernetes.io/part-of: apps + app.kubernetes.io/managed-by: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + source: + repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git + targetRevision: HEAD + path: apps/base/forte-drop-postgresql/resources + + destination: + server: https://kubernetes.default.svc + namespace: forte-drop + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - CreateNamespace=true + - Validate=true + - ServerSideApply=true + + ignoreDifferences: + - group: apps + kind: StatefulSet + jsonPointers: + - /spec/volumeClaimTemplates diff --git a/apps/base/forte-drop-postgresql/kustomization.yaml b/apps/base/forte-drop-postgresql/kustomization.yaml new file mode 100644 index 0000000..28749fb --- /dev/null +++ b/apps/base/forte-drop-postgresql/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- forte-drop-postgresql.yaml diff --git a/apps/base/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml b/apps/base/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml new file mode 100644 index 0000000..ad22994 --- /dev/null +++ b/apps/base/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: forte-drop-pg-creds + namespace: forte-drop +spec: + encryptedData: + pgpassword: AgBYokuQRCTmPGC8soB7n8W39nmSAZDTV97i77NmdMh5ndaZNtCtXxhMcpM2z8kaGv2tCWIh47dr38a5tGPZ0TsmeEQOF9Rbbtq1fyR7pJwT8S+N2Z2zu354wNWQlltEAVvTvthe2wTer/BpyRofeSZprxihmfNpuHUP8rLsnIXln5tOWDzJ8hnRWoYZFITaihC2qrJj/kFE0Rfdcmzt1tSq3jCB/rWijVaJF9XSh4rzQoqZDiUNjDPUjyERILw59JWU4zf9OKcqNHDnmpXBR4LSjLhd9waN6ElEzO4gGcVaHISKrTwewX1ONwPHDnw6lqkQObyBPx8aUsGzxLkUhNDtvIYDkB4BKWP5Qu4bNcSztIbrxi6l7Lr/DWC9qTbKm1/p83rc6r8VqRMURUyQg8/vBlCHOIUbZ8DM1OfNlMd8gvcSkaxEVIdCDUjguCvE3cGyG4cqv2unllZQ+9417WwLNJecT6x1EL3nQyAlK5c9vUIbcVyaFlbSUcGB7xmPgZrZ6/3RDyOH6Tmew1ssV9gLvdaehscUE0fjnnFnJpczkwdyxIOSNLIkjlWetCKEbhowJbzk05h3M2p6XQQOuNTnsYjAADMGD72GUgAQlY8KXmDELtv09KELcXbeYS4gABPpMrVmvZymq8lqQ13Py8o+cIqbrU5V86WxASTfQ5gMo/ymYabuhTBIapcnaKR1dFCCfu8deh5f2HJ6/1NjdWR+XvEshg+EF5OkTUInukX4vA== + pgusername: AgCs6vyQ8CIv5OneP/jMltIPGdZQbpq/BFmQM1mkBD61Ve+anzve5K0Gkg+zsNfbZf0pOPAXtu4C4aL1Lwv7gqpoe4Hp/UEb/X9uLfJ1b8ZitmM1XsPmmSiCskHjrc2BLkAvfrVIXkHc3LOY2uZ/E5stc6Ss2WFE8/uzzVXW0B8fdEK0criludQ8iwR1gypulEcDNomXgkK/1gmmCWosUcVv4jDMDhqBD+b9WYnBB6J73gUclWVMvYDFdNas2PuoRzu5Twc9TAZrTxN5lvLOXAonOo0YiUbUhEC83sfMWYDT5/9OxqcJhAxtgFe9j83MpCwLSwfeLZm7UsUapWDb60MxPJLGvoGD/ZOhkeYt/YCZYROa57TMslVIL5YU1KCiNWvtRjIqnvdiBxI7MRvPUfAoawS4ktT5PDhTTfrixFbaF95jul2kKBXV+OYB1UNsFhcCgZx9rzYRt4lNmBv4m4HeXIp3EYY8VlGLQ45BVVqjJ4QkISvb7ifQWH1aPMQllj+J3GwW0KJN0dEgsh1LT+C7W5I5mq461NOTF1eih/XRBeuPoLlgApxiGXvFCTx8lji2/JIdOaqcg29hdabSprxa0YMStChi2pbtHhRzAuFCp8mInGt8Q406vu67Y4/51yuwI40YeDVu0lf010TB+/v2Zy3OrNyjlqrD5JNynsLuRl3UhuAKC14Xhg/MiDLvTzfsYE8aog== + template: + metadata: + name: forte-drop-pg-creds + namespace: forte-drop diff --git a/apps/base/forte-drop-postgresql/resources/kustomization.yaml b/apps/base/forte-drop-postgresql/resources/kustomization.yaml new file mode 100644 index 0000000..7b5a754 --- /dev/null +++ b/apps/base/forte-drop-postgresql/resources/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- postgresql.yaml +- forte-drop-pg-creds-sealed.yaml +- pg-backup-cronjob.yaml diff --git a/apps/base/forte-drop-postgresql/resources/pg-backup-cronjob.yaml b/apps/base/forte-drop-postgresql/resources/pg-backup-cronjob.yaml new file mode 100644 index 0000000..4304424 --- /dev/null +++ b/apps/base/forte-drop-postgresql/resources/pg-backup-cronjob.yaml @@ -0,0 +1,93 @@ +# Nightly logical backup of the forte-drop Postgres → UpCloud Managed Object Storage. +# Dumps to s3://drops/_pgbackups/ (the `_` prefix is collision-proof: app slugs match +# /^[a-z0-9][a-z0-9-]{0,62}$/ and can never start with `_`). Retains 30 days. +# +# Pod shape: initContainer pg_dump → shared emptyDir → mc upload + retention prune. +# Both images pinned. S3 creds reuse forte-drop-secrets (the app's UpCloud user has +# s3:* on the drops bucket). PG creds from forte-drop-pg-creds. +apiVersion: batch/v1 +kind: CronJob +metadata: + name: forte-drop-pg-backup + namespace: forte-drop + labels: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop + app.kubernetes.io/component: backup +spec: + schedule: "0 2 * * *" # 02:00 UTC daily + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + backoffLimit: 2 + template: + metadata: + labels: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop + app.kubernetes.io/component: backup + spec: + restartPolicy: Never + securityContext: + runAsNonRoot: true + runAsUser: 65532 + fsGroup: 65532 + volumes: + - name: work + emptyDir: {} + initContainers: + - name: dump + image: postgres:16-alpine + command: + - sh + - -c + - | + set -euo pipefail + TS=$(date -u +%Y%m%dT%H%M%SZ) + echo "dumping to /work/forte-drop-${TS}.sql.gz" + PGPASSWORD="$PGPASSWORD" pg_dump \ + -h forte-drop-postgresql.forte-drop.svc \ + -p 5432 -U "$PGUSER" -d drops \ + --no-owner --no-privileges \ + | gzip -9 > "/work/forte-drop-${TS}.sql.gz" + echo "dump complete: $(ls -lh /work/)" + env: + - name: PGUSER + valueFrom: + secretKeyRef: { name: forte-drop-pg-creds, key: pgusername } + - name: PGPASSWORD + valueFrom: + secretKeyRef: { name: forte-drop-pg-creds, key: pgpassword } + volumeMounts: + - name: work + mountPath: /work + containers: + - name: upload + image: quay.io/minio/mc:RELEASE.2024-11-21T17-21-54Z + command: + - sh + - -c + - | + set -euo pipefail + mc alias set obj "$S3_ENDPOINT" "$S3_KEY" "$S3_SECRET" + mc cp /work/*.sql.gz "obj/${S3_BUCKET}/_pgbackups/" + echo "uploaded. pruning backups older than 30d:" + mc rm --recursive --force --older-than 30d "obj/${S3_BUCKET}/_pgbackups/" || true + echo "backup retention pass complete" + env: + - name: S3_ENDPOINT + valueFrom: + secretKeyRef: { name: forte-drop-secrets, key: S3_ENDPOINT } + - name: S3_BUCKET + value: "drops" + - name: S3_KEY + valueFrom: + secretKeyRef: { name: forte-drop-secrets, key: S3_KEY } + - name: S3_SECRET + valueFrom: + secretKeyRef: { name: forte-drop-secrets, key: S3_SECRET } + volumeMounts: + - name: work + mountPath: /work diff --git a/apps/base/forte-drop-postgresql/resources/postgresql.yaml b/apps/base/forte-drop-postgresql/resources/postgresql.yaml new file mode 100644 index 0000000..4fa4aa8 --- /dev/null +++ b/apps/base/forte-drop-postgresql/resources/postgresql.yaml @@ -0,0 +1,105 @@ +apiVersion: v1 +kind: Service +metadata: + name: forte-drop-postgresql + namespace: forte-drop + labels: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop + app.kubernetes.io/component: database +spec: + type: ClusterIP + ports: + - name: tcp-postgresql + port: 5432 + targetPort: tcp-postgresql + selector: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: forte-drop-postgresql + namespace: forte-drop + labels: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop + app.kubernetes.io/component: database +spec: + serviceName: forte-drop-postgresql + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop + template: + metadata: + labels: + app.kubernetes.io/name: postgresql + app.kubernetes.io/instance: forte-drop + app.kubernetes.io/component: database + spec: + containers: + - name: postgresql + image: postgres:16-alpine + # NOTE: no securityContext. The official postgres image's entrypoint must + # start as root to chown a fresh /var/lib/postgresql/data, then drops to + # the postgres user (uid 70 in alpine) via gosu. Forcing runAsNonRoot here + # breaks initdb on a fresh PVC. Matches the vaultwarden-postgresql pattern. + ports: + - name: tcp-postgresql + containerPort: 5432 + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: forte-drop-pg-creds + key: pgusername + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: forte-drop-pg-creds + key: pgpassword + - name: POSTGRES_DB + value: drops + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + livenessProbe: + exec: + command: + - sh + - -c + - pg_isready -U "$POSTGRES_USER" -d drops + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - sh + - -c + - pg_isready -U "$POSTGRES_USER" -d drops + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + volumeClaimTemplates: + - metadata: + name: data + annotations: + argocd.argoproj.io/sync-options: Prune=false,Delete=false + spec: + accessModes: + - ReadWriteOnce + storageClassName: upcloud-block-storage-maxiops + resources: + requests: + storage: 5Gi diff --git a/apps/overlays/upc-dev/kustomization.yaml b/apps/overlays/upc-dev/kustomization.yaml index 22bf6ad..9b92f0f 100644 --- a/apps/overlays/upc-dev/kustomization.yaml +++ b/apps/overlays/upc-dev/kustomization.yaml @@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../../base +- ../../base/forte-drop-postgresql - ../../base/forte-drop - ../../base/forte-drop-mcp - dbunk-demo From 61a8a2b4ac0f410ae0c7d17717ef9962a05eae20 Mon Sep 17 00:00:00 2001 From: Sten Date: Fri, 29 May 2026 12:14:09 +0200 Subject: [PATCH 08/10] chore(apps): clarify auth-oidc follow-up (drop commented-out resource line) ai-review: a commented-out resource line reads as GitOps debt. Replace the '# - auth-oidc-sealed.yaml' line with an explicit NOTE explaining it's a deliberate post-deploy step (needs the registrar-generated client-secret), not a disabled resource. --- apps/base/forte-drop/kustomization.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/base/forte-drop/kustomization.yaml index af947b2..83e7172 100644 --- a/apps/base/forte-drop/kustomization.yaml +++ b/apps/base/forte-drop/kustomization.yaml @@ -6,4 +6,8 @@ resources: - keycloak-client-forte-drop.yaml - forte-drop-pdb.yaml - forte-drop-secrets-sealed.yaml -# - auth-oidc-sealed.yaml # added in follow-up commit (after Keycloak registrar creates client_secret) + +# NOTE: the web sidecar's auth-oidc SealedSecret is added in a follow-up commit, +# once the Keycloak registrar has created forte-drop-oidc-credentials post-deploy +# (see PR description for the one-time seal step). It is intentionally NOT a +# resource here yet — sealing it requires the registrar-generated client-secret. From 338b4de3baaf3548e98be4abd09ff944fe18d870 Mon Sep 17 00:00:00 2001 From: Sten Date: Fri, 29 May 2026 14:05:29 +0200 Subject: [PATCH 09/10] refactor(apps): registrar-managed oidc creds, drop mcp client, DRY secret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per platform review (danijel): - keycloak-client-forte-drop: add the secret{} block telling the registrar where to write the credential Secret + key names (forte-drop-oidc-credentials, client-id/client-secret). The forte-helm oidc sidecar consumes that registrar-created Secret — no manual auth-oidc SealedSecret step (removed that NOTE). - Delete keycloak-client-forte-drop-mcp: auth.type: mcp auto-registers the MCP client; no manual config needed. - Re-seal forte-drop-secrets with all shared env (BASE_DOMAIN, PG*, S3_*, PASSWORD_GATE_SECRET) so both deployments get identical values via envSecretName (values extraEnv now carries only APP_MODE). --- .../keycloak-client-forte-drop-mcp.yaml | 27 ------------------- apps/base/forte-drop-mcp/kustomization.yaml | 9 +++---- .../forte-drop/forte-drop-secrets-sealed.yaml | 18 ++++++++----- .../keycloak-client-forte-drop.yaml | 22 ++++++++++----- apps/base/forte-drop/kustomization.yaml | 5 ---- 5 files changed, 32 insertions(+), 49 deletions(-) delete mode 100644 apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml diff --git a/apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml b/apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml deleted file mode 100644 index 228062a..0000000 --- a/apps/base/forte-drop-mcp/keycloak-client-forte-drop-mcp.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# MCP audience client. RFC 7591 dynamic-registration capable MCP clients (e.g., -# Claude Desktop) discover this via /.well-known/oauth-protected-resource and -# request tokens with aud=https://mcp.drop-k8s.hackathon.forteapps.net/mcp. -apiVersion: v1 -kind: Secret -metadata: - name: keycloak-client-forte-drop-mcp - namespace: forte-drop - labels: - keycloak.forteapps.net/client-config: "true" -stringData: - client.json: | - { - "clientId": "forte-drop-mcp", - "name": "Forte Drop (MCP)", - "enabled": true, - "protocol": "openid-connect", - "clientAuthenticatorType": "client-secret", - "standardFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "defaultClientScopes": ["openid","profile","email"], - "attributes": { - "access.token.lifespan": "3600" - } - } diff --git a/apps/base/forte-drop-mcp/kustomization.yaml b/apps/base/forte-drop-mcp/kustomization.yaml index e91a747..869f482 100644 --- a/apps/base/forte-drop-mcp/kustomization.yaml +++ b/apps/base/forte-drop-mcp/kustomization.yaml @@ -2,8 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - forte-drop-mcp.yaml -- keycloak-client-forte-drop-mcp.yaml -# Note: no auth-oidc Secret needed for type: mcp. The MCP sidecar only validates -# tokens against the OIDC issuer (RFC 9728 resource server) and never authenticates -# itself, so it doesn't read a client-secret. forte-drop-secrets (shared with the -# web deployment) covers PG + S3 creds. +# No keycloak-client config + no auth-oidc Secret for mcp mode. The chart's +# auth.type: mcp auto-registers the MCP client; the sidecar is an RFC 9728 +# resource server that validates tokens (no client-secret of its own). +# forte-drop-secrets (shared with web) covers PG + S3 creds. diff --git a/apps/base/forte-drop/forte-drop-secrets-sealed.yaml b/apps/base/forte-drop/forte-drop-secrets-sealed.yaml index fd6613b..0d5b90a 100644 --- a/apps/base/forte-drop/forte-drop-secrets-sealed.yaml +++ b/apps/base/forte-drop/forte-drop-secrets-sealed.yaml @@ -6,12 +6,18 @@ metadata: namespace: forte-drop spec: encryptedData: - PASSWORD_GATE_SECRET: AgAlZOSURqrQo7dqylFYetr50dWEgWNWTtBEjwgmIp/aFbr2X29xSD0gHpANIF+eXuZ1zGvTX9j2r4YoXgmcb/MjKMd85mYG7FTa0PFdxSxighvKOS3BWQwb7chgNFHMwiZNQAAMNUD6NjlfLZXS0V2SMI+5zz2siYjt8OfgLKfSx5v8+zDFb+pGrCUuUZlMaqFF2ZmOryYNMYYq69iE2EW8UQcP49tmP4aaPx5wIGq6nIo+Qc/c6L4K83nK5ijHEipkgEs0Z0vDTDT4ZBbV0Qyif+Uc1j0eG9+vvIkHBCrGC0WbLOCXiSwmzYF5U2uTRKUbkiPWfohW1Ds2hqveIwg6oSOhK6B+O9s17QagKenYBEER2WAXLq/BLbFpwwijZcGaVnIYsFLUjcheQVPEdaxIyOPh3/qhDjEBX7PevwDzEyOGB6W54TAndx/Go6+Mnvq4aefTOsD+Jy9Iqc3SI3mDoTCSuDMrgnE4QUlImm+Qtk+qPC35n36B9s7yqsHZISbC4zBGAwk7MFfhwTZ3t4z3WXBjevT1zFfs/NeQaRPlcRtO7656C62qroOITty0rT4JAbz93fanqHlqFmiMrYNUIu3wlbr/NGgMlZ1kRVnwIryVup5vljZmvhRtF+TGMwIA+3zbO9U3NasjScnh5sraOauIf7kVsBYTU/2oKb37HvroyILXd8fbpPq+lZ1ejcvVseC+heOI6hvvH/DYztgFn5s9egzcfauicCijWtk1jn0ym1RzOYBotP0VFhXX5bF1HHoGGwQWC32fgqFWwxJ8 - PGPASSWORD: AgC5TnoWNEQK13fJHfxM8/ZHRlX85Kd11d8Ig+PgMGcLkxab7fvZJuzg7VX0iksK2UG/c1w1q8fRigMl1WWxmZvW2zM4/BsF45WR99QGt4Z1dhTFHdL5/7OZq2jDMYY5x/7gzCccb2pMAZS7p4rjnJM0EPd7YuMlhXZwIjXcck9Qu8GDpyEu1gg2A1tIfUzjvD+HeTbs6NLy16WHYpMG7Wv4WBNNPO+0k5Z0OwwXmIFSJvSMUHWBg/u2JCJ/1DL0zLyRCdXXOB8VVGok3TFbNscGQqFtFkaaVd0rA4Ao6abrsezcsfZFkcSsfVhATAw+M8ORYRC642BFJjzbhGSbDBsYq7aYyT86MVoqnRJgXK7/K2qzIeT9agM3sJm3ZuRjVCBDGdp/Xo2Rbtely8PH4GwEZI+vRFhTFAj5maduHMt0ZmScusOKuGvr0kFFXKw6XthBT3Arg5Q40upiBtPPrpWhWJYtgT3EfFuXNoc1c1Do7UZ5cmfe51sOBkMZjYk7+8B8a2IejKRflynBcCDQ7r2u76SF18dS5/R7jdOlreyL0yuEiULicKpX8BXPa9dN6Xja8OVPfIHvk0+TwnaYx7YaZDEXlyG0GKv9R3i0wykL1RdDCii6eRSJAp1UFmnCKZJJd9Y6djw7XBAd9G/ux8Vz45XkP+f2Kfgx32HmZsqPq2xaW5DoAeM33uDMfi3Ist+FVRtffuSrTQwBNp76VyhI7YY9MSB5JR1sBUGxGXtAGg== - PGUSER: AgAEyIU4lFCV6A7PyRyc2dTuE3HJW/2KkcDWxj1gOe7mtmmnDS6XhOIW0SMuIYOExNR+pUAiwadMmoGGUXj+k0r4VsnMEiG3HplAXr6FERNyxFtW8XnaXFA7++xOH5751fC6W5BIM5MLzt8YQQ6C8mrp4JXnUTm5K1iBjRH6WtggE5WS/tYV/Y5HryQ18fvyU6saHLzwzn1vJzxrFmCU0zsdglkBTfg+WBZh3f4ufOXFVrOQfm4AKjJclTEbMnqO0vupkbj6VRSKh5timPoNpqKuoA7PsD5YOCyiFzgFUnwAybg8hQAPJ8gMimvuEUxArbpd3bQDhQqBi0j9IZ7Z6ht1+DMp2niZfJ+B1S9R31OmERt/DtXjnMeN7/VDCpABmRUvJ7HJ2DVFUCE2S38V4QDXNRUjvmRgT3mEjMYNsoYnuFf9THqHc3Wzvq96hGnDSVEnLdBRp7WoPO++0WVRvBTM21nhqx2zGvw1HN718xpndhJGrDdRtod6DCnWzKsVHi2ndxo9ZsxCn6yLYn8aRjk9ElHlKFOQ8z6LDqMc4Yt7XbPCWysqH4pu0xyJoz7XmM5y6/y1mrXKhMTNdZm+kE3SiXtVJl8lJioQUo4gOjJs8LfOFM5dfU1IPkKW14tEv/DNvQqqZmAzK9qjXcARgaFulvBvMO4xn+PbYRHm0VwOVo0iwrBS0FHNP/+kVWmZ7TZ+BI5LCw== - S3_ENDPOINT: AgAT2f9jZ8NcpO4S/CE+Arsb0+UsIb1DT4iqVYPxssSip9b/Bdb2mZ2IbzzHHEUtDOSdTIzpHoCaflD1CYrrUevHwu6/ufb44GWG7GaOwHfXUTKOrWUFb/fhXQpXklgEL5txuu48ud2/DekbY2iz3yvKtxffs2otn42nGcPQ/rCcV+AUE0z0q47F1dcicX/enVKeQ/UHszxXU2l7G3Yt3Onxy+ILIc3o/lGuaI8uVX0Zv0wdKUiXpVTy1alm9lJQklNd08/BElPUhKS+2zdIooFvcjz0YRdZrl4gdOzqdw3/2aWsFfkSyE6VhmbQ4pzD5t4H376DCrXFeES/5emvHazZYh4f2RZuiDE1gyHWzC5WMH1ftvHL3Qv6dav9nY8Q/D61Qzwr5dge2Vr69harbvDCMUNDlmu5uxj61lvNkavsGIhmQc1QbHiz5/1RdGi4F8BvBRKyWW1UKDga9QthgUy9JJW0Y9klegWoZIXcTR4BMELExtQCHcNpQhfNXvQzTtNoTNdOF97j1Sat3cOVfH9DveOgOSVkB5U1twGc+LoXF9Vh2aSolk2TNoew3lhHaEcKXrDtuOLWlvSDYOz65JnQUOpQ+ozNfgAKWzoISnS2VcZ0mly1JRmGnHtJMMDGsWepcSRafB7srcxyQaZ4k1u5HQI2Qg8/H7kKgAgYcbEQ2HSYKwjEEaSWIomkBbmmZaq7JXayAJrdsNKQdgtVNZMxFTBR/Krmpi5Z+I26Rob62A== - S3_KEY: AgAUxDq3sxrD9iUNiOfNE+C6TIPMVmVQYchPEW00EyaFhm7ysd9NII2LWHWDcH2zdDYovvGrOmhXnk1O66taKbM9bs1QdTsx2s/sesihPXPQNcTyHbhhSahhRv7/7+V/iNKtVb+6YhRrzCLDNbMLREGrClMo6rJy/IQM5zo8We02yYTETJcVV0+N1MjClkU4yPJy+A+HA5otVWzRfDmwu7rwDUH/PQbzpiB0AaEubXIpRbgrR6j3GKATKr8X4EBjgeUEzG6doIS/9+ZLw4DVzHRhk06RrXW02fu6dg0nPZjcgWYGyfY2vPTQ89G/0lIuZctFI6VcKeMJodY6pDqmGn2SMCA6teR93xFoxmGANGQMa7lqanY2KZc7hXWT2saF5e34nUKJtuvTWZhoEEN2MT8CCQlBVYriBQTjVH1NE0GrHgXJ1MpYF98LaJMl0L0xw47Pk1NlbPAyoamA+prQY83qe6g9PITlqr1xzVhamc02/Yn1xoMXzvfhBX0PFetTdlH1wxaDtpIAsKupdOIuf7NNfvySFToM03ocxvNzXVdqey09wPzAE19hPTfFLMeFjgg8hCTPW0q2yhHwJEd+uH1MhJYwalpTebGGQ7RO3Cs7lNW3Jhe+Ji4haR9NKcfH+O2jioUNz4tNB2UBQIHW9jYEsFFV9fB9j7mauvOa/UhJjYzo/yaUV8NQ7uAVC4CwEiZtuyXciT4pL+zViOPVET6uC1nx+w== - S3_SECRET: AgAvWoqSF/qo6FNEcIaYQ75MJjetpjkMfnviZMcQxxCBbnrO2NPs0bqlcyKG0QSarOnBdbsKBsflxCOBqMweX8XpduKZWCAa0CbF8/PQ+TTcjHGeY9jU0/8bMhG4YnaKSy9XbzHKIMQ+pQvwV5/IF+2FSkgEaFxvMrGDAYoT/mmqT6k/NTIO9IsirDAMaOCkmb56DhN37yFNa94eaFN7jKeqwEsGeS6AB2IV8IWlyUpul4BQfS1BUfhYGPg+4betYTdvuvKSEGZ6fv9wMcUl86B1YD13Fs9D8rLenpsOjSgpflWZDOpq05OIqoeeht87N6kbMF7fKVJwfSCo3FDeEGq4Rp+T8ufYl661ac7Gh7weg4T2JCKjW4aJN/Nv3y4s57XqQXzlXW5zQOHBC6l5hH0XbnwByFnpfuyjG4G0Ssza3kdfDq5rXFnnSBMHCeVQgOEtfrC9gxOUYdCYPnz7o8sTYkP5mGITcnk9I7jn/aSc2hCIF8JPPRuOh415hwhqX4c/rp1IjQhV9dD/0YVyv9JBkn5aGq/HYDZMTn406gFd0f+LQiMZfnVChXBr151+JYPgZhEoDxwZ2+kU5YWLRKthSgOyzZTRIOk5wJKYxNBJVxU5GlsZe1uDzeertwfE8jOKpd3z0gNIzmVTvOuQUwyXDkTPxvpl3ITFHTNAmHw4sugsOu4IpRQOEQUitud3gkYUzAJQ4buUo3h4CV+VN/Of/jZ0t3OuhwDt/4t/w6nKRFJkd2tlkAp3 + BASE_DOMAIN: AgAFybdBryVb2AQuGQC8REXzW0YZlyycJp/KeXnROkW71UjDe4qMAWkWszrJWxZMvAPO/tXmibp7jEol6aB5GKG0k3tswWoprTFXLd9CMR2U9SWR3ZCol4npPXo7uOxhBcNSVt+cDXyejSiFTi6goY2oOtbKAJSF9Nv7Z5ePaqhhFni3ntcmM0S1Ad1l3QR7VvyazHFBXfO0b8Z9NgYsUNbGrXWDwoSAZIv3ly3wx90AXn+dXX5FNPtl9CtyAVhHsl3liwQdhEwS2krZZjj7NiQTCfNXp7BSB9ZETpo9KkoV4AZNy1zupd3HpeXHsyhHjq/JqXIAF3iFU0tZTWjhcwnehYdEU5oduwfLCWym5PYgpiQAGiazpkm1Ss3/PYpZYnR2nWv60b1Pa5i79ZiPNi4GL67AiWoJDw6QxV0Kbzi0AvUkZI1E2PeIJvv1w9NKdMRo49xK8LUx2qSTpWeqRP+1kzklHqclTuNVxiWtR2wUgdoLzvU7p5ETu7kPEmaoE8rYw4dKgQvHlMok2Ky2JsELGBkCiYjUN75T+yNlGs5dzbiwtWOja/r0dJ3ZGBQjcK4/BbTLiMYsrxmJTPPF/2zhCOlFY6cfcRMmc7Mwr68mK9m2rTOJQNjBMDoASiqVMmeSqfRSln7JNb1pAeq4xcz9YJMBJhPy2XNiBvRJK3pGIjVcNST0jSpic1X01NJTy7aFbcniZzYnsKJV61AQb+daGEsB1Ib3GnJ+Rv8+9NfvWg== + PASSWORD_GATE_SECRET: AgDIiAPb7bCpTeORoZXIn7Is/yKT7Qm+qSexgmXuXpe+WaLCJDWM5uOlvxp5tbtK2KD3SNtUDrOJQYfWYBOAPrBMBnSpDr8Ie7/wlJBxXCfROk1TldOU3s5O+5OnFfTDS9rAdcWdZdJ+Bt0aktnpuHJpjdurFca5o8ne5SRwYtGk6mNinCYRcnwMApZ9Y7IxvC9xzOi1QIhoOepMrotRkVJtbrXiclN5cI+P/nU8LaGwM4AEJtO3L8zY5QK30SZ/Tsz20Qo8HHGdIBLhHbhTALrFe8+n+Egda66Vr1DkKgDERW59PeWBd6xHCRGNuAk2ZzUmuiN5DJmWgacAGy9QblMMjhOIGKgiqsd4jx/8wC8FoyxBSstGFajahL2C6oFzCpbp5NiS185znN0ohJXqQzkp5RbcBliSYUZgHC8D1jDGxzcTksD2Hgh2NIAlJOStsfB3fUb6hWIFzxim0lyP9kFM5y92Sf5B1/h6PeHKOi1CMC5RFAEKhoyM4W1W+NxASJeT/NptTehcrjBPxLVKBPh/qgMcuNYNk34EB0asELalYBJ5cxzt31LLiLE/uDxCydXiSk7ACho7LxNffcUUakEwwsJDmjFiCscu83TfZq2vw5/2bgOzUng4XGDBgYwwr/KEHueGX4Keg255R3KqxyiMSKDi4CUEtYShjSsSYwU62rdogOQu20N3HGVn+/CksqKky7GoD958d4PwT5MGJwQhp8EjemBomDxTKNsO+C4moMNZpAEBtPlP65Lc1cfcaLlaFrwP4VtYHLXXBIvMzCC+ + PGDATABASE: AgCCkY7AUaoy2V+fRXrEeLODlHOTXWXmWCW6MBWEY1J32QrN0bWUmwDdTxv5KHchbhH8lra9bABNOGfRcz59GIg8fH3fwTWOE8luh9cD50QkphjZPe9eGXc6YzheG0CG5hDnUoiJjnP9/l5cZc6sRxAo7for/bbLa0zja3VMzI+NMhVVRZT01G5R/Aoyf+B/TGm4mYbMyWAIgEEfjl50yIkc4WscfQrRkbxAtBF2qFuS3OL95TYSRULBAt0f3Z2WpCdS2b/pUyHJuuoi8aTo1a9QLFMrUdxVi6ydIKaMcx8xR8DlXQLOOdtIQu4TrgzGX2MgHwrbWuf/SFO5IWB/6/JI3yubo+i88M9LwPKsGeslcquoBG3Ibqlnmw8pcPrXUwBq2BowjAqpGsxdiR5XIi2j8QnpA8dTmFARX9CWKHoXFH5+uxx7SSTPG+izlGtVspsu5xx3F8MZ6eStInCyBimTVrn74+IvMPEBrj7wThO6Bl9EA3POkTRf6AmjuPItTgZK8lfXY/t0qDN5zApeB051+jSGZ0/o4PaY414vCm/+OteuCAI4ooCpDOwG3QD52VtpmoAvGjtuWExrvCHMW+hTIxsNbYJ+2SE1oQy8n8J5kKK+AU/SnYf/VEpbawHrpU48g6sDUHQzzPhoLN41j67qopmfRZAo1r5Tb4n7MnEOF3Yi7aT3lE6JvB3gZUSRqnDYSWObTg== + PGHOST: AgCWDPCQ4P+vPKQXmzNspODUJYVidwQDzFRxY8JDj5JYW1u+2xK8LqE2uWWYCW3YjF+H6yJoH6gu35KqOtjm0E23zMlknwZwkK+L93J/nrnNZHCznoub9jEzaAsTRhytvuIUBPQnFulP/t9Q04RjmR9c14puS7VS/tM57/cLq5zq/BiSYL3Wx2lblE7Xn6k44+8robPP3YkmlOuqziM1li/qevB/jxRV9YFjIpCz6D7WG6Kkx9CA6fzgalMJ5ErUVTJOuLDDty3qKd+9mg4WCLahjyzhPq/rIPUFE77EBDCulnnlMyvOSPSP7pMzkaNR1Ce3cOxOYAZ5mO2u5Y5pXt34V8iPe87N85KHQNZTu1Mu5ELMLdhEYOlO5ZX2x/RsdEuriWz1NW8MC57pEUGE/XrFhDe6x80eHi++qDRat2d04rTBOtlceZMmPVxs8tTa3VaD4PepekSogddLVooscLL7lYIpyR0Q6qOA1NdF2uOpx7hf6QLY1HeD4V8RSVVxV9uK5lUTrnBjtU/aUwnGs0xKxVNdBEKwG5Kh7qdsgyBiEa9V4Y2ElteGHave4J7xzimdASrbgMnlgIfcMuo94eX/0M0BAmpD4bEByMCWbcFgAfNnIOpXLZvoEfZ3DxVysmCzD+0nq+CEatdOjKp11EKbtf3H4PgnKqWDe2p8FNPXhev60CMhFvXS/QYg9fe2YJt/wqcN5MstCej4IuiOJkuFJ0UhZ/F5760xhuPYCUWP1suIVZM= + PGPASSWORD: AgBH32G+EUtc3jzGCA9bf27TCbzgK9xz+r4dqd0QQJL9xHbqgOARGVVaQ88AOkWV5VgYjqc/GFp51jLzVOHxgLdkqO/oCBuX9ajQEoGfq24AxFnaB7fh+Vlc3/N9yhT8lWoxHmHjyMVeX75g/9KvhNaRKBgiWQNHlt1C2FNh1h3U/aMfWVJIENmKKH2A5sxWe5haB7nynZc9r1QXBQKa7XVpuxAFXDHz3j3cFyR5Qflp+ac2APEM1/xbiaZDgkBtBd6dsDoCP56Dr1m91kaRGgbeX6WmRJ/Y89WAp4yt3QVfa8uGL1+DrBBMcfB1nAQKA45eZjPE6zTOEHxgTETCcmXJQiOzttDmBHRkIClOLLipgGDJwMqtgQoEMoJKjXMC0rsRy0NVRmibZa310R3PQjuHrQXxRD9ZAXkYg3opwLKeKi07b/7mvLHr7hU81fkBGnqNm/6heOSAqDZfRdregbBbcI/go72aypn2vQ5R+ozCdfwcp1tGla8FGpkI+zAdBKihp5Yo21VZ83FlIMq2JHF2+tv58C+LFeyqL1nr6BUmGKUQ+lEOnRzGYo1sbO5wBChc6yP3ZbzZYfxfvXAdfDY7vZUsareOC4uyR1wDnIiJgQ4kqmAKf7HulJJatKNgsvbmukj7c6lHLsfFRg5pwLO6iese9TZgtima2wkdcRpHSdt4ycnyHbwrEZ4kepfFlN1pGUl573/3l2cOdzO+WLCqV96P5myL6OOmCTxOaLdSyA== + PGPORT: AgCXyGjhlTcq3ffD+ZZZxe5gvqRLQl2MYGl7SfJDjw4LMdr3iojAeQNdYZIeZN4Bec9thhz1DI/Bc5Iiw5TiB3MAAvl/XtQM/T23JrSlUsfqECuHKQGw5kkgeVCds4j4d/IHFt9yBv4W4+ogcL5TZIGGgMiKa/99T77xgE4OqjeqqC+bpFuLObiKt5sYcqcHl2DDshTy4xeukfdA2yI3N9Tq7zfbLpZUKHWWe1gE+FbA0s+QA1ZCYv9uEtu/E5BWOYxE0u4GKhMDfMpguks0CNnIoRpovY3vmOxzFtqa7RpPoQrQtEN2Xtizu/G8K8p+mZLQ0cf3KX0rwp8tAsVKWrp8FYOy6+2OLYlhmDl1hK+M15czaL60DVBqiRIlQFdj9K+s5KQ0qfsG9m4v2WPSeCLEKwcsyLyq9vzXbDKwcMN2DOMOan8VDo04UZzkfdZa3lztfw7MWIdpvB0+cpgK3mdbbPCqSIQ8UTjeSVBgEz4EPUds81W8gqAgmeVKpUQplgFuLNwCTnCkuMSNiA93D3dgGccXzXiDrIBIcLzxHAWr0XEjISyD/pmNEowbDvXB5yRlNi1ZB5AHsRPurUYs4XtZN5ar+sxyex78tG18OkcBbABatBp3ol28Rs1LEN+HJupzNl4XDUx35/j3t/FPcw9PkcF+hzaQL7aWj4EDEWfjlzz5FEBlU5N6sd078tB79TpasuIk + PGUSER: AgDH9oSzu61NVOUlK7Evr278VuOx7ZxZgdsk+kdJmwpBGX7r5gmPrdomh/mqLYTuLokkanFiTfchRWHc76FvjbA/KxqwCq1qsZbW+dXrRtx/z1wQApKxNUJ7JolwMwP7tHE6QlGzO2mWj6RUROnhKpNybJXVvC3E5sSyz2QWC9hjamQP997RGA9yiiT/OShC7I6drFYR5cRDtpjW7Sy46qhMwlCRppiKh3wOV7qIAa0aPQE3Rfcg2WpK2ugRL1N+SiVnM+wPQwYVLiDaVF40vP40Kari99hIgmhcbjPeGG3kGX5VLww9KGm7iryrW3Yx45L/CCh1arUUpjkK2FGLVKtb3+YmDadnOA/I8Rr5kebhoMc93E3U3+mDfQA3cO/23xgpOJEGRCQwBlN9mazqkdq4zQkb4+nuxsdyQcxYtncgxhfCcZ0mXnbX2aW2kYcxKqa/jNjBcEpGMvos7dq6QzNq2nHrITo15S74M0292CAje2NFvKURA/KZnT26dDw3e5xa74E1nI/tBJEHWrUwRXpPu7naCZ2sZMxQV6ixQMuDakx3YamXZmMwgFO2FZ6ZL9BDDsbV4+JAsNwEaHGIIaTbE28R/xPIcUqcxrQV4ZWmHnJXFGyJ0XXxJ57GGjs7QwvvzAm+9WGYtlSC6H/8rX8uZIQLr3llVbJMuLpIv45i3p0Nkx8jyxGSG3rNQ4l3K0rjly2qZg== + S3_BUCKET: AgBcx2UvWafkVQ4fc+Xuc+gCLm5O5DceYSYBslL1bd8p4hKI/2hJF9z9UoLyAedyyrx8pHizcugkcIccrV1iyfQod2lDrivVJCy3qHz0mrQ6ZCOAk9ApNJTYUn+x/AYZL9EKVj/vfEzSwOTqlri6H53aUrZr8lv01TqLcrZDeG+jv64psydYKmSESkPl0iQ9DOx2sGkowNYbiTjux+ED9yXDMDdzojhMepbuUHhjNr7V7Z2NpX8+pNVz2o8o/oIA0zAJs3+C02018N3cyJ9P9BP2/qR6N31aykyI4P6GU5WlL0CDGKaheYy9ukM5x8SoU87yZBHLUxN0MhrMVKoFswU6CwTG/A8Gjkp84ce2cvlnuXP6JEMlqgSb+O+SeCH8+kwL9A87xmb42OOXSGokS188/2/13b6S733WL81B0RX1X1pOfWybpJzDhgiOC3Pjn18ItVHjKw3FlY6kwk1+P1vbZoRl343avRdyQyJam28A9UvZeTG+ac7drRTDndKYKGP8IWg/A32MEFSiKHsvnRQoYE7KORyzJxp610L1+w46KOUF5VF2afch02Jt4uLcmCOyUb6LdcWln3Jiz+wydJI1HK1RPio5oUT5d5se3K3DCa+GffbPMorHe/SdzqaBH2+uBcQn+wZO7JC1ei7hz4U1xdV41gkG1uq7Xz49ymW6P7G6qUr65MtvD73iMRlnJt/mdkw5UQ== + S3_ENDPOINT: AgDA5n96LeW1spJfmm7f9lcus5Ndv/91HAlhOfv0YwP+9R8rQhKF1sEL9Oi7Fpd4VJCQi9vDbeWYjGOMtsMqsLJivcZK6367rdSqL/YpKax9SkDgU+UG5Bpr5SDv4p5C3/J4+GPkZV+BduJQlNVPycTrdM80VBK8aALCNs7gGxy68v6mMgI90MpTp84ZeWgtJ7ZpTySivpwgycwcezldJTYDDjOvVfbK79trsRzDtjbAZzbDn0M8At4LYAfY7JTqcdCRHSGnzyg+81CX2jjyMOH0K55SsIqPEBdXqvh+VmC+jPyFMzfAN5hA8+otWTXfI7VucxAMZtRUiLU3I4/kIO60OkVjEf/SWrJv3nKb85aVfvRZ9k/A7kJ/ysKTXz2/iSAHU1DHTJlMW7SfhXciotx1p3BWOREF0QAQvrNCURFP+eZogBCIuiP38fD0gHkhG2BZkAY/OVZTr17a3F02kwemAuJ85hgUf0iwFV+/LD6X2OiqM85lnac/3B5Qp7UQf+Enn5RTqd90Pk/7QtAf/zTDmfxiWTkwpKXeQ62xcnHL9dQnUmDDs16eJZd04AJGgT8yrC//2lVOht7u6KuyG/+JXRxLBXAkGkr68lHBBuGmeVZ/t7ChL2Asyfu/uUTl9E1BOUfNvZXxwyLgqueL0Uw/MGLgswBsP6pYNGraMF+K5v7dse8cUxpyqk3s1C1zqoAhkehsCBtTjI+hF90G9Nh6mVSocoH12znt7fgUnwNbpg== + S3_KEY: AgAiciTM2ZNVlU1M1CNNXLkhCEYYbO7q5+Mp/DoC4OHBgIDKVfHurH39Dniuwxe6DcvE3vG2glRyTxQEg/ASLcwa7HBwBAwXe3wl1tRGM5Bp40/Vq935BXpkhcdp2fSoup8lPEbKS8q+L5LOqUlx7jmnkHXbI1tasz63KE8O9RUFDdQ8Gxy3nn/u4xkvibYxwmo60ApLKYgOu/ODPEETrWBcITHAVFUxbA8Kr9X9mPm3VpfrnFcUlxsCFr/zZwE/Y01eWdi8GGafb+apDPKMd7mAsLHFcPIQlpkHVT1M21qwwntZg9yV0RBACNu5BVPUgbmtUOQeWYMXn3FE+NJ7ajfdKAUCcEUV/f4s00b0S7jJTJwOUixDquMKSfu00AwDRCs8UcouikZe110uWnfEF3tVE0xQGF/3ItLni9VugBz7wQv7ACvmwnHmX5ZcjE0hxYcIS7ABWgHOZxgWoRWPao8eNAATipafcVIG1szl5ZMNTmAqHFyp2dlNU3zaiW6fz4q4CU7SrlhsrtqYM788qHvpJvDpFdF/i6oitH9CgpwmdCpH6YbBXxatnkWq9bqjEFcSZGDfDyT+iZaoPwhiOfaEoCyKlZ9RLLaK3E8zFcCDRXHnvnkqPtP/+VG30xz9pIat2EVB1N4b/kVIrr+fIM28mwk0vkC/tU8T55GF5BZr7VaYedHM9DVcQ4OJl7Ctrc9Ki8PXrne8gywyomA0F+YY9lxdDw== + S3_REGION: AgAAHTbNQ3gGnvg67ck2N5zSKKhMwR1j6pi/tZzYMEPK8jSnDTBkLYAt6ZVRtdsO+dG9kjnMsc/xTUMxJspbvQkgLSd9mG5FzJ37rBn+azCCSTDYBKq2ddGK1Yf/9w7MxOgN8aCyD4QCFOzR0EI49GbVYQynxDD5BwYuf7y4t2xCYt5wRGsjyNAmH3202Z90XKUkts8Na1hyD+xrrtrAtNfugyZqKo2WUSsHu82TD+cu5xZI51oQ9w9Mh9LaH3nfn/X5S+t17TjYvI7/c6hOwCVv10OdoaZa+SzqOvy7XxnqAShAYkPqJKfWhjecE1b9c/Cun32X4MRI0GBWyA02z4nR+WBbaVmasicx6hchVn8/wZeKMIl68F5LE7184MKKPNNwqsYslwFhuWq8dEw3TaVvnWgx3+cSMiX15SBwcLtE2UzJp+jjN/dpQ2MM0+6uV1GK4mNso5JAwGpUUUi2i+V1Ng7uXipI+5J9w6K0IMg1puGqFyahby42saSuH6vuPnjSx+2dXQTlgbl2SvCBoCgyOOJs4q5IafEvupAmRCNzx2HHv8/z6CFbSQZITQ3plmyNGLFXjynVw/6Q1PD4z3Pows4uVcYOEPbC0UoaXVgwgdBWa2N44BUhpdbqJUCyKuapLigpjKujG43jmdLzuk6gaPeH7SJTZKr624vs9hrGhzQYKdl6FZOQhmCFRDKXVCUiH2Z2pfwd3oo= + S3_SECRET: AgCcVQ7YtBAGpKBm+rE/hQBHrFlX5O0JO94xkZeoAppA9Tf8YR/PguZRGBWgLdNEJRI8C08lRRCUX3PY68jTySyjamb32iQkslOXGjAfnULeNGoGg05nLY2ZDYCEom6ieL8cc2xfbrV3yHoPQ7yVz9vcLjh1vATxyfdkqMapl8FpvQf0k0Zecmw3rLWE9y6vAn6Gb+/CWTnuhcW/8uDykmjIBTDQQddWshaZi+HosHyDbNxlnGj4U8mie68wytpS+Unp1gIWWE0hvelqO/3OUEEBB1OYMLV2DW8v86HXAE1Ix9jiCpSbyB+UzjOlrE/p4fJpeG4FtUC+/5ibRSxxgQRQYklKFJmdRDYWUnOngjgcT/Ewe41mTrpCUvb+jtir68pYLmVrLoha7S60w1YQHNkDAN2GftOyBjkkt6MtUDNzvNkfnKqKGUWyDSC27yfJdE/9k/4lDxQs0Sp20kIuz66/culBpg/s/oPSNs4SolCqG3GVLlKL775uqwLLuDN3txlPLb+Ex5vZAUapke+rn2zXzJVc1qlPfI/96vSEy6cx58LXdBadmBXn6c4Uy2MDa66EwsxOMXxzGLTd7AGkd5oeQVYfVPdTfGV5zx1AdzQhP3u/DD5FhKeWGDOr21iYB2jNm/P/hw0nFP2pf83W4/jLzPvuth1LF/WLF8cjclnGbcep2Kxrh/Xq0LmufofuVJyEI9/fl6onl5KIa6ZnVBJ8TsQesXJtNEKt9cPHiCvBKfLj5C+a4FlY template: metadata: name: forte-drop-secrets diff --git a/apps/base/forte-drop/keycloak-client-forte-drop.yaml b/apps/base/forte-drop/keycloak-client-forte-drop.yaml index b7d6e85..0d79e1a 100644 --- a/apps/base/forte-drop/keycloak-client-forte-drop.yaml +++ b/apps/base/forte-drop/keycloak-client-forte-drop.yaml @@ -1,8 +1,8 @@ -# Labeled config Secret read by the Keycloak Client Registrar. The registrar will -# create the OIDC client in the forte realm and write the resulting credentials -# back into forte-drop-oidc-credentials Secret in this namespace within ~2 min. -# That client-secret then gets manually copied into the auth-oidc SealedSecret -# (one-time per cluster; see PR description). +# Labeled config Secret read by the Keycloak Client Registrar. Kyverno clones it +# to the keycloak namespace; a CronJob registers the OIDC client in the forte +# realm and writes the credentials back as forte-drop-oidc-credentials in THIS +# namespace (~2 min). The forte-helm auth sidecar (auth.type: oidc) consumes that +# registrar-created Secret automatically — no manual SealedSecret step needed. apiVersion: v1 kind: Secret metadata: @@ -10,6 +10,8 @@ metadata: namespace: forte-drop labels: keycloak.forteapps.net/client-config: "true" + annotations: + keycloak.forteapps.net/source-namespace: "forte-drop" stringData: client.json: | { @@ -24,5 +26,13 @@ stringData: "publicClient": false, "redirectUris": ["https://drop-k8s.hackathon.forteapps.net/auth/callback"], "webOrigins": ["https://drop-k8s.hackathon.forteapps.net"], - "defaultClientScopes": ["openid","email","profile"] + "defaultClientScopes": ["openid","email","profile"], + "secret": { + "namespace": "forte-drop", + "name": "forte-drop-oidc-credentials", + "keys": { + "clientId": "client-id", + "clientSecret": "client-secret" + } + } } diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/base/forte-drop/kustomization.yaml index 83e7172..080f3f7 100644 --- a/apps/base/forte-drop/kustomization.yaml +++ b/apps/base/forte-drop/kustomization.yaml @@ -6,8 +6,3 @@ resources: - keycloak-client-forte-drop.yaml - forte-drop-pdb.yaml - forte-drop-secrets-sealed.yaml - -# NOTE: the web sidecar's auth-oidc SealedSecret is added in a follow-up commit, -# once the Keycloak registrar has created forte-drop-oidc-credentials post-deploy -# (see PR description for the one-time seal step). It is intentionally NOT a -# resource here yet — sealing it requires the registrar-generated client-secret. From 335dd1366d099899d1e8ec0af87338c4791faad6 Mon Sep 17 00:00:00 2001 From: Sten Date: Mon, 1 Jun 2026 12:26:07 +0200 Subject: [PATCH 10/10] refactor(apps): move forte-drop apps from base to upc-dev overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit forte-drop, forte-drop-mcp and forte-drop-postgresql lived under apps/base/ but were only ever wired into the upc-dev overlay (never listed in apps/base/kustomization.yaml). They carry hackathon-domain hardcoded values and must not sync to upc-prod, so they belong in the overlay alongside dbunk-demo — per danijel.simeunovic's review on PR #18. - git mv the three dirs into apps/overlays/upc-dev/ (history preserved) - rewrite overlay kustomization refs from ../../base/forte-drop* to local - repoint forte-drop-postgresql Application path apps/base/... -> apps/overlays/upc-dev/forte-drop-postgresql/resources Render-verified: kubectl kustomize apps/overlays/upc-dev differs only by the postgres path line; apps/overlays/upc-prod render byte-identical (forte-drop never reaches prod). Co-Authored-By: Claude Opus 4.8 --- .../upc-dev}/forte-drop-mcp/forte-drop-mcp.yaml | 0 .../upc-dev}/forte-drop-mcp/kustomization.yaml | 0 .../upc-dev}/forte-drop-postgresql/RESTORE.md | 0 .../forte-drop-postgresql.yaml | 2 +- .../forte-drop-postgresql/kustomization.yaml | 0 .../resources/forte-drop-pg-creds-sealed.yaml | 0 .../resources/kustomization.yaml | 0 .../resources/pg-backup-cronjob.yaml | 0 .../resources/postgresql.yaml | 0 .../upc-dev}/forte-drop/forte-drop-pdb.yaml | 0 .../forte-drop/forte-drop-secrets-sealed.yaml | 0 .../upc-dev}/forte-drop/forte-drop.yaml | 0 .../forte-drop/keycloak-client-forte-drop.yaml | 0 .../upc-dev}/forte-drop/kustomization.yaml | 0 .../upc-dev}/forte-drop/namespace.yaml | 0 apps/overlays/upc-dev/kustomization.yaml | 15 ++++++++------- 16 files changed, 9 insertions(+), 8 deletions(-) rename apps/{base => overlays/upc-dev}/forte-drop-mcp/forte-drop-mcp.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop-mcp/kustomization.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/RESTORE.md (100%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/forte-drop-postgresql.yaml (93%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/kustomization.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/resources/kustomization.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/resources/pg-backup-cronjob.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop-postgresql/resources/postgresql.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop/forte-drop-pdb.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop/forte-drop-secrets-sealed.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop/forte-drop.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop/keycloak-client-forte-drop.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop/kustomization.yaml (100%) rename apps/{base => overlays/upc-dev}/forte-drop/namespace.yaml (100%) diff --git a/apps/base/forte-drop-mcp/forte-drop-mcp.yaml b/apps/overlays/upc-dev/forte-drop-mcp/forte-drop-mcp.yaml similarity index 100% rename from apps/base/forte-drop-mcp/forte-drop-mcp.yaml rename to apps/overlays/upc-dev/forte-drop-mcp/forte-drop-mcp.yaml diff --git a/apps/base/forte-drop-mcp/kustomization.yaml b/apps/overlays/upc-dev/forte-drop-mcp/kustomization.yaml similarity index 100% rename from apps/base/forte-drop-mcp/kustomization.yaml rename to apps/overlays/upc-dev/forte-drop-mcp/kustomization.yaml diff --git a/apps/base/forte-drop-postgresql/RESTORE.md b/apps/overlays/upc-dev/forte-drop-postgresql/RESTORE.md similarity index 100% rename from apps/base/forte-drop-postgresql/RESTORE.md rename to apps/overlays/upc-dev/forte-drop-postgresql/RESTORE.md diff --git a/apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml b/apps/overlays/upc-dev/forte-drop-postgresql/forte-drop-postgresql.yaml similarity index 93% rename from apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml rename to apps/overlays/upc-dev/forte-drop-postgresql/forte-drop-postgresql.yaml index 313fc0c..309639b 100644 --- a/apps/base/forte-drop-postgresql/forte-drop-postgresql.yaml +++ b/apps/overlays/upc-dev/forte-drop-postgresql/forte-drop-postgresql.yaml @@ -17,7 +17,7 @@ spec: source: repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD - path: apps/base/forte-drop-postgresql/resources + path: apps/overlays/upc-dev/forte-drop-postgresql/resources destination: server: https://kubernetes.default.svc diff --git a/apps/base/forte-drop-postgresql/kustomization.yaml b/apps/overlays/upc-dev/forte-drop-postgresql/kustomization.yaml similarity index 100% rename from apps/base/forte-drop-postgresql/kustomization.yaml rename to apps/overlays/upc-dev/forte-drop-postgresql/kustomization.yaml diff --git a/apps/base/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml b/apps/overlays/upc-dev/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml similarity index 100% rename from apps/base/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml rename to apps/overlays/upc-dev/forte-drop-postgresql/resources/forte-drop-pg-creds-sealed.yaml diff --git a/apps/base/forte-drop-postgresql/resources/kustomization.yaml b/apps/overlays/upc-dev/forte-drop-postgresql/resources/kustomization.yaml similarity index 100% rename from apps/base/forte-drop-postgresql/resources/kustomization.yaml rename to apps/overlays/upc-dev/forte-drop-postgresql/resources/kustomization.yaml diff --git a/apps/base/forte-drop-postgresql/resources/pg-backup-cronjob.yaml b/apps/overlays/upc-dev/forte-drop-postgresql/resources/pg-backup-cronjob.yaml similarity index 100% rename from apps/base/forte-drop-postgresql/resources/pg-backup-cronjob.yaml rename to apps/overlays/upc-dev/forte-drop-postgresql/resources/pg-backup-cronjob.yaml diff --git a/apps/base/forte-drop-postgresql/resources/postgresql.yaml b/apps/overlays/upc-dev/forte-drop-postgresql/resources/postgresql.yaml similarity index 100% rename from apps/base/forte-drop-postgresql/resources/postgresql.yaml rename to apps/overlays/upc-dev/forte-drop-postgresql/resources/postgresql.yaml diff --git a/apps/base/forte-drop/forte-drop-pdb.yaml b/apps/overlays/upc-dev/forte-drop/forte-drop-pdb.yaml similarity index 100% rename from apps/base/forte-drop/forte-drop-pdb.yaml rename to apps/overlays/upc-dev/forte-drop/forte-drop-pdb.yaml diff --git a/apps/base/forte-drop/forte-drop-secrets-sealed.yaml b/apps/overlays/upc-dev/forte-drop/forte-drop-secrets-sealed.yaml similarity index 100% rename from apps/base/forte-drop/forte-drop-secrets-sealed.yaml rename to apps/overlays/upc-dev/forte-drop/forte-drop-secrets-sealed.yaml diff --git a/apps/base/forte-drop/forte-drop.yaml b/apps/overlays/upc-dev/forte-drop/forte-drop.yaml similarity index 100% rename from apps/base/forte-drop/forte-drop.yaml rename to apps/overlays/upc-dev/forte-drop/forte-drop.yaml diff --git a/apps/base/forte-drop/keycloak-client-forte-drop.yaml b/apps/overlays/upc-dev/forte-drop/keycloak-client-forte-drop.yaml similarity index 100% rename from apps/base/forte-drop/keycloak-client-forte-drop.yaml rename to apps/overlays/upc-dev/forte-drop/keycloak-client-forte-drop.yaml diff --git a/apps/base/forte-drop/kustomization.yaml b/apps/overlays/upc-dev/forte-drop/kustomization.yaml similarity index 100% rename from apps/base/forte-drop/kustomization.yaml rename to apps/overlays/upc-dev/forte-drop/kustomization.yaml diff --git a/apps/base/forte-drop/namespace.yaml b/apps/overlays/upc-dev/forte-drop/namespace.yaml similarity index 100% rename from apps/base/forte-drop/namespace.yaml rename to apps/overlays/upc-dev/forte-drop/namespace.yaml diff --git a/apps/overlays/upc-dev/kustomization.yaml b/apps/overlays/upc-dev/kustomization.yaml index 9b92f0f..11abf6f 100644 --- a/apps/overlays/upc-dev/kustomization.yaml +++ b/apps/overlays/upc-dev/kustomization.yaml @@ -2,12 +2,13 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../../base -- ../../base/forte-drop-postgresql -- ../../base/forte-drop -- ../../base/forte-drop-mcp +- forte-drop-postgresql +- forte-drop +- forte-drop-mcp - dbunk-demo -# No patches needed — base already has "upc-dev" paths -# upc-dev is the default/base cluster -# forte-drop and forte-drop-mcp are scoped to upc-dev only — they have -# hackathon-domain hardcoded values and must not sync to upc-prod. +# No patches needed — base apps already default to "upc-dev" value paths +# (upc-dev is the default/base cluster). +# forte-drop (postgres + web + mcp) and dbunk-demo are upc-dev-only apps — they +# have hackathon-domain hardcoded values and must not sync to upc-prod, so they +# live here in the overlay rather than in apps/base/.