diff --git a/examples/README.md b/examples/README.md index 34398e741696..c65aacc103b7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -34,7 +34,7 @@ For a complete description of the Argo workflow spec, please refer to https://gi - [Kubernetes Resources](#kubernetes-resources) - [Docker-in-Docker Using Sidecars](#docker-in-docker-aka-dind-using-sidecars) - [Continuous Integration Example](#continuous-integration-example) - +- [Custom Template Variable Referrence](#Custom Template Variable Referrence) ## Argo CLI In case you want to follow along with this walkthrough, here's a quick overview of the most useful argo command line interface (CLI) commands. @@ -1258,6 +1258,46 @@ spec: mirrorVolumeMounts: true ``` +## Custom Template Variable Referrence +In this example, we can see how we can use the other template language variable reference (E.g: Jinja) in Argo workflow template. +Argo will validate and resolve only the variable that starts with Argo allowed prefix +{***"item", "steps", "inputs", "outputs", "workflow", "tasks"***} + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: custom-template-variable- +spec: + entrypoint: hello-hello-hello + + templates: + - name: hello-hello-hello + steps: + - - name: hello1 + template: whalesay + arguments: + parameters: [{name: message, value: "hello1"}] + - - name: hello2a + template: whalesay + arguments: + parameters: [{name: message, value: "hello2a"}] + - name: hello2b + template: whalesay + arguments: + parameters: [{name: message, value: "hello2b"}] + + - name: whalesay + inputs: + parameters: + - name: message + container: + image: docker/whalesay + command: [cowsay] + args: ["{{user.username}}"] + +``` + ## Continuous Integration Example Continuous integration is a popular application for workflows. Currently, Argo does not provide event triggers for automatically kicking off your CI jobs, but we plan to do so in the near future. Until then, you can easily write a cron job that checks for new commits and kicks off the needed workflow, or use your existing Jenkins server to kick off the workflow. diff --git a/test/e2e/functional/custom_template_variable.yaml b/test/e2e/functional/custom_template_variable.yaml new file mode 100644 index 000000000000..f9ee8fca8df2 --- /dev/null +++ b/test/e2e/functional/custom_template_variable.yaml @@ -0,0 +1,32 @@ +# This template demonstrates the customer variable suppport. +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: custom-template-variable- +spec: + entrypoint: hello-hello-hello + + templates: + - name: hello-hello-hello + steps: + - - name: hello1 + template: whalesay + arguments: + parameters: [{name: message, value: "hello1"}] + - - name: hello2a + template: whalesay + arguments: + parameters: [{name: message, value: "hello2a"}] + - name: hello2b + template: whalesay + arguments: + parameters: [{name: message, value: "hello2b"}] + + - name: whalesay + inputs: + parameters: + - name: message + container: + image: docker/whalesay + command: [cowsay] + args: ["{{custom.variable}}"] diff --git a/workflow/common/common.go b/workflow/common/common.go index d228d9c0f835..66275638cd55 100644 --- a/workflow/common/common.go +++ b/workflow/common/common.go @@ -115,6 +115,9 @@ const ( KubeConfigDefaultVolumeName = "kubeconfig" ) +// GlobalVarWorkflowRootTags is a list of root tags in workflow which could be used for variable reference +var GlobalVarValidWorkflowVariablePrefix = []string{"item.", "steps.", "inputs.", "pod.", "workflow.", "tasks."} + // ExecutionControl contains execution control parameters for executor to decide how to execute the container type ExecutionControl struct { // Deadline is a max timestamp in which an executor can run the container before terminating it diff --git a/workflow/validate/validate.go b/workflow/validate/validate.go index 8f5085fb466f..4bdaf20d2d24 100644 --- a/workflow/validate/validate.go +++ b/workflow/validate/validate.go @@ -230,6 +230,11 @@ func resolveAllVariables(scope map[string]interface{}, tmplStr string) error { fstTmpl := fasttemplate.New(tmplStr, "{{", "}}") fstTmpl.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { + + // Skip the custom variable references + if !checkValidWorkflowVariablePrefix(tag) { + return 0, nil + } _, ok := scope[tag] if !ok && unresolvedErr == nil { if (tag == "item" || strings.HasPrefix(tag, "item.")) && allowAllItemRefs { @@ -245,6 +250,16 @@ func resolveAllVariables(scope map[string]interface{}, tmplStr string) error { return unresolvedErr } +// checkValidWorkflowVariablePrefix is a helper methood check variable starts workflow root elements +func checkValidWorkflowVariablePrefix(tag string) bool { + for _, rootTag := range common.GlobalVarValidWorkflowVariablePrefix { + if strings.HasPrefix(tag, rootTag) { + return true + } + } + return false +} + func validateNonLeaf(tmpl *wfv1.Template) error { if tmpl.ActiveDeadlineSeconds != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.activeDeadlineSeconds is only valid for leaf templates", tmpl.Name) diff --git a/workflow/validate/validate_test.go b/workflow/validate/validate_test.go index 886dfd126bbf..252b1db5191c 100644 --- a/workflow/validate/validate_test.go +++ b/workflow/validate/validate_test.go @@ -1142,3 +1142,23 @@ func TestSpecBadSequenceCountAndEnd(t *testing.T) { err := ValidateWorkflow(wf, true) assert.Error(t, err) } + +var customVariableInput = ` +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: hello-world- +spec: + entrypoint: whalesay + templates: + - name: whalesay + container: + image: docker/whalesay:{{user.username}} +` + +// TestCustomTemplatVariable verifies custom template variable +func TestCustomTemplatVariable(t *testing.T) { + wf := unmarshalWf(customVariableInput) + err := ValidateWorkflow(wf, true) + assert.Equal(t, err, nil) +}