Repository: helm/helm (go)

Issue:
Helm template throws stack overflow error when trying to use file checksum
<!-- If you need help or think you have found a bug, please help us with your issue by entering the following information (otherwise you can delete this text): -->

Output of `helm version`:
version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}

Output of `kubectl version`:
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b88c290", GitTreeState:"clean", BuildDate:"2019-04-08T17:11:31Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3-k3s.2", GitCommit:"e7e6a3c4e9a7d80b87793612730d10a863a25980", GitTreeState:"clean", BuildDate:"2019-11-18T18:31:23Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"linux/amd64"}

Cloud Provider/Platform (AKS, GKE, Minikube etc.): 
k3s

Helm template blows up when trying to use checksums. To reproduce it, simply create a helm chart with "helm create" then edit the deployment.yaml so the spec template contains:
...
template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/deployment.yaml") . | sha256sum }}
...

Try to generate the manifests with: "helm template --output-dir manifests". You will get something like the log attached.
This of course affects any other commands that rely on generating the manifests like "helm install".


[log.txt](https://github.com/helm/helm/files/3904819/log.txt)



Pull request description:
fix stack overflow error
fix #7111

I found that when a circular reference existed in template "include" function, an endless loop happened in rendering until stack overflow.

My solution is recording the name which is included in a slice. Check the name when the next render comes, if it has been in the recorded slice, throw an error. If not, execute template and pop it out.

Patch:
diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go
index dae0b6be7..5a7d54993 100644
--- a/pkg/engine/engine.go
+++ b/pkg/engine/engine.go
@@ -93,11 +93,19 @@ func warnWrap(warn string) string {
 // initFunMap creates the Engine's FuncMap and adds context-specific functions.
 func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
 	funcMap := funcMap()
+	includedNames := make([]string, 0)
 
 	// Add the 'include' function here so we can close over t.
 	funcMap["include"] = func(name string, data interface{}) (string, error) {
 		var buf strings.Builder
+		for _, n := range includedNames {
+			if n == name {
+				return "", errors.Wrapf(fmt.Errorf("unable to excute template"), "rendering template has a nested reference name: %s", name)
+			}
+		}
+		includedNames = append(includedNames, name)
 		err := t.ExecuteTemplate(&buf, name, data)
+		includedNames = includedNames[:len(includedNames)-1]
 		return buf.String(), err
 	}
 


Test patch:
diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go
index 031527dd0..f3609fcbd 100644
--- a/pkg/engine/engine_test.go
+++ b/pkg/engine/engine_test.go
@@ -460,6 +460,15 @@ func TestAlterFuncMap_include(t *testing.T) {
 		},
 	}
 
+	// Check nested reference in include FuncMap
+	d := &chart.Chart{
+		Metadata: &chart.Metadata{Name: "nested"},
+		Templates: []*chart.File{
+			{Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)},
+			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
+		},
+	}
+
 	v := chartutil.Values{
 		"Values": "",
 		"Chart":  c.Metadata,
@@ -477,6 +486,12 @@ func TestAlterFuncMap_include(t *testing.T) {
 	if got := out["conrad/templates/quote"]; got != expect {
 		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
 	}
+
+	_, err = Render(d, v)
+	expectErrName := "nested/templates/quote"
+	if err == nil {
+		t.Errorf("Expected err of nested reference name: %v", expectErrName)
+	}
 }
 
 func TestAlterFuncMap_require(t *testing.T) {