helm: Tpl funcion does not work in range
I am pretty new to Helm and not really sure this is indeed an issue or an intended behaviour since I haven’t managed to find enough documentation about the tpl function.
I am trying to use the tpl function in a range over a list of objects like the following one:
anything:
- name: "Ciao"
value: "value1"
- name: "Mondo"
value: "value2"
I want that for each object a string like {{ .name }} is evaluated giving as result “Ciao” at the first iteration and “Mondo” at the second iteration.
I have tried to implement this behaviour as follows:
{{- range .Values.anything }}
trigger: {{ tpl "{{ .name }}" . }}
{{- end }}
Helm is not able to render the template and I get the following error:
Error: render error in "tpl-bug/templates/configmap.yaml": template: tpl-bug/templates/configmap.yaml:5:17: executing "tpl-bug/templates/configmap.yaml" at <tpl "{{ .name }}" .>: error calling tpl: Cannot retrieve Template.Basepath from values inside tpl function: {{ .name }} (BasePath is not a value)
I have developed a minimal chart that reproduces the issue: tpl-bug.zip
To me it looks like tpl always requires the root scope $ in order to be able to find Template.BasePath, at least this is what I was able to understand from the code in engine.go:
basePath, err := vals.PathValue("Template.BasePath")
if err != nil {
return "", fmt.Errorf("Cannot retrieve Template.Basepath from values inside tpl function: %s (%s)", tpl, err.Error())
}
Is this the intended behaviour? If yes how could I achieve the same result anyway? I have simplified a lot my real scenario, in my real case the string “{{ .name }}” comes from an external resource.
Output of helm version:
Client: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"} Server: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}
Output of kubectl version:
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.0", GitCommit:"e8462b5b5dc2584fdcd18e6bcfe9f1e4d970a529", GitTreeState:"clean", BuildDate:"2019-06-19T16:40:16Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.5", GitCommit:"2166946f41b36dea2c4626f90a77706f426cdea2", GitTreeState:"clean", BuildDate:"2019-03-25T15:19:22Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
Cloud Provider/Platform (AKS, GKE, Minikube etc.): Microsoft Azure
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 5
- Comments: 15 (1 by maintainers)
I spend some time to find a workaround for this, so, this might be useful for someone:
Inside the nested template the global context of the parent template is accessible as usual through dot, the
rangevariables are accessible like.key,.value.To use the tpl funciton inside a range loop , i m passing $ (global ctx) instead of .
{{ range .Values.list }} {{ tpl .item.template $ }} {{ end }}
For anyone who stumbles upon this issues, one solution is to pass the
Templateobject into thetplfunction alongside your other values viadict.See this implementation:
@bacongobbler this seems like the ‘cleanest’ approach, i wonder if it’s worth noting in documentation?
At runtime, the rendering engine needs to know the path of the original template as well as the name of the template, both of which cannot be inferred ahead of time.
tplchecks those values in order to know which template it is modifying. Looking at the code, that seems like the intended behaviour. The issue here is therangefunction: when inside arangefunction, the scope of dot (.) is limited to the scope of the range. In other words, your call totplis the equivalent ofBecause the
tplfunction relies on objects that are in the root scope of the dot (.) value (like.Template), the rendering fails. Replacing.with$as you pointed out should allow thetplfunction to work.If you can determine another way to find out which template is calling
tpl, we’d happily accept a PR.@chudytom that should work find yes.
Obviously the structure of the
dictdepends on how the template used in thetplfunction expects its values. This structure can be anything and theValueskey is not necessarily required.For example, if the template looks for values at:
.someValue.anotherValue, yourtplcall could look likeAnother example, if the template looks for values at
.data.someValue.data.AnotherValue, yourtplcall could look likeAs long as you pass through the root
.Templateobject it should all work fine.Hope that clears things up.
@zffocussss
$is a reference to the global scope. For example, these two lines mean the same (if they’re located in the top scope):Now let’s take a look at the following
values.yaml:I seem to be running into the same issue here and cannot figure out how to apply @Zebradil workaround values.yaml:
templates/roles.yaml
templates/_helpers.tpl
@wmiller112 did you figure it out?