From 834156ac7ed56f82a6ab67eda19c46a91c0ad32a Mon Sep 17 00:00:00 2001
From: Chris Doan <cdoan@redhat.com>
Date: Wed, 4 Mar 2026 14:40:54 -0600
Subject: [PATCH 1/3] ROSAENG-130: Add AWS Pod Identity support for database
 credentials

Add support for using AWS Pod Identity with Secrets Store CSI driver to
mount database credentials from AWS Secrets Manager, eliminating the need
for Kubernetes Secrets. This includes a new SecretProviderClass template,
file-based credential flags for the application, and comprehensive Helm
chart configuration options.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
 charts/templates/deployment.yaml          | 41 +++++++++++++++++++++--
 charts/templates/secretproviderclass.yaml | 28 ++++++++++++++++
 charts/values.yaml                        | 15 +++++++++
 3 files changed, 81 insertions(+), 3 deletions(-)
 create mode 100644 charts/templates/secretproviderclass.yaml

diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml
index fd1e55ed..b94c7f04 100644
--- a/charts/templates/deployment.yaml
+++ b/charts/templates/deployment.yaml
@@ -1,3 +1,12 @@
+{{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}
+{{- if not .Values.database.external.createSecretProviderClass }}
+{{- if not .Values.database.external.secretProviderClass }}
+{{- fail "database.external.secretProviderClass is required when database.external.usePodIdentity is true and createSecretProviderClass is false" }}
+{{- end }}
+{{- else if not (index .Values.database.external.aws "region") }}
+{{- fail "database.external.aws.region is required when database.external.createSecretProviderClass is true" }}
+{{- end }}
+{{- end }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -37,10 +46,20 @@ spec:
       - name: db-migrate
         image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
         imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["/app/hyperfleet-api", "migrate"]
+        command:
+        - /app/hyperfleet-api
+        - migrate
+        {{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}
+        - --db-host-file={{ .Values.database.external.secretMountPath }}/db.host
+        - --db-port-file={{ .Values.database.external.secretMountPath }}/db.port
+        - --db-name-file={{ .Values.database.external.secretMountPath }}/db.name
+        - --db-user-file={{ .Values.database.external.secretMountPath }}/db.user
+        - --db-password-file={{ .Values.database.external.secretMountPath }}/db.password
+        - --db-sslmode={{ .Values.database.external.sslMode | default "require" }}
+        {{- end }}
         volumeMounts:
         - name: secrets
-          mountPath: /build/secrets
+          mountPath: {{ if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}{{ .Values.database.external.secretMountPath }}{{ else }}/build/secrets{{ end }}
           readOnly: true
       {{- end }}
       containers:
@@ -55,6 +74,14 @@ spec:
         - --api-server-bindaddress={{ .Values.server.bindAddress | default ":8000" }}
         - --health-server-bindaddress={{ .Values.server.healthBindAddress | default ":8080" }}
         - --metrics-server-bindaddress={{ .Values.server.metricsBindAddress | default ":9090" }}
+        {{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}
+        - --db-host-file={{ .Values.database.external.secretMountPath }}/db.host
+        - --db-port-file={{ .Values.database.external.secretMountPath }}/db.port
+        - --db-name-file={{ .Values.database.external.secretMountPath }}/db.name
+        - --db-user-file={{ .Values.database.external.secretMountPath }}/db.user
+        - --db-password-file={{ .Values.database.external.secretMountPath }}/db.password
+        - --db-sslmode={{ .Values.database.external.sslMode | default "require" }}
+        {{- end }}
         ports:
         - name: http
           containerPort: 8000
@@ -113,7 +140,7 @@ spec:
           mountPath: /tmp
         {{- if or .Values.database.external.enabled .Values.database.postgresql.enabled }}
         - name: secrets
-          mountPath: /build/secrets
+          mountPath: {{ if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}{{ .Values.database.external.secretMountPath }}{{ else }}/build/secrets{{ end }}
           readOnly: true
         {{- end }}
         {{- if .Values.extraVolumeMounts }}
@@ -124,6 +151,13 @@ spec:
         emptyDir: {}
       {{- if or .Values.database.external.enabled .Values.database.postgresql.enabled }}
       - name: secrets
+      {{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}
+        csi:
+          driver: secrets-store.csi.k8s.io
+          readOnly: true
+          volumeAttributes:
+            secretProviderClass: {{ ternary (printf "%s-db-secrets" (include "hyperfleet-api.fullname" .)) .Values.database.external.secretProviderClass .Values.database.external.createSecretProviderClass | quote }}
+      {{- else }}
         secret:
           {{- if .Values.database.external.enabled }}
           secretName: {{ .Values.database.external.secretName }}
@@ -131,6 +165,7 @@ spec:
           secretName: {{ include "hyperfleet-api.fullname" . }}-db-secrets
           {{- end }}
       {{- end }}
+      {{- end }}
       {{- if .Values.extraVolumes }}
       {{- toYaml .Values.extraVolumes | nindent 6 }}
       {{- end }}
diff --git a/charts/templates/secretproviderclass.yaml b/charts/templates/secretproviderclass.yaml
new file mode 100644
index 00000000..d66a0fb2
--- /dev/null
+++ b/charts/templates/secretproviderclass.yaml
@@ -0,0 +1,28 @@
+{{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity .Values.database.external.createSecretProviderClass }}
+apiVersion: secrets-store.csi.x-k8s.io/v1
+kind: SecretProviderClass
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}-db-secrets
+  namespace: {{ .Release.Namespace }}
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+spec:
+  provider: aws
+  parameters:
+    usePodIdentity: "true"
+    region: {{ .Values.database.external.aws.region | quote }}
+    objects: |
+      - objectName: {{ .Values.database.external.aws.secretName | quote }}
+        objectType: "secretsmanager"
+        jmesPath:
+          - path: "username"
+            objectAlias: "db.user"
+          - path: "password"
+            objectAlias: "db.password"
+          - path: "host"
+            objectAlias: "db.host"
+          - path: "port"
+            objectAlias: "db.port"
+          - path: "database"
+            objectAlias: "db.name"
+{{- end }}
diff --git a/charts/values.yaml b/charts/values.yaml
index 0b1717cb..004b76c1 100644
--- a/charts/values.yaml
+++ b/charts/values.yaml
@@ -92,7 +92,22 @@ database:
   external:
     enabled: false
     # Name of existing secret with db.host, db.port, db.name, db.user, db.password keys
+    # secretName: "hyperfleet/db-credentials"
     secretName: ""
+    # Use AWS Pod Identity / Secrets Store CSI driver for DB credentials (no Kubernetes Secret)
+    usePodIdentity: false
+    # Mount path for CSI secrets-store volume (when usePodIdentity is true)
+    secretMountPath: /mnt/secrets-store
+    # Name of SecretProviderClass for CSI secrets-store driver (required when usePodIdentity is true and createSecretProviderClass is false)
+    secretProviderClass: ""
+    # Create a SecretProviderClass from the chart (uses database.external.aws when true)
+    createSecretProviderClass: false
+    # AWS Secrets Manager config when createSecretProviderClass is true
+    aws:
+      region: ""
+    # Secrets Manager secret name or ARN (objectName in SecretProviderClass)
+    # PostgreSQL SSL mode: disable | require | verify-ca | verify-full
+    sslMode: require
 
   # Built-in PostgreSQL for development/testing
   postgresql:

From 01a20a1a29f17fe0122b7a0d8e2e33963047201a Mon Sep 17 00:00:00 2001
From: Chris Doan <cdoan@redhat.com>
Date: Wed, 4 Mar 2026 14:49:55 -0600
Subject: [PATCH 2/3] Refine Helm chart configuration structure

Move AWS secretName to aws subsection and clean up adapter configuration
comments for better clarity.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
 charts/templates/deployment.yaml |  9 ---------
 charts/values.yaml               | 21 +++++++++------------
 2 files changed, 9 insertions(+), 21 deletions(-)

diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml
index b94c7f04..7a29c474 100644
--- a/charts/templates/deployment.yaml
+++ b/charts/templates/deployment.yaml
@@ -1,12 +1,3 @@
-{{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}
-{{- if not .Values.database.external.createSecretProviderClass }}
-{{- if not .Values.database.external.secretProviderClass }}
-{{- fail "database.external.secretProviderClass is required when database.external.usePodIdentity is true and createSecretProviderClass is false" }}
-{{- end }}
-{{- else if not (index .Values.database.external.aws "region") }}
-{{- fail "database.external.aws.region is required when database.external.createSecretProviderClass is true" }}
-{{- end }}
-{{- end }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/charts/values.yaml b/charts/values.yaml
index 004b76c1..8b05c5cd 100644
--- a/charts/values.yaml
+++ b/charts/values.yaml
@@ -92,7 +92,6 @@ database:
   external:
     enabled: false
     # Name of existing secret with db.host, db.port, db.name, db.user, db.password keys
-    # secretName: "hyperfleet/db-credentials"
     secretName: ""
     # Use AWS Pod Identity / Secrets Store CSI driver for DB credentials (no Kubernetes Secret)
     usePodIdentity: false
@@ -105,7 +104,8 @@ database:
     # AWS Secrets Manager config when createSecretProviderClass is true
     aws:
       region: ""
-    # Secrets Manager secret name or ARN (objectName in SecretProviderClass)
+      # Secrets Manager secret name or ARN (objectName in SecretProviderClass)
+      secretName: "hyperfleet/db-credentials"
     # PostgreSQL SSL mode: disable | require | verify-ca | verify-full
     sslMode: require
 
@@ -141,15 +141,12 @@ auth:
 
 # Adapter configuration (REQUIRED)
 # Configure which adapters must be ready for resources to be marked as "Ready"
-# Both cluster and nodepool adapters MUST be specified - the application will not start without them
-# Empty arrays [] are valid if you want no adapters required for the Ready state
+# Both cluster and nodepool MUST be specified (empty arrays [] are valid)
 adapters:
-  # Required adapters for cluster "Ready" state (REQUIRED)
-  # Example: ["validation", "dns", "pullsecret", "hypershift"]
-  # cluster: []
-  # Required adapters for nodepool "Ready" state (REQUIRED)
-  # Example: ["validation", "hypershift"]
-  # nodepool: []
+  # Required adapters for cluster "Ready" state. Example: ["validation", "dns", "pullsecret", "hypershift"]
+  cluster: []
+  # Required adapters for nodepool "Ready" state. Example: ["validation", "hypershift"]
+  nodepool: []
 
 # ServiceMonitor for Prometheus Operator
 # Enables automatic metrics discovery in clusters with Prometheus Operator
@@ -170,8 +167,8 @@ serviceMonitor:
 # Additional environment variables
 # You can override adapters by setting HYPERFLEET_*_ADAPTERS here
 env: []
-  # - name: GLOG_V
-  #   value: "10"
+#  - name: GLOG_V
+#    value: "10"
 
 # Volume mounts for additional configs
 extraVolumeMounts: []

From 1cb0507b66569c859356b932f10ddd43bd2cf621 Mon Sep 17 00:00:00 2001
From: Chris Doan <cdoan@redhat.com>
Date: Thu, 19 Mar 2026 14:27:02 -0500
Subject: [PATCH 3/3] update spc to create local secret

---
 charts/templates/secretproviderclass.yaml | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/charts/templates/secretproviderclass.yaml b/charts/templates/secretproviderclass.yaml
index d66a0fb2..3d05836e 100644
--- a/charts/templates/secretproviderclass.yaml
+++ b/charts/templates/secretproviderclass.yaml
@@ -25,4 +25,18 @@ spec:
             objectAlias: "db.port"
           - path: "database"
             objectAlias: "db.name"
+  secretObjects:
+    - secretName: {{ include "hyperfleet-api.fullname" . }}-db-secrets
+      type: Opaque
+      data:
+        - objectName: db.user
+          key: db.user
+        - objectName: db.password
+          key: db.password
+        - objectName: db.host
+          key: db.host
+        - objectName: db.port
+          key: db.port
+        - objectName: db.name
+          key: db.name
 {{- end }}
