Skip to content

Commit

Permalink
feat: Add workflow vars for template name and entrypoint
Browse files Browse the repository at this point in the history
Add two new global workflow variables.
Issue #6772 asks for the template name, but we cannot always know what
this is (simplest unknowable is kubectl apply of a workflow with a
generateName:) So provide that by deduction. See the tests added in
this commit for what we're aiming for there.
Also provide the variable mainEndpoint, which is set to the name of
the invoked entrypoint, which is controllable, knowable, and could be
used in the same way, and seems like a useful thing to have anyway.

Addresses #6772.

Signed-off-by: Alan Clucas <[email protected]>
  • Loading branch information
Joibel committed Mar 24, 2023
1 parent 0b8724b commit f148456
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,9 @@ For `Template`-level metrics:
| Variable | Description|
|----------|------------|
| `workflow.name` | Workflow name |
| `workflow.templateName` | Workflow template name. This works only for template names, it may give wrong answers for non-generated names |
| `workflow.namespace` | Workflow namespace |
| `workflow.main entrypoint` | Workflow's initial entrypoint |
| `workflow.serviceAccountName` | Workflow service account name |
| `workflow.uid` | Workflow UID. Useful for setting ownership reference to a resource, or a unique artifact location |
| `workflow.parameters.<NAME>` | Input parameter to the workflow |
Expand Down
4 changes: 4 additions & 0 deletions workflow/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,12 @@ const (

// GlobalVarWorkflowName is a global workflow variable referencing the workflow's metadata.name field
GlobalVarWorkflowName = "workflow.name"
// GlobalVarWorkflowTemplateName is a global workflow variable referencing the workflow's name reverse engineered to the template name from a generateName
GlobalVarWorkflowTemplateName = "workflow.templateName"
// GlobalVarWorkflowNamespace is a global workflow variable referencing the workflow's metadata.namespace field
GlobalVarWorkflowNamespace = "workflow.namespace"
// GlobalVarWorkflowMainEntrypoint is a global workflow variable referencing the workflow's top level entrypoint name
GlobalVarWorkflowMainEntrypoint = "workflow.mainEntrypoint"
// GlobalVarWorkflowServiceAccountName is a global workflow variable referencing the workflow's spec.serviceAccountName field
GlobalVarWorkflowServiceAccountName = "workflow.serviceAccountName"
// GlobalVarWorkflowUID is a global workflow variable referencing the workflow's metadata.uid field
Expand Down
13 changes: 13 additions & 0 deletions workflow/controller/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,10 +552,23 @@ func (woc *wfOperationCtx) getWorkflowDeadline() *time.Time {
return &deadline
}

// templateNameFromName tries to strip the suffix for a cronworkflow [\d]{10} or from a
// generateName [a-z0-9]{5} from the name.
var templateNameFromName = regexp.MustCompile(`^(.{3,}?)(\-?[\d]{10})?(\-?[a-z0-9]{5})?$`)

func getTemplateNameFromName(name string) string {
if templateNameFromName.MatchString(name) {
return templateNameFromName.ReplaceAllString(name, "$1")
}
return name
}

// setGlobalParameters sets the globalParam map with global parameters
func (woc *wfOperationCtx) setGlobalParameters(executionParameters wfv1.Arguments) error {
woc.globalParams[common.GlobalVarWorkflowName] = woc.wf.ObjectMeta.Name
woc.globalParams[common.GlobalVarWorkflowTemplateName] = getTemplateNameFromName(woc.wf.ObjectMeta.Name)
woc.globalParams[common.GlobalVarWorkflowNamespace] = woc.wf.ObjectMeta.Namespace
woc.globalParams[common.GlobalVarWorkflowMainEntrypoint] = woc.execWf.Spec.Entrypoint
woc.globalParams[common.GlobalVarWorkflowServiceAccountName] = woc.execWf.Spec.ServiceAccountName
woc.globalParams[common.GlobalVarWorkflowUID] = string(woc.wf.ObjectMeta.UID)
woc.globalParams[common.GlobalVarWorkflowCreationTimestamp] = woc.wf.ObjectMeta.CreationTimestamp.Format(time.RFC3339)
Expand Down
46 changes: 45 additions & 1 deletion workflow/controller/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ func TestGlobalParams(t *testing.T) {

assert.Contains(t, woc.globalParams, "workflow.duration")
assert.Contains(t, woc.globalParams, "workflow.name")
assert.Contains(t, woc.globalParams, "workflow.templateName")
assert.Contains(t, woc.globalParams, "workflow.namespace")
assert.Contains(t, woc.globalParams, "workflow.parameters")
assert.Contains(t, woc.globalParams, "workflow.annotations")
Expand All @@ -375,6 +376,22 @@ func TestGlobalParams(t *testing.T) {
assert.Contains(t, woc.globalParams, "workflow.labels.workflows.argoproj.io/phase")
}

func TestTemplateName(t *testing.T) {
for input, expected := range map[string]string{
`template-name-1234567890`: `template-name`, // Cronworkflows run on a cron
`template-name1234567890`: `template-name`, // Cronworkflow without trailing -
`foobar-fs4hy`: `foobar`, // Standard template submission
`foobar-foobar-ashda`: `foobar-foobar`,
`foobarfs4hy`: `foobar`, // Template without trailing -
`foobar`: `foobar`, // We don't replace the end of this
`foo-bar`: `foo-bar`,
`foobar-foobar-0987654321-4ab1a`: `foobar-foobar`, // Cronworkflow, resubmitted
`foobar-foobar`: `foobar-f`, // Can't really prevent this kind of thing, not desired though
} {
assert.Equal(t, expected, getTemplateNameFromName(input))
}
}

// TestSidecarWithVolume verifies ia sidecar can have a volumeMount reference to both existing or volumeClaimTemplate volumes
func TestSidecarWithVolume(t *testing.T) {
wf := wfv1.MustUnmarshalWorkflow(sidecarWithVol)
Expand Down Expand Up @@ -6620,7 +6637,7 @@ spec:
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["{{workflows.scheduledTime}}"]
args: ["{{workflow.scheduledTime}}"]
`

func TestWorkflowScheduledTimeVariable(t *testing.T) {
Expand All @@ -6634,6 +6651,33 @@ func TestWorkflowScheduledTimeVariable(t *testing.T) {
assert.Equal(t, "2006-01-02T15:04:05-07:00", woc.globalParams[common.GlobalVarWorkflowCronScheduleTime])
}

var wfMainEntrypointVariable = `
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
name: hello-world
spec:
entrypoint: whalesay
shutdown: "Stop"
templates:
- name: whalesay
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["{{workflow.mainEntrypoint}}"]
`

func TestWorkflowMainEntrypointVariable(t *testing.T) {
wf := wfv1.MustUnmarshalWorkflow(wfMainEntrypointVariable)
cancel, controller := newController(wf)
defer cancel()

ctx := context.Background()
woc := newWorkflowOperationCtx(wf, controller)
woc.operate(ctx)
assert.Equal(t, "whalesay", woc.globalParams[common.GlobalVarWorkflowMainEntrypoint])
}

var wfNodeNameField = `
apiVersion: argoproj.io/v1alpha1
kind: Workflow
Expand Down
2 changes: 2 additions & 0 deletions workflow/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ type templateValidationCtx struct {
func newTemplateValidationCtx(wf *wfv1.Workflow, opts ValidateOpts) *templateValidationCtx {
globalParams := make(map[string]string)
globalParams[common.GlobalVarWorkflowName] = placeholderGenerator.NextPlaceholder()
globalParams[common.GlobalVarWorkflowTemplateName] = placeholderGenerator.NextPlaceholder()
globalParams[common.GlobalVarWorkflowNamespace] = placeholderGenerator.NextPlaceholder()
globalParams[common.GlobalVarWorkflowMainEntrypoint] = placeholderGenerator.NextPlaceholder()
globalParams[common.GlobalVarWorkflowServiceAccountName] = placeholderGenerator.NextPlaceholder()
globalParams[common.GlobalVarWorkflowUID] = placeholderGenerator.NextPlaceholder()
return &templateValidationCtx{
Expand Down

0 comments on commit f148456

Please sign in to comment.