diff --git a/helm/litellm-helm/templates/migrations-job.yaml b/helm/litellm-helm/templates/migrations-job.yaml index 5a873cbb965..1bcdb7c1bb5 100644 --- a/helm/litellm-helm/templates/migrations-job.yaml +++ b/helm/litellm-helm/templates/migrations-job.yaml @@ -75,8 +75,28 @@ spec: - name: DATABASE_URL value: {{ .Values.db.url | quote }} {{- else if .Values.db.deployStandalone }} - - name: DATABASE_URL - value: postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ .Release.Name }}-postgresql/{{ .Values.postgresql.auth.database }} + {{- /* + Hand the credentials over as separate variables and let the entrypoint + assemble DATABASE_URL, exactly as the proxy Deployment does. Building the + URL here instead interpolated the password raw, so any of : / ? # [ ] @ + + or a space in postgresql.auth.password silently reshaped the URL and the + Job failed to connect. Sourcing the two secret values also keeps the + password out of the rendered Job and the Helm release secret. + */}} + - name: DATABASE_USERNAME + valueFrom: + secretKeyRef: + name: {{ include "litellm.fullname" . }}-dbcredentials + key: username + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "litellm.fullname" . }}-dbcredentials + key: password + - name: DATABASE_HOST + value: {{ .Release.Name }}-postgresql + - name: DATABASE_NAME + value: {{ .Values.postgresql.auth.database }} {{- end }} {{- if .Values.envVars }} {{- range $key, $val := .Values.envVars }} diff --git a/helm/litellm-helm/tests/migrations-job_tests.yaml b/helm/litellm-helm/tests/migrations-job_tests.yaml index dd4276ac60f..8a454440744 100644 --- a/helm/litellm-helm/tests/migrations-job_tests.yaml +++ b/helm/litellm-helm/tests/migrations-job_tests.yaml @@ -130,6 +130,88 @@ tests: name: DISABLE_SCHEMA_UPDATE value: "false" + - it: should hand deployStandalone credentials over as separate env vars, not a built URL + template: migrations-job.yaml + set: + migrationJob: + enabled: true + db: + deployStandalone: true + useExisting: false + asserts: + # Assembling the URL in the template interpolated the password raw, so a + # password holding any of : / ? # [ ] @ + reshaped the URL and the Job + # connected to the wrong host, or to none. The entrypoint builds the URL + # from these four instead, percent-encoding as it goes, which is what the + # proxy Deployment already relies on for this same case. + - notContains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_URL + any: true + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_USERNAME + valueFrom: + secretKeyRef: + name: RELEASE-NAME-litellm-dbcredentials + key: username + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: RELEASE-NAME-litellm-dbcredentials + key: password + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_HOST + value: RELEASE-NAME-postgresql + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_NAME + value: litellm + + - it: should keep a password holding URL-special characters out of the rendered Job + template: migrations-job.yaml + set: + migrationJob: + enabled: true + db: + deployStandalone: true + useExisting: false + postgresql: + auth: + password: "p@ss/w+rd=" + asserts: + # The password now only ever travels through the dbcredentials Secret, so + # it is neither mangled into a URL nor readable in `kubectl get job -o yaml` + # and the Helm release secret. + - notMatchRegexRaw: + pattern: "p@ss/w\\+rd=" + + - it: should follow postgresql.auth.database for the standalone database name + template: migrations-job.yaml + set: + migrationJob: + enabled: true + db: + deployStandalone: true + useExisting: false + postgresql: + auth: + database: custom-db + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: DATABASE_NAME + value: custom-db + - it: should not include DATABASE_URL when deployStandalone is false template: migrations-job.yaml set: