Helm Secret Juggling

This solution requires a values file with the following schema. Notice how each component is in components. The best part of this is that if there is no secrets property in the component, it will skip the secrets template.

If no value is provided by setting it via the command line and it has a value already set, it will use the current value in the secret.

All secrets will be initialized with the value of initial unless they are specifically provided on install or upgrade like so.

helm upgrade juggle ./chart_directory -n juggle \
  --set components.second.secrets.SUPER_SECRET="string"
## values.yaml ##

components:
  first:
    name: first component

  second:
    name: second component

    secrets:
      SUPER_SECRET: null 
      ANOTHER_SECRET: null
## secrets.yaml ##

{{- range $system, $settings := .Values.components -}}
{{ if $settings.secrets }}
{{ include "system.secrets.component"  (dict "component" $settings "values" $.Values "release" $.Release) }}
{{- end }}
{{- end }}
## _secrets.tpl ##

{{/*
Secret initializer and data maintainer
This helper will initialize the secret data with "initial" value if the secret is not found in the secret object.
IF the secret is found in the secret object, it will use the set value from the secret object thereby maintaining the secret data.
*/}}
{{- define "helper.secrets" -}}
  {{- $secretObj := (lookup "v1" "Secret" .release.Namespace .component.name) | default dict }}
  {{- range $key, $value := .component.secrets }}
  {{- if empty $value -}}
  {{- $secretData := (get $secretObj "data") | default dict }}
  {{- $secret := (get $secretData $key) | default ("initial" | b64enc) }}
  {{ $key }}: {{ $secret | quote }}
  {{- else }}
  {{ $key }}: {{ $value | b64enc | quote }}
  {{- end -}}
  {{- end }}
{{- end -}}

{{- define "system.secrets.component" }}

---
apiVersion: v1
kind: Secret
metadata:
  name: {{ .component.name }}
  labels:
{{ include "helper.metaLabels" . | indent 4 }}
type: Opaque
data:
{{ include "helper.secrets" . | indent 2 }}
{{- end }}

Troubleshooting

In order to really test this during a ` Dry Run` you need to make sure you tweak your option so helm has access to the server data at run time.

--dry-run=server