Repository: helm/helm (go)

Issue:
Add TOML parsing capability
Currently, the standard library includes functions to parse data from [JSON and YAML](https://github.com/helm/helm/blob/main/pkg/engine/funcs.go#L50-L56), but there is no built-in function to parse data from [TOML](https://toml.io/).

Given that TOML is sometimes used as format for configuration files (e.g. [GitLab Runner configuration](https://docs.gitlab.com/runner/install/kubernetes.html#additional-configuration)), it would be beneficial to have a `fromToml` function in the standard library, like `fromJson` and `fromYaml`, to provide consistency for developers.

Example use case:
We want to deploy GitLab runners with helm and want to use Googles config-connector to create necessary GCS buckets for caching.
The bucket name therefore needs to be provided twice: in the config-connector resource and in the GitLab config TOML. 
To keep the config DRY, we want to be able to extract the GCS bucket name from GitLab's TOML configuration. This is not possible without parsing the TOML configuration.
Other solutions such as YAML anchors or reusing values in the same `values.yaml` file won't work either.

PS: I have a PR in the making. Just adding this issue to adhere to the contribution guidelines.

Pull request:
Feature / toml parsing
<!--  Thanks for sending a pull request!  Here are some tips for you:
1. Make sure to read the Contributing Guide before submitting your PR: https://github.com/helm/helm/blob/main/CONTRIBUTING.md
2. If this PR closes another issue, add 'closes #<issue number>' somewhere in the PR summary. GitHub will automatically close that issue when this PR gets merged. Alternatively, adding 'refs #<issue number>' will not close the issue, but help provide the reviewer more context.-->

**What this PR does / why we need it**:
Adds `fromToml` function that allows parsing TOML configuration. Like `fromJson` or `fromYaml`.
Closes #12024.

**Special notes for your reviewer**:
I added docs for `toToml` (have been missing) and `fromToml` (new implementation) to the [helm-www](https://github.com/helm/helm-www) repo in this PR: https://github.com/helm/helm-www/pull/1455

**If applicable**:
- [x] this PR contains documentation
-> Documentation can be found here: https://github.com/helm/helm-www/pull/1455
- [x] this PR contains unit tests
- [x] this PR has been tested for backwards compatibility
-> It's a new feature and should therefore not cause any compatibility issues.


Patch:
diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go
index 8f05a3a1d..e291fae15 100644
--- a/pkg/engine/funcs.go
+++ b/pkg/engine/funcs.go
@@ -48,6 +48,7 @@ func funcMap() template.FuncMap {
 	// Add some extra functionality
 	extra := template.FuncMap{
 		"toToml":        toTOML,
+		"fromToml":      fromTOML,
 		"toYaml":        toYAML,
 		"fromYaml":      fromYAML,
 		"fromYamlArray": fromYAMLArray,
@@ -132,6 +133,21 @@ func toTOML(v interface{}) string {
 	return b.String()
 }
 
+// fromTOML converts a TOML document into a map[string]interface{}.
+//
+// This is not a general-purpose TOML parser, and will not parse all valid
+// TOML documents. Additionally, because its intended use is within templates
+// it tolerates errors. It will insert the returned error message string into
+// m["Error"] in the returned map.
+func fromTOML(str string) map[string]interface{} {
+	m := make(map[string]interface{})
+
+	if err := toml.Unmarshal([]byte(str), &m); err != nil {
+		m["Error"] = err.Error()
+	}
+	return m
+}
+
 // toJSON takes an interface, marshals it to json, and returns a string. It will
 // always return a string, even on marshal error (empty string).
 //


Test patch:
diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go
index f8be52bf6..de1896a47 100644
--- a/pkg/engine/engine_test.go
+++ b/pkg/engine/engine_test.go
@@ -78,7 +78,7 @@ func TestFuncMap(t *testing.T) {
 	}
 
 	// Test for Engine-specific template functions.
-	expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson", "lookup"}
+	expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "fromToml", "toJson", "fromJson", "lookup"}
 	for _, f := range expect {
 		if _, ok := fns[f]; !ok {
 			t.Errorf("Expected add-on function %q", f)
diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go
index 29bc121b5..95984c490 100644
--- a/pkg/engine/funcs_test.go
+++ b/pkg/engine/funcs_test.go
@@ -37,6 +37,30 @@ func TestFuncs(t *testing.T) {
 		tpl:    `{{ toToml . }}`,
 		expect: "foo = \"bar\"\n",
 		vars:   map[string]interface{}{"foo": "bar"},
+	}, {
+		tpl:    `{{ fromToml . }}`,
+		expect: "map[hello:world]",
+		vars:   `hello = "world"`,
+	}, {
+		tpl:    `{{ fromToml . }}`,
+		expect: "map[table:map[keyInTable:valueInTable subtable:map[keyInSubtable:valueInSubTable]]]",
+		vars: `
+[table]
+keyInTable = "valueInTable"
+[table.subtable]
+keyInSubtable = "valueInSubTable"`,
+	}, {
+		tpl:    `{{ fromToml . }}`,
+		expect: "map[tableArray:[map[keyInElement0:valueInElement0] map[keyInElement1:valueInElement1]]]",
+		vars: `
+[[tableArray]]
+keyInElement0 = "valueInElement0"
+[[tableArray]]
+keyInElement1 = "valueInElement1"`,
+	}, {
+		tpl:    `{{ fromToml . }}`,
+		expect: "map[Error:toml: line 0: unexpected EOF; expected key separator '=']",
+		vars:   "one",
 	}, {
 		tpl:    `{{ toJson . }}`,
 		expect: `{"foo":"bar"}`,