Home > Blockchain >  Helm multi line YAML secret formatting
Helm multi line YAML secret formatting

Time:01-11

Currently I use helm charts openshift (kubernetes) secrets to manage the deployment of my application. In this helm chart, I need my Route (or any object) to consume the tls certificate from a secret.

I can get very close, unfortunately, the formatting is changed and it is not a valid certificate. This is likely because I am not reading in the multi line YAML string correctly.

My config map:

kind: ConfigMap
apiVersion: v1
name: mymap
data:
  certificate: |-
    -----BEGIN CERTIFICATE-----
    certificate data
    -----END CERTIFICATE-----

My Route config file:

kind: Route
apiVersion: route.openshift.io/v1
spec:
  tls:
  {{- $secretObj := (lookup "v1" "ConfigMap" .Release.Namespace "mymap") | default dict }}
  {{- $secretData := (get $secretObj "data") | default dict }}
  {{- $ssocertificate:= (get $secretData "certificate") }}
    certificate: |
      {{$ssocertificate}}

Unfortunately, this does not work. When I try and roll out the helm chart I get:

Error: UPGRADE FAILED: YAML parse error on nginx-helm/templates/nginx-helm.yaml: error converting YAML to JSON: yaml: line 22: could not find expected ':'
helm.go:84: [debug] error converting YAML to JSON: yaml: line 22: could not find expected ':'

If the config map is modified to NOT be a |- YAML multiline format. (>- instead of |-)

kind: ConfigMap
apiVersion: v1
name: mymap
data:
  certificate: >-
    -----BEGIN CERTIFICATE-----
    certificate data
    -----END CERTIFICATE-----

It will read in as expected, but does not work for my use case as the certificate lines will not be proper.

Additional information: Simplifying the problem to:

kind: Route
apiVersion: route.openshift.io/v1
spec:
  tls:
  {{- $secretObj := (lookup "v1" "ConfigMap" .Release.Namespace "mymap") | default dict }}
    certificate: {{$secretObj }}

And trying to deploy gives the same error.

Running helm template --debug (thx for suggestion) returns:

Error: template: nginx-helm/templates/nginx-helm.yaml:233:33: executing "nginx-helm/templates/nginx-helm.yaml" at <$secretObj.data>: wrong type for value; expected map[string]interface {}; got interface {}

Thanks for your help.

CodePudding user response:

Try by using "|" instead of "|-" . It will work.

CodePudding user response:

In YAML block scalar syntax, every line needs to be indented the same amount, and that leading whitespace is trimmed when the YAML content is read.

On the reading end, this means that the ConfigMap value contains a single string with embedded newlines. kubectl get configmap -o yaml might show it written in a single line, and it would have an equivalent value

# kubectl get configmap mymap -o yaml
data:
  certificate: "-----BEGIN CERTIFICATE-----\ncertificate data\n-----END CERTIFICATE-----"

In your Helm chart, this means the $ssocertificate variable again contains this single string, with embedded newlines, and without any leading indentation. You are writing out six leading spaces, followed by the literal content of the variable. I'm expecting you're seeing

# helm template --debug .
spec:
  tls:
    certificate: |
      -----BEGIN CERTIFICATE-----
certificate data
-----END CERTIFICATE-----

but when the non-first lines aren't indented, this breaks the YAML parsing.

The answer to this is to use the Helm (Sprig) indent template function. This adds a consistent amount of indentation to every line in its argument, including embedded newlines, and including the first line. Since indent does indent the first line, it also means you need to not have any whitespace before it on the line where you invoke it.

spec:
  tls:
    certificate: |-
{{ $ssocertificate | indent 6 }}

The difference between | and |- mostly just controls whether the string ends with a newline. For a TLS certificate this probably doesn't matter; I'm used to seeing |- in most places.

  • Related