helm: toYaml removes all quotes and yaml types breaks

Output of helm version:

Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}

Output of kubectl version:

Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.7", GitCommit:"dd5e1a2978fd0b97d9b78e1564398aeea7e7fe92", GitTreeState:"clean", BuildDate:"2018-04-19T00:05:56Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"8+", GitVersion:"v1.8.10-gke.0", GitCommit:"16ebd0de8e0ab2d1ef86d5b16ab1899b624a77cd", GitTreeState:"clean", BuildDate:"2018-03-20T20:21:01Z", GoVersion:"go1.8.3b4", Compiler:"gc", Platform:"linux/amd64"}

Cloud Provider/Platform (AKS, GKE, Minikube etc.): GKE

I got following problem:

File: values.yml

info:
  address: "0x1ddf249430c343f72f8b12848aa3f932538e3336"

File: configmap.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ template "fullname" . }}
  labels:
    app: {{ template "fullname" . }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
data:
  file.yml: |-
{{ toYaml .Values.info | indent 4 }}

After toYaml address will be not a string.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 33
  • Comments: 52 (11 by maintainers)

Commits related to this issue

Most upvoted comments

In my opinion this is a bug and not an question/support like it is actually labeled.

It might be a bit late now but I had the same problem but was able to get around it by copying what these guys did here:

https://github.com/kubernetes/charts/blob/master/incubator/elasticsearch-curator/values.yaml

I solved it by not using toYaml … That’s a seemingly direct way to get data from a values file to append to my env: key, but this solution allows me to make sure all of the values are quoted:

container:
  env:
  - name: SOME_ENV_VARIABLE
     value: "foo"
  - name: SOME_OTHER_VARIABLE
     value: "bar"
{{- range .Values.some_value.env }}
  - name: {{ .name }}
    value: {{ .value | quote }}
{{- end }}

Yes, reopen please. This is a big issue

I was able to fix the problem making this: values.yaml:

configmap:
  PROJECT_ID: ""
  ENDPOINT: ""
  PORT: ""
  PROTOCOL: ""

And in my configmap I wrote this:

{{- if .Values.configmap }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{- include "chart.labels" . | nindent 4 }}
data:
{{- range $key, $val := $.Values.configmap }}
  {{ $key | quote }}: {{ $val | quote }}
{{- end }}
{{- end }}

/ no stale please /remove label stale /unstale

or whatever it is …

In other words perhaps some documentation around how to use toYaml in a chart would be more sufficient.

I’ve actually searched for hours to find clear and unambiguous documentation about toYaml and I simply could not find it anywhere. Many people talk about using it in general and using it a certain way, but I could not find a single, actual and official definition of that feature. It seems like this is some kind of hack or hidden feature, nobody should be using.

A feature without documentation is almost like a feature that does not exist, because if nobody can be sure to use it precisely according to spec – as there is no spec – then the feature becomes useless, and unusable in scenarios, where you want to be absolutely sure, it does exactly what is expected.

This issue here, which has been open for such an enormously long time, pretty much wouldn’t exist, if documentation would be proper.

I constantly break my teeth out when trying to find documentation on any of the Kubernetes tools, which are popular. Kubernetes has documentation, but considering how complicated that software is, it would need 100 times more documentation, than it has now. Traefik has terrible documentation. You can’t know anything when reading their documentation. With Helm it’s actually the funniest. When browsing the documentation, it seems like there is a lot of information there, but if you really want to look something up and get to know a Helm feature, you can’t get to know anything, as only a few features are touched and even then, it’s not really explained, but only mentioned in a sentence, as if it were the most obvious thing in the world.

Basically, software without documentation becomes useless for most users. Too few people have the time or ability to browse through a thousand lines of Go source code, just to get the general gist of what is supposed to happen, regarding a certain functionality.

That said, I understand if it’s not possible for the core maintainers to address every single issue in a timely manner. On the other hand, I think it’s unfair to demand, that users, especially the ones who don’t even know anything about how this works, are supposed to do work for this, like in this case, document this feature.

I would even document it, if I would get the point of it. However, I cannot get the point of it, as there is no information on it, beside the source code itself, offering that “feature”.

A tiny step making this whole thing slightly less worse, is at least the following comment.

https://github.com/helm/helm/issues/4262#issuecomment-674193064

At least, this gives a little insight into what is happening. Still, one would need to read the whole YAML implementation of this specific library for the Go language, to be really sure, what the spec of this feature is or at least, to know what should and should not happen.

In other words, changing the behavior of toYaml may NOT be desirable, and it may be more preferable to document its behavior so that we don’t accidentally break charts that have worked around this behavior.

I also do not agree with this. First of all, where is the spec? If there is no spec, there is no definitive behaviour. I.e. it’s already broken. Nobody can be sure, it does exactly what it is expected to do, as proven by this issue. As I said, this issue wouldn’t exist, if proper documentation would be available.

To my knowledge, the current state of toYaml is, that its behaviour is undefined. So, there is no breaking it, as it is already broken.

Just encountered this, it makes toYaml pretty useless for us. With this in values.yaml:

foo:
  bar: ":tricky-{}[]!#|>foobar%@"

this template

foo:
{{  .Values.foo | toYaml | indent 2 }}

loses the quotes:

foo:
  bar: :tricky-{}[]!#|>foobar%@

This bug bit me in the ass today 😦

Part of the map to that is passed through toYam are regexps that need to be in single quotes Since the toYaml removes the quotes, the regexps are either different or directly invalid yaml

Is anyone looking at this 3 year old bug?

Hi everyone, I would like to leave my contribution to this issue, I have investigated a little bit, and I don’t think this is actually a bug.

The package github.com/ghodss/yaml (used by Helm) uses gopkg.in/yaml.v2 internally. The gopkg.in/yaml.v2 try to figure it out which type of data it is going to be parsed and written to the YAML output. This is done in the marshal function in encode.go, in case of a map it will iterate through the keys and values and call marshal function recursively for each key and value

The scenario described by this issue, we have a string value 0x1ddf249430c343f72f8b19868ea4f932500e0000, the marshal function will call a function called stringv which will decide how to output the value, for instance, if it should be quoted, unquoted and so on.

Inside stringv, the function resolve is called and it will try to parse the value using strconv.ParseInt and strconv.ParseUInt, however, the value 0x1ddf249430c343f72f8b19868ea4f932500e0000 cannot be parsed correctly and the resolver assume that the value can be written as plain text returning a tag:yaml.org,2002:str and the value returned will be unquoted.

If we use the same example to reproduce the bug but instead set the key with a valid value, it works:

package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
)

