Repository: argoproj/argo (go)

Issue:
Allow whitespace in templates
# Summary

Currently Argo does not allow white space in templating, rendering of `{{workflow.parameters.variable}}` does correctly work, however `{{ workflow.parameters.variable }}` doesn't render the variable. Also `argo lint` did not issue any warning or error.

# Use Cases

I often find it more readable when you can add whitespace to separate markers like `{{` and `}}` from variable names.

---
<!-- Issue Author: Don't delete this message to encourage other users to support your issue! -->
**Message from the maintainers**:

Impacted by this bug? Give it a 👍. We prioritise the issues with the most 👍.

Pull request:
feat(controller): Allow whitespace in variable subsitution. Fixes #4286
Checklist:

* [x] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this is a chore.
* [x] The title of the PR is (a) [conventional](https://www.conventionalcommits.org/en/v1.0.0/), (b) states what changed, and (c) suffixes the related issues number. E.g. `"fix(controller): Updates such and such. Fixes #1234"`.  
* [ ] My organization is added to [USERS.md](https://github.com/argoproj/argo/blob/master/USERS.md).
* [x] I've signed the CLA.
* [x] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
* [x] My builds are green. Try syncing with master if they are not. 


Patch:
diff --git a/Makefile b/Makefile
index f3b20f312..b4fd6e0d9 100644
--- a/Makefile
+++ b/Makefile
@@ -121,6 +121,11 @@ define docker_pull
 	if [ $(K3D) = true ]; then k3d image import $(1); fi
 endef
 
+ifndef $(GOPATH)
+	GOPATH=$(shell go env GOPATH)
+	export GOPATH
+endif
+
 .PHONY: build
 build: status clis images manifests
 
@@ -254,7 +259,7 @@ crds: $(GOPATH)/bin/controller-gen
 $(GOPATH)/bin/controller-gen:
 	trap 'rm -Rf vendor' EXIT
 	go mod vendor
-	go install ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen
+	go install -mod=vendor ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen
 
 $(GOPATH)/bin/go-to-protobuf:
 	trap 'rm -Rf vendor' EXIT
@@ -478,7 +483,7 @@ dist/mixed.swagger.json: $(GOPATH)/bin/swagger $(SWAGGER_FILES) dist/swagger-con
 dist/swaggifed.swagger.json: dist/mixed.swagger.json hack/swaggify.sh
 	cat dist/mixed.swagger.json | sed 's/VERSION/$(VERSION)/' | ./hack/swaggify.sh > dist/swaggifed.swagger.json
 
-dist/kubeified.swagger.json: dist/swaggifed.swagger.json dist/kubernetes.swagger.json 
+dist/kubeified.swagger.json: dist/swaggifed.swagger.json dist/kubernetes.swagger.json
 	go run ./hack/swagger kubeifyswagger dist/swaggifed.swagger.json dist/kubeified.swagger.json
 
 api/openapi-spec/swagger.json: dist/kubeified.swagger.json
@@ -520,4 +525,4 @@ check-version-warning:
 
 .PHONY: parse-examples
 parse-examples:
-	go run -tags fields ./hack parseexamples
\ No newline at end of file
+	go run -tags fields ./hack parseexamples
diff --git a/docs/variables.md b/docs/variables.md
index 3145542d6..5a0c1db95 100644
--- a/docs/variables.md
+++ b/docs/variables.md
@@ -1,9 +1,9 @@
 # Workflow Variables
 
-Some fields in a workflow specification allow for variable references which are automatically substituted by Argo. 
+Some fields in a workflow specification allow for variable references which are automatically substituted by Argo.
 
 ??? note "How to use variables"
-    Variables are enclosed in curly braces and **must not** include whitespace.
+    Variables are enclosed in curly braces and **may** include whitespace between the brackets and variable.
 
     ``` yaml
     apiVersion: argoproj.io/v1alpha1
@@ -24,7 +24,7 @@ Some fields in a workflow specification allow for variable references which are
         container:
           image: docker/whalesay
           command: [cowsay]
-        # args: ["{{ inputs.parameters.message }}"]       <- bad
+        # args: ["{{ inputs.parameters.message }}"]       <- good
           args: ["{{inputs.parameters.message}}"]         #  good
     ```
 
diff --git a/workflow/common/util.go b/workflow/common/util.go
index 0e6e36cc9..ffca81d8a 100644
--- a/workflow/common/util.go
+++ b/workflow/common/util.go
@@ -373,7 +373,7 @@ func SubstituteParams(tmpl *wfv1.Template, globalParams, localParams Parameters)
 func Replace(fstTmpl *fasttemplate.Template, replaceMap map[string]string, allowUnresolved bool) (string, error) {
 	var unresolvedErr error
 	replacedTmpl := fstTmpl.ExecuteFuncString(func(w io.Writer, tag string) (int, error) {
-		replacement, ok := replaceMap[tag]
+		replacement, ok := replaceMap[strings.TrimSpace(tag)]
 		if !ok {
 			// Attempt to resolve nested tags, if possible
 			if index := strings.LastIndex(tag, "{{"); index > 0 {


Test patch:
diff --git a/workflow/common/util_test.go b/workflow/common/util_test.go
index 0b66a00c7..13f741d62 100644
--- a/workflow/common/util_test.go
+++ b/workflow/common/util_test.go
@@ -157,3 +157,17 @@ func TestNestedReplaceString(t *testing.T) {
 		}
 	}
 }
+
+func TestReplaceStringWithWhiteSpace(t *testing.T) {
+
+	replaceMap := map[string]string{"inputs.parameters.message": "hello world"}
+
+	test := `{{ inputs.parameters.message }}`
+	fstTmpl, err := fasttemplate.NewTemplate(test, "{{", "}}")
+	if assert.NoError(t, err) {
+		replacement, err := Replace(fstTmpl, replaceMap, true)
+		if assert.NoError(t, err) {
+			assert.Equal(t, "hello world", replacement)
+		}
+	}
+}