func main() {
	p := map[string]string{"key": "0x1c8"}
	d, _ := yaml.Marshal(p)
	fmt.Print(string(d))
}

Output:

key: "0x1c8"

Trying to parse that value with the strconv.ParseInt you can see the error:

data, err := strconv.ParseInt("0x1ddf249430c343f72f8b19868ea4f932500e0000", 0, 64)
fmt.Println(data)
fmt.Println(err)

Result:

9223372036854775807
strconv.ParseInt: parsing "0x1ddf249430c343f72f8b19868ea4f932500e0000": value out of range

As you can see in the error message the value 9223372036854775807 is the max size of int64. That’s why the parsed value is out of range.

So I don’t think it is a bug since YAML package seems to be doing the right thing and the toYAML function in Helm is simply getting the results and returning it which is correct. It was just the value that was too long to be parsed to a int64 causing the value to be unquoted.

Maybe the post above using the | quote would be a option to force quoting the value even when the YAML parser thinks it should be in plain text.

Do you have any feedback on this findings? @bacongobbler

So I’m trying to us toYaml to dynamically inject custom Environment variables along side required environment variables in my container: definition in my Helm chart.

The problem is that regardless of whether the value is a numer or string, the requirement is that the environment value needs to be quoted. Because toYaml is unquoting it my helm chart is failing.

Anyone know how I can prevent this?

Not being able to keep the type of the original yaml is a very surprising behavior. One expects the string "1" to stay a string and not turn into a yaml integer 1: The issue seems to be in “sigs.k8s.io/yaml” package in the Unmarshal function as the following program demonstrates:

package main

import (
	"fmt"

	"sigs.k8s.io/yaml"
)

var data = `
a: Easy!
b:
  c: 2
  d: [3, 4]
  e: "5"
`

func main() {

	// Unmarshal the YAML back into a Person struct.
	m := map[string]interface{}{}

	err = yaml.Unmarshal([]byte(data), &m)
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}
	fmt.Println(m)
}

/*
age: 30
name: John

map[a:Easy! b:map[c:2 d:[3 4] e:5]]
*/

The reference go-yaml v2 & v3 program (with the "e" addition) keeps the quotes just fine.

I used single quotes around the double quotes to get double quotes in toYaml output. SERVER_USE-FORWARD-HEADERS: ‘“true”’

I’m not sure if this should go under this issue, although it’s definitely in the same genre.

YAML types (eg property: !MyClass {}) get stripped out, and actually replaced by empty objects. Very broken.

@shal You closed this issue, did the problem disappear for you? I see a similar behavior with toYaml which is caused problems for me right now.

I used single quotes around the double quotes to get double quotes in toYaml output. SERVER_USE-FORWARD-HEADERS: ‘“true”’

This doesn’t work for me. The single quotes are just carried over to the final output 😦

None of the proposed solutions work if you don’t control the chart you are using. In my case it’s: https://github.com/argoproj/argo-helm/blob/main/charts/argo-cd/templates/extra-manifests.yaml

This allows adding any extra K8s object to the chart, but without quotes it’s proving to be very challenging 😕


EDIT: I finally found a workaround that works for my use-case (cronjob schedule). Instead of this:

schedule: '0 1 * * *' 

which results in:

schedule: 0 1 * * *

I can add whitespace between the quote and the expression:

schedule: ' 0 1 * * * ' 

and it will be rendered as:

schedule: ' 0 1 * * * ' 

Which K8s accepts, despite the extra whitespace (it does not accept a cron schedule without quotes). Hope this helps someone!

i use this, works for me

{{ tpl $value $ | quote }}