Repository: kubeflow/pipelines
Pull request #11861: feat(backend): parameterize retryStrategy  input in Argo workflow
Author: login: alyssacgoins
Labels: name: lgtm
name: approved
name: needs-ok-to-test
name: size/XL
Created: 2025-04-25T14:36:09Z | Merged: 2025-05-07T19:14:39Z

**Description of your changes:**
This PR improves how the retryStrategy field is set in the argo workflow spec. When a component with setRetry enabled is included in a pipeline, two single separate container and executor templates are added - retry-system-container-executor and retry-system-container-impl. All four retryStrategy fields (duration, factor maxDuration and limit) are input as parameters into these templates, in order to allow the reuse (previously, separate templates were created for every unique retryStrategy configuration).

If the input pipeline contains no components with setRetry enabled, these two templates will not be created. 


**Checklist:**
- [x ] You have [signed off your commits](https://www.kubeflow.org/docs/about/contributing/#sign-off-your-commits)
- [x] The title for your pull request (PR) should follow our title convention. [Learn more about the pull request title convention used in this repository](https://github.com/kubeflow/pipelines/blob/master/CONTRIBUTING.md#pull-request-title-convention). 
<!--
   PR titles examples:
    * `fix(frontend): fixes empty page. Fixes #1234`
       Use `fix` to indicate that this PR fixes a bug.
    * `feat(backend): configurable service account. Fixes #1234, fixes #1235`
       Use `feat` to indicate that this PR adds a new feature. 
    * `chore: set up changelog generation tools`
       Use `chore` to indicate that this PR makes some changes that users don't need to know.
    * `test: fix CI failure. Part of #1234`
        Use `part of` to indicate that a PR is working on an issue, but shouldn't close the issue when merged.
-->


Linked issue #1234: allow output artifact store configuration (vs hard coded)
it seems like the output artifacts are always stored in a specific minio service, port, namespace, bucket, secrets, etc (`minio-service.kubeflow:9000`). 

see: https://github.com/kubeflow/pipelines/blob/f40a22a3f4a8e06d20cf3e3f425b5058d5c87e0b/sdk/python/kfp/compiler/_op_to_template.py#L148

it would be great to make it flexible, e.g. allow using S3, or change namespace or bucket names.
i suggest making it configurable, i can do such PR if we agree its needed. 
Linked issue comments: user: login: Ark-kun
created_at: 2019-04-26T06:22:35Z
body: There is already a PR for this. But we'd like to solve this in a different way - by having an in-cluster configMap with the storage configuration. Argo already supports that, but we're hesitating to use that mechanism since KF Pipelines might not be the exclusive user of the Argo controller.

Fortunately, the problem will be solved in Argo 2.4 (per-pipeline configMap reference)
author_association: CONTRIBUTOR
user: login: Ark-kun
created_at: 2019-04-26T06:26:51Z
body: Also https://github.com/kubeflow/pipelines/issues/363 https://github.com/kubeflow/pipelines/issues/1195 https://github.com/kubeflow/pipelines/issues/362
author_association: CONTRIBUTOR
user: login: yaronha
created_at: 2019-04-26T18:55:50Z
body: @Ark-kun a mechanism like config map sounds better, it will also make the YAMLs cleaner, currently u get the same 27 lines added to every container, see below, would be nice to hide it:

```yaml
    outputs:
      artifacts:
      - name: mlpipeline-ui-metadata
        path: /mlpipeline-ui-metadata.json
        s3:
          accessKeySecret:
            key: accesskey
            name: mlpipeline-minio-artifact
          bucket: mlpipeline
          endpoint: minio-service.kubeflow:9000
          insecure: true
          key: runs/{{workflow.uid}}/{{pod.name}}/mlpipeline-ui-metadata.tgz
          secretKeySecret:
            key: secretkey
            name: mlpipeline-minio-artifact
      - name: mlpipeline-metrics
        path: /mlpipeline-metrics.json
        s3:
          accessKeySecret:
            key: accesskey
            name: mlpipeline-minio-artifact
          bucket: mlpipeline
          endpoint: minio-service.kubeflow:9000
          insecure: true
          key: runs/{{workflow.uid}}/{{pod.name}}/mlpipeline-metrics.tgz
          secretKeySecret:
            key: secretkey
            name: mlpipeline-minio-artifact
```
author_association: CONTRIBUTOR
user: login: Ark-kun
created_at: 2019-06-01T01:59:41Z
body: Fixed by https://github.com/kubeflow/pipelines/pull/1297
author_association: CONTRIBUTOR

Diff:
diff --git a/backend/src/v2/compiler/argocompiler/argo.go b/backend/src/v2/compiler/argocompiler/argo.go
index f428c55e2ff..aacc38fc9cc 100644
--- a/backend/src/v2/compiler/argocompiler/argo.go
+++ b/backend/src/v2/compiler/argocompiler/argo.go
@@ -333,21 +333,25 @@ func hashValue(value interface{}) (string, error) {
 }
 
 const (
-	paramComponent        = "component"      // component spec
-	paramTask             = "task"           // task spec
-	paramContainer        = "container"      // container spec
-	paramImporter         = "importer"       // importer spec
-	paramRuntimeConfig    = "runtime-config" // job runtime config, pipeline level inputs
-	paramParentDagID      = "parent-dag-id"
-	paramExecutionID      = "execution-id"
-	paramIterationCount   = "iteration-count"
-	paramIterationIndex   = "iteration-index"
-	paramExecutorInput    = "executor-input"
-	paramDriverType       = "driver-type"
-	paramCachedDecision   = "cached-decision"   // indicate hit cache or not
-	paramPodSpecPatch     = "pod-spec-patch"    // a strategic patch merged with the pod spec
-	paramCondition        = "condition"         // condition = false -> skip the task
-	paramKubernetesConfig = "kubernetes-config" // stores Kubernetes config
+	paramComponent               = "component"      // component spec
+	paramTask                    = "task"           // task spec
+	paramContainer               = "container"      // container spec
+	paramImporter                = "importer"       // importer spec
+	paramRuntimeConfig           = "runtime-config" // job runtime config, pipeline level inputs
+	paramParentDagID             = "parent-dag-id"
+	paramExecutionID             = "execution-id"
+	paramIterationCount          = "iteration-count"
+	paramIterationIndex          = "iteration-index"
+	paramExecutorInput           = "executor-input"
+	paramDriverType              = "driver-type"
+	paramCachedDecision          = "cached-decision"            // indicate hit cache or not
+	paramPodSpecPatch            = "pod-spec-patch"             // a strategic patch merged with the pod spec
+	paramCondition               = "condition"                  // condition = false -> skip the task
+	paramKubernetesConfig        = "kubernetes-config"          // stores Kubernetes config
+	paramRetryMaxCount           = "retry-max-count"            // limit on number of retries
+	paramRetryBackOffDuration    = "retry-backoff-duration"     // duration of backoff between retries
+	paramRetryBackOffFactor      = "retry-backoff-factor"       // multiplier for backoff duration between retries
+	paramRetryBackOffMaxDuration = "retry-backoff-max-duration" // limit on backoff duration between retries
 )
 
 func runID() string {
diff --git a/backend/src/v2/compiler/argocompiler/argo_test.go b/backend/src/v2/compiler/argocompiler/argo_test.go
index f95e32626b3..69537da7646 100644
--- a/backend/src/v2/compiler/argocompiler/argo_test.go
+++ b/backend/src/v2/compiler/argocompiler/argo_test.go
@@ -88,6 +88,11 @@ func Test_argo_compiler(t *testing.T) {
 			argoYAMLPath:     "testdata/hello_world_log_level.yaml",
 			envVars:          map[string]string{"PIPELINE_LOG_LEVEL": "3"},
 		},
+		{
+			jobPath:          "../testdata/hello_world_with_retry_all_args.json",
+			platformSpecPath: "",
+			argoYAMLPath:     "testdata/hello_world_with_retry_all_args.yaml",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(fmt.Sprintf("%+v", tt), func(t *testing.T) {
diff --git a/backend/src/v2/compiler/argocompiler/container.go b/backend/src/v2/compiler/argocompiler/container.go
index 17f780f2a5c..5709daeade7 100644
--- a/backend/src/v2/compiler/argocompiler/container.go
+++ b/backend/src/v2/compiler/argocompiler/container.go
@@ -267,10 +267,11 @@ func (c *workflowCompiler) containerExecutorTask(name string, inputs containerEx
 		Template: c.addContainerExecutorTemplate(name, refName),
 		When:     when,
 		Arguments: wfapi.Arguments{
-			Parameters: []wfapi.Parameter{
+			Parameters: append([]wfapi.Parameter{
 				{Name: paramPodSpecPatch, Value: wfapi.AnyStringPtr(inputs.podSpecPatch)},
-				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")},
-			},
+				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")}},
+				c.getTaskRetryParametersWithValues(name)...,
+			),
 		},
 	}
 
@@ -289,8 +290,8 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 	nameContainerImpl := "system-container-impl"
 	taskRetrySpec := c.getTaskRetryPolicySpec(name)
 	if taskRetrySpec != nil {
-		nameContainerExecutor = name + "-" + nameContainerExecutor
-		nameContainerImpl = name + "-" + "system-container-impl"
+		nameContainerExecutor = "retry-" + nameContainerExecutor
+		nameContainerImpl = "retry-" + nameContainerImpl
 	}
 	_, ok := c.templates[nameContainerExecutor]
 	if ok {
@@ -299,20 +300,22 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 	container := &wfapi.Template{
 		Name: nameContainerExecutor,
 		Inputs: wfapi.Inputs{
-			Parameters: []wfapi.Parameter{
+			Parameters: append([]wfapi.Parameter{
 				{Name: paramPodSpecPatch},
-				{Name: paramCachedDecision, Default: wfapi.AnyStringPtr("false")},
-			},
+				{Name: paramCachedDecision, Default: wfapi.AnyStringPtr("false")}},
+				c.addParameterDefault(c.getTaskRetryParameters(name), "0")...,
+			),
 		},
 		DAG: &wfapi.DAGTemplate{
 			Tasks: []wfapi.DAGTask{{
 				Name:     "executor",
 				Template: nameContainerImpl,
 				Arguments: wfapi.Arguments{
-					Parameters: []wfapi.Parameter{{
+					Parameters: append([]wfapi.Parameter{{
 						Name:  paramPodSpecPatch,
-						Value: wfapi.AnyStringPtr(inputParameter(paramPodSpecPatch)),
-					}},
+						Value: wfapi.AnyStringPtr(inputParameter(paramPodSpecPatch))}},
+						c.addParameterInputPath(c.getTaskRetryParameters(name))...,
+					),
 				},
 				// When cached decision is true, the container
 				// implementation template will be skipped, but
@@ -335,12 +338,11 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 		args = append(args, "--publish_logs", value)
 	}
 	executor := &wfapi.Template{
-		Name:          nameContainerImpl,
-		RetryStrategy: c.getTaskRetryStrategy(name),
+		Name: nameContainerImpl,
 		Inputs: wfapi.Inputs{
-			Parameters: []wfapi.Parameter{
-				{Name: paramPodSpecPatch},
-			},
+			Parameters: append([]wfapi.Parameter{
+				{Name: paramPodSpecPatch}},
+				c.getTaskRetryParameters(name)...),
 		},
 		// PodSpecPatch input param is where actual image, command and
 		// args come from. It is treated as a strategic merge patch on
@@ -448,6 +450,13 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 			Env:     commonEnvs,
 		},
 	}
+	// If retry policy is set, add retryStrategy to executor
+	if taskRetrySpec != nil {
+		executor.RetryStrategy = c.getTaskRetryStrategyFromInput(inputParameter(paramRetryMaxCount),
+			inputParameter(paramRetryBackOffDuration),
+			inputParameter(paramRetryBackOffFactor),
+			inputParameter(paramRetryBackOffMaxDuration))
+	}
 	// Update pod metadata if it defined in the Kubernetes Spec
 	kubernetesConfigParam := c.wf.Spec.Arguments.GetParameterByName(argumentsKubernetesSpec + refName)
 
@@ -527,38 +536,71 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append(
+		[]wfapi.Parameter{},
+		wfapi.Parameter{
+			Name:  paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount),
+		})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(
+			parameters,
+			wfapi.Parameter{
+				Name:  paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(
+		parameters,
+		wfapi.Parameter{
+			Name:  paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor()),
+		})
+	if retryPolicy.GetBackoffMaxDuration() != nil {
+		retryBackOffMaxDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffMaxDuration().Seconds)
+		parameters = append(
+			parameters,
+			wfapi.Parameter{
+				Name:  paramRetryBackOffMaxDuration,
+				Value: retryBackOffMaxDuration})
+	}
+	return parameters
+}
 
-	var argoMaxDuration string
-	backoffMaxDuration := retryPolicy.GetBackoffMaxDuration()
-	if backoffMaxDuration != nil {
-		argoMaxDuration = strconv.FormatInt(backoffMaxDuration.Seconds, 10)
-	}
-	backoff := &wfapi.Backoff{
-		Factor: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: int32(retryPolicy.GetBackoffFactor()),
-		},
-		MaxDuration: argoMaxDuration,
-		Duration:    argoBackOffDuration,
+func (c *workflowCompiler) addParameterDefault(parameters []wfapi.Parameter, defaultValue string) []wfapi.Parameter {
+	// Set the "Default" field of each parameter in input slice with input defaultValue.
+	for i := range parameters {
+		parameters[i].Default = wfapi.AnyStringPtr(defaultValue)
 	}
-	return &wfapi.RetryStrategy{
-		Limit: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: retryPolicy.MaxRetryCount,
-		},
-		Backoff: backoff,
+	return parameters
+}
+
+func (c *workflowCompiler) addParameterInputPath(parameters []wfapi.Parameter) []wfapi.Parameter {
+	// Format "Value" field of each parameter in input slice as '{{inputs.parameters.[parameter name]}}'
+	for i := range parameters {
+		parameters[i].Value = wfapi.AnyStringPtr(inputParameter(parameters[i].Name))
 	}
+	return parameters
 }
 
 // Extends the PodMetadata to include Kubernetes-specific executor config.
@@ -590,6 +632,26 @@ func extendPodMetadata(
 	}
 }
 
+func (c *workflowCompiler) getTaskRetryStrategyFromInput(maxCount string, backOffDuration string, backOffFactor string,
+	backOffMaxDuration string) *wfapi.RetryStrategy {
+	backoff := &wfapi.Backoff{
+		Factor: &intstr.IntOrString{
+			Type:   intstr.String,
+			StrVal: backOffFactor,
+		},
+		MaxDuration: backOffMaxDuration,
+		Duration:    backOffDuration,
+	}
+
+	return &wfapi.RetryStrategy{
+		Limit: &intstr.IntOrString{
+			Type:   intstr.String,
+			StrVal: maxCount,
+		},
+		Backoff: backoff,
+	}
+}
+
 // Extends metadata map values, highPriorityMap should overwrites lowPriorityMap values
 // The original Map inputs should have higher priority since its defined by admin
 // TODO: Use maps.Copy after moving to go 1.21+
diff --git a/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry.yaml b/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry.yaml
index 40a8ef8aa13..d88c8436a00 100644
--- a/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry.yaml
+++ b/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry.yaml
@@ -16,7 +16,7 @@ spec:
         dest=\"text\", type=str, required=True, default=argparse.SUPPRESS)\n_parsed_args
         = vars(_parser.parse_args())\n\n_outputs = hello_world(**_parsed_args)\n"],"image":"python:3.9"}'
       - name: components-root
-        value: '{"dag":{"tasks":{"hello-world":{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"retryPolicy":{"backoffFactor":2,"backoffMaxDuration":"3600s","maxRetryCount":2},"taskInfo":{"name":"hello-world"}}}},"inputDefinitions":{"parameters":{"text":{"type":"STRING"}}}}'
+        value: '{"dag":{"tasks":{"hello-world":{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"retryPolicy":{"backoffFactor":2,"backoffMaxDuration":"3600s","maxRetryCount":2},"taskInfo":{"name":"hello-world"}},"hello-world-non-retry":{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"taskInfo":{"name":"hello-world-2"}}}},"inputDefinitions":{"parameters":{"text":{"type":"STRING"}}}}'
   entrypoint: entrypoint
   podMetadata:
     annotations:
@@ -105,16 +105,32 @@ spec:
               parameters:
                 - name: pod-spec-patch
                   value: '{{inputs.parameters.pod-spec-patch}}'
+                - name: retry-max-count
+                  value: '{{inputs.parameters.retry-max-count}}'
+                - name: retry-backoff-duration
+                  value: '{{inputs.parameters.retry-backoff-duration}}'
+                - name: retry-backoff-factor
+                  value: '{{inputs.parameters.retry-backoff-factor}}'
+                - name: retry-backoff-max-duration
+                  value: '{{inputs.parameters.retry-backoff-max-duration}}'
             name: executor
-            template: hello-world-system-container-impl
+            template: retry-system-container-impl
             when: '{{inputs.parameters.cached-decision}} != true'
       inputs:
         parameters:
           - name: pod-spec-patch
           - default: "false"
             name: cached-decision
+          - default: "0"
+            name: retry-max-count
+          - default: "0"
+            name: retry-backoff-duration
+          - default: "0"
+            name: retry-backoff-factor
+          - default: "0"
+            name: retry-backoff-max-duration
       metadata: {}
-      name: hello-world-system-container-executor
+      name: retry-system-container-executor
       outputs: {}
     - container:
         command:
@@ -170,16 +186,20 @@ spec:
       inputs:
         parameters:
           - name: pod-spec-patch
+          - name: retry-max-count
+          - name: retry-backoff-duration
+          - name: retry-backoff-factor
+          - name: retry-backoff-max-duration
       metadata: {}
-      name: hello-world-system-container-impl
+      name: retry-system-container-impl
       outputs: {}
       podSpecPatch: '{{inputs.parameters.pod-spec-patch}}'
       retryStrategy:
         backoff:
-          duration: "0"
-          factor: 2
-          maxDuration: "3600"
-        limit: 2
+          duration: '{{inputs.parameters.retry-backoff-duration}}'
+          factor: '{{inputs.parameters.retry-backoff-factor}}'
+          maxDuration: '{{inputs.parameters.retry-backoff-max-duration}}'
+        limit: '{{inputs.parameters.retry-max-count}}'
       volumes:
         - emptyDir: {}
           name: kfp-launcher
@@ -195,6 +215,96 @@ spec:
           name: dot-cache-scratch
         - emptyDir: {}
           name: dot-config-scratch
+    - dag:
+        tasks:
+          - arguments:
+              parameters:
+                - name: pod-spec-patch
+                  value: '{{inputs.parameters.pod-spec-patch}}'
+            name: executor
+            template: system-container-impl
+            when: '{{inputs.parameters.cached-decision}} != true'
+      inputs:
+        parameters:
+          - name: pod-spec-patch
+          - default: "false"
+            name: cached-decision
+      metadata: { }
+      name: system-container-executor
+      outputs: { }
+    - container:
+        command:
+          - should-be-overridden-during-runtime
+        env:
+          - name: KFP_POD_NAME
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.name
+          - name: KFP_POD_UID
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.uid
+        envFrom:
+          - configMapRef:
+              name: metadata-grpc-configmap
+              optional: true
+        image: gcr.io/ml-pipeline/should-be-overridden-during-runtime
+        name: ""
+        resources: { }
+        volumeMounts:
+          - mountPath: /kfp-launcher
+            name: kfp-launcher
+          - mountPath: /gcs
+            name: gcs-scratch
+          - mountPath: /s3
+            name: s3-scratch
+          - mountPath: /minio
+            name: minio-scratch
+          - mountPath: /.local
+            name: dot-local-scratch
+          - mountPath: /.cache
+            name: dot-cache-scratch
+          - mountPath: /.config
+            name: dot-config-scratch
+      initContainers:
+        - args:
+            - --copy
+            - /kfp-launcher/launch
+          command:
+            - launcher-v2
+          image: ghcr.io/kubeflow/kfp-launcher
+          name: kfp-launcher
+          resources:
+            limits:
+              cpu: 500m
+              memory: 128Mi
+            requests:
+              cpu: 100m
+          volumeMounts:
+            - mountPath: /kfp-launcher
+              name: kfp-launcher
+      inputs:
+        parameters:
+          - name: pod-spec-patch
+      metadata: { }
+      name: system-container-impl
+      outputs: { }
+      podSpecPatch: '{{inputs.parameters.pod-spec-patch}}'
+      volumes:
+        - emptyDir: { }
+          name: kfp-launcher
+        - emptyDir: { }
+          name: gcs-scratch
+        - emptyDir: { }
+          name: s3-scratch
+        - emptyDir: { }
+          name: minio-scratch
+        - emptyDir: { }
+          name: dot-local-scratch
+        - emptyDir: { }
+          name: dot-cache-scratch
+        - emptyDir: { }
+          name: dot-config-scratch
     - dag:
         tasks:
           - arguments:
@@ -216,9 +326,37 @@ spec:
                 - default: "false"
                   name: cached-decision
                   value: '{{tasks.hello-world-driver.outputs.parameters.cached-decision}}'
+                - name: retry-max-count
+                  value: "2"
+                - name: retry-backoff-factor
+                  value: "2"
+                - name: retry-backoff-max-duration
+                  value: "3600"
             depends: hello-world-driver.Succeeded
             name: hello-world
-            template: hello-world-system-container-executor
+            template: retry-system-container-executor
+          - arguments:
+              parameters:
+                - name: component
+                  value: '{{workflow.parameters.components-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7}}'
+                - name: task
+                  value: '{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"taskInfo":{"name":"hello-world-2"}}'
+                - name: container
+                  value: '{{workflow.parameters.implementations-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7}}'
+                - name: parent-dag-id
+                  value: '{{inputs.parameters.parent-dag-id}}'
+            name: hello-world-non-retry-driver
+            template: system-container-driver
+          - arguments:
+              parameters:
+                - name: pod-spec-patch
+                  value: '{{tasks.hello-world-non-retry-driver.outputs.parameters.pod-spec-patch}}'
+                - default: "false"
+                  name: cached-decision
+                  value: '{{tasks.hello-world-non-retry-driver.outputs.parameters.cached-decision}}'
+            depends: hello-world-non-retry-driver.Succeeded
+            name: hello-world-non-retry
+            template: system-container-executor
       inputs:
         parameters:
           - name: parent-dag-id
diff --git a/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry_all_args.yaml b/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry_all_args.yaml
new file mode 100644
index 00000000000..961f9424ed4
--- /dev/null
+++ b/backend/src/v2/compiler/argocompiler/testdata/hello_world_with_retry_all_args.yaml
@@ -0,0 +1,468 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  creationTimestamp: null
+  generateName: hello-world-
+spec:
+  arguments:
+    parameters:
+      - name: components-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7
+        value: '{"executorLabel":"exec-hello-world","inputDefinitions":{"parameters":{"text":{"type":"STRING"}}}}'
+      - name: implementations-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7
+        value: '{"args":["--text","{{$.inputs.parameters[''text'']}}"],"command":["sh","-ec","program_path=$(mktemp)\nprintf
+        \"%s\" \"$0\" \u003e \"$program_path\"\npython3 -u \"$program_path\" \"$@\"\n","def
+        hello_world(text):\n    print(text)\n    return text\n\nimport argparse\n_parser
+        = argparse.ArgumentParser(prog=''Hello world'', description='''')\n_parser.add_argument(\"--text\",
+        dest=\"text\", type=str, required=True, default=argparse.SUPPRESS)\n_parsed_args
+        = vars(_parser.parse_args())\n\n_outputs = hello_world(**_parsed_args)\n"],"image":"python:3.9"}'
+      - name: components-root
+        value: '{"dag":{"tasks":{"hello-world":{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"retryPolicy":{"backoffDuration":"1s","backoffFactor":2,"backoffMaxDuration":"3600s","maxRetryCount":2},"taskInfo":{"name":"hello-world"}},"hello-world-non-retry":{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"taskInfo":{"name":"hello-world-2"}}}},"inputDefinitions":{"parameters":{"text":{"type":"STRING"}}}}'
+  entrypoint: entrypoint
+  podMetadata:
+    annotations:
+      pipelines.kubeflow.org/v2_component: "true"
+    labels:
+      pipelines.kubeflow.org/v2_component: "true"
+  serviceAccountName: pipeline-runner
+  templates:
+    - container:
+        args:
+          - --type
+          - CONTAINER
+          - --pipeline_name
+          - namespace/n1/pipeline/hello-world
+          - --run_id
+          - '{{workflow.uid}}'
+          - --run_name
+          - '{{workflow.name}}'
+          - --run_display_name
+          - ""
+          - --dag_execution_id
+          - '{{inputs.parameters.parent-dag-id}}'
+          - --component
+          - '{{inputs.parameters.component}}'
+          - --task
+          - '{{inputs.parameters.task}}'
+          - --container
+          - '{{inputs.parameters.container}}'
+          - --iteration_index
+          - '{{inputs.parameters.iteration-index}}'
+          - --cached_decision_path
+          - '{{outputs.parameters.cached-decision.path}}'
+          - --pod_spec_patch_path
+          - '{{outputs.parameters.pod-spec-patch.path}}'
+          - --condition_path
+          - '{{outputs.parameters.condition.path}}'
+          - --kubernetes_config
+          - '{{inputs.parameters.kubernetes-config}}'
+          - --http_proxy
+          - ""
+          - --https_proxy
+          - ""
+          - --no_proxy
+          - ""
+        command:
+          - driver
+        image: ghcr.io/kubeflow/kfp-driver
+        name: ""
+        resources:
+          limits:
+            cpu: 500m
+            memory: 512Mi
+          requests:
+            cpu: 100m
+            memory: 64Mi
+      inputs:
+        parameters:
+          - name: component
+          - name: task
+          - name: container
+          - name: parent-dag-id
+          - default: "-1"
+            name: iteration-index
+          - default: ""
+            name: kubernetes-config
+      metadata: {}
+      name: system-container-driver
+      outputs:
+        parameters:
+          - name: pod-spec-patch
+            valueFrom:
+              default: ""
+              path: /tmp/outputs/pod-spec-patch
+          - default: "false"
+            name: cached-decision
+            valueFrom:
+              default: "false"
+              path: /tmp/outputs/cached-decision
+          - name: condition
+            valueFrom:
+              default: "true"
+              path: /tmp/outputs/condition
+    - dag:
+        tasks:
+          - arguments:
+              parameters:
+                - name: pod-spec-patch
+                  value: '{{inputs.parameters.pod-spec-patch}}'
+                - name: retry-max-count
+                  value: '{{inputs.parameters.retry-max-count}}'
+                - name: retry-backoff-duration
+                  value: '{{inputs.parameters.retry-backoff-duration}}'
+                - name: retry-backoff-factor
+                  value: '{{inputs.parameters.retry-backoff-factor}}'
+                - name: retry-backoff-max-duration
+                  value: '{{inputs.parameters.retry-backoff-max-duration}}'
+            name: executor
+            template: retry-system-container-impl
+            when: '{{inputs.parameters.cached-decision}} != true'
+      inputs:
+        parameters:
+          - name: pod-spec-patch
+          - default: "false"
+            name: cached-decision
+          - default: "0"
+            name: retry-max-count
+          - default: "0"
+            name: retry-backoff-duration
+          - default: "0"
+            name: retry-backoff-factor
+          - default: "0"
+            name: retry-backoff-max-duration
+      metadata: {}
+      name: retry-system-container-executor
+      outputs: {}
+    - container:
+        command:
+          - should-be-overridden-during-runtime
+        env:
+          - name: KFP_POD_NAME
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.name
+          - name: KFP_POD_UID
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.uid
+        envFrom:
+          - configMapRef:
+              name: metadata-grpc-configmap
+              optional: true
+        image: gcr.io/ml-pipeline/should-be-overridden-during-runtime
+        name: ""
+        resources: {}
+        volumeMounts:
+          - mountPath: /kfp-launcher
+            name: kfp-launcher
+          - mountPath: /gcs
+            name: gcs-scratch
+          - mountPath: /s3
+            name: s3-scratch
+          - mountPath: /minio
+            name: minio-scratch
+          - mountPath: /.local
+            name: dot-local-scratch
+          - mountPath: /.cache
+            name: dot-cache-scratch
+          - mountPath: /.config
+            name: dot-config-scratch
+      initContainers:
+        - args:
+            - --copy
+            - /kfp-launcher/launch
+          command:
+            - launcher-v2
+          image: ghcr.io/kubeflow/kfp-launcher
+          name: kfp-launcher
+          resources:
+            limits:
+              cpu: 500m
+              memory: 128Mi
+            requests:
+              cpu: 100m
+          volumeMounts:
+            - mountPath: /kfp-launcher
+              name: kfp-launcher
+      inputs:
+        parameters:
+          - name: pod-spec-patch
+          - name: retry-max-count
+          - name: retry-backoff-duration
+          - name: retry-backoff-factor
+          - name: retry-backoff-max-duration
+      metadata: {}
+      name: retry-system-container-impl
+      outputs: {}
+      podSpecPatch: '{{inputs.parameters.pod-spec-patch}}'
+      retryStrategy:
+        backoff:
+          duration: '{{inputs.parameters.retry-backoff-duration}}'
+          factor: '{{inputs.parameters.retry-backoff-factor}}'
+          maxDuration: '{{inputs.parameters.retry-backoff-max-duration}}'
+        limit: '{{inputs.parameters.retry-max-count}}'
+      volumes:
+        - emptyDir: {}
+          name: kfp-launcher
+        - emptyDir: {}
+          name: gcs-scratch
+        - emptyDir: {}
+          name: s3-scratch
+        - emptyDir: {}
+          name: minio-scratch
+        - emptyDir: {}
+          name: dot-local-scratch
+        - emptyDir: {}
+          name: dot-cache-scratch
+        - emptyDir: {}
+          name: dot-config-scratch
+    - dag:
+        tasks:
+          - arguments:
+              parameters:
+                - name: pod-spec-patch
+                  value: '{{inputs.parameters.pod-spec-patch}}'
+            name: executor
+            template: system-container-impl
+            when: '{{inputs.parameters.cached-decision}} != true'
+      inputs:
+        parameters:
+          - name: pod-spec-patch
+          - default: "false"
+            name: cached-decision
+      metadata: { }
+      name: system-container-executor
+      outputs: { }
+    - container:
+        command:
+          - should-be-overridden-during-runtime
+        env:
+          - name: KFP_POD_NAME
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.name
+          - name: KFP_POD_UID
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.uid
+        envFrom:
+          - configMapRef:
+              name: metadata-grpc-configmap
+              optional: true
+        image: gcr.io/ml-pipeline/should-be-overridden-during-runtime
+        name: ""
+        resources: { }
+        volumeMounts:
+          - mountPath: /kfp-launcher
+            name: kfp-launcher
+          - mountPath: /gcs
+            name: gcs-scratch
+          - mountPath: /s3
+            name: s3-scratch
+          - mountPath: /minio
+            name: minio-scratch
+          - mountPath: /.local
+            name: dot-local-scratch
+          - mountPath: /.cache
+            name: dot-cache-scratch
+          - mountPath: /.config
+            name: dot-config-scratch
+      initContainers:
+        - args:
+            - --copy
+            - /kfp-launcher/launch
+          command:
+            - launcher-v2
+          image: ghcr.io/kubeflow/kfp-launcher
+          name: kfp-launcher
+          resources:
+            limits:
+              cpu: 500m
+              memory: 128Mi
+            requests:
+              cpu: 100m
+          volumeMounts:
+            - mountPath: /kfp-launcher
+              name: kfp-launcher
+      inputs:
+        parameters:
+          - name: pod-spec-patch
+      metadata: { }
+      name: system-container-impl
+      outputs: { }
+      podSpecPatch: '{{inputs.parameters.pod-spec-patch}}'
+      volumes:
+        - emptyDir: { }
+          name: kfp-launcher
+        - emptyDir: { }
+          name: gcs-scratch
+        - emptyDir: { }
+          name: s3-scratch
+        - emptyDir: { }
+          name: minio-scratch
+        - emptyDir: { }
+          name: dot-local-scratch
+        - emptyDir: { }
+          name: dot-cache-scratch
+        - emptyDir: { }
+          name: dot-config-scratch
+    - dag:
+        tasks:
+          - arguments:
+              parameters:
+                - name: component
+                  value: '{{workflow.parameters.components-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7}}'
+                - name: task
+                  value: '{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"retryPolicy":{"backoffDuration":"1s","backoffFactor":2,"backoffMaxDuration":"3600s","maxRetryCount":2},"taskInfo":{"name":"hello-world"}}'
+                - name: container
+                  value: '{{workflow.parameters.implementations-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7}}'
+                - name: parent-dag-id
+                  value: '{{inputs.parameters.parent-dag-id}}'
+            name: hello-world-driver
+            template: system-container-driver
+          - arguments:
+              parameters:
+                - name: pod-spec-patch
+                  value: '{{tasks.hello-world-driver.outputs.parameters.pod-spec-patch}}'
+                - default: "false"
+                  name: cached-decision
+                  value: '{{tasks.hello-world-driver.outputs.parameters.cached-decision}}'
+                - name: retry-max-count
+                  value: "2"
+                - name: retry-backoff-duration
+                  value: "1"
+                - name: retry-backoff-factor
+                  value: "2"
+                - name: retry-backoff-max-duration
+                  value: "3600"
+            depends: hello-world-driver.Succeeded
+            name: hello-world
+            template: retry-system-container-executor
+          - arguments:
+              parameters:
+                - name: component
+                  value: '{{workflow.parameters.components-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7}}'
+                - name: task
+                  value: '{"cachingOptions":{"enableCache":true},"componentRef":{"name":"comp-hello-world"},"inputs":{"parameters":{"text":{"componentInputParameter":"text"}}},"taskInfo":{"name":"hello-world-2"}}'
+                - name: container
+                  value: '{{workflow.parameters.implementations-203fce8adabe0cfa7da54b9d3ff79c772136c926974659b51c378727c7ccdfb7}}'
+                - name: parent-dag-id
+                  value: '{{inputs.parameters.parent-dag-id}}'
+            name: hello-world-non-retry-driver
+            template: system-container-driver
+          - arguments:
+              parameters:
+                - name: pod-spec-patch
+                  value: '{{tasks.hello-world-non-retry-driver.outputs.parameters.pod-spec-patch}}'
+                - default: "false"
+                  name: cached-decision
+                  value: '{{tasks.hello-world-non-retry-driver.outputs.parameters.cached-decision}}'
+            depends: hello-world-non-retry-driver.Succeeded
+            name: hello-world-non-retry
+            template: system-container-executor
+      inputs:
+        parameters:
+          - name: parent-dag-id
+      metadata: {}
+      name: root
+      outputs: {}
+    - container:
+        args:
+          - --type
+          - '{{inputs.parameters.driver-type}}'
+          - --pipeline_name
+          - namespace/n1/pipeline/hello-world
+          - --run_id
+          - '{{workflow.uid}}'
+          - --run_name
+          - '{{workflow.name}}'
+          - --run_display_name
+          - ""
+          - --dag_execution_id
+          - '{{inputs.parameters.parent-dag-id}}'
+          - --component
+          - '{{inputs.parameters.component}}'
+          - --task
+          - '{{inputs.parameters.task}}'
+          - --runtime_config
+          - '{{inputs.parameters.runtime-config}}'
+          - --iteration_index
+          - '{{inputs.parameters.iteration-index}}'
+          - --execution_id_path
+          - '{{outputs.parameters.execution-id.path}}'
+          - --iteration_count_path
+          - '{{outputs.parameters.iteration-count.path}}'
+          - --condition_path
+          - '{{outputs.parameters.condition.path}}'
+          - --http_proxy
+          - ""
+          - --https_proxy
+          - ""
+          - --no_proxy
+          - ""
+        command:
+          - driver
+        image: ghcr.io/kubeflow/kfp-driver
+        name: ""
+        resources:
+          limits:
+            cpu: 500m
+            memory: 512Mi
+          requests:
+            cpu: 100m
+            memory: 64Mi
+      inputs:
+        parameters:
+          - name: component
+          - default: ""
+            name: runtime-config
+          - default: ""
+            name: task
+          - default: "0"
+            name: parent-dag-id
+          - default: "-1"
+            name: iteration-index
+          - default: DAG
+            name: driver-type
+      metadata: {}
+      name: system-dag-driver
+      outputs:
+        parameters:
+          - name: execution-id
+            valueFrom:
+              path: /tmp/outputs/execution-id
+          - name: iteration-count
+            valueFrom:
+              default: "0"
+              path: /tmp/outputs/iteration-count
+          - name: condition
+            valueFrom:
+              default: "true"
+              path: /tmp/outputs/condition
+    - dag:
+        tasks:
+          - arguments:
+              parameters:
+                - name: component
+                  value: '{{workflow.parameters.components-root}}'
+                - name: runtime-config
+                  value: '{"parameters":{"text":{"stringValue":"hi there"}}}'
+                - name: driver-type
+                  value: ROOT_DAG
+            name: root-driver
+            template: system-dag-driver
+          - arguments:
+              parameters:
+                - name: parent-dag-id
+                  value: '{{tasks.root-driver.outputs.parameters.execution-id}}'
+                - name: condition
+                  value: ""
+            depends: root-driver.Succeeded
+            name: root
+            template: root
+      inputs: {}
+      metadata: {}
+      name: entrypoint
+      outputs: {}
+status:
+  finishedAt: null
+  startedAt: null
\ No newline at end of file
diff --git a/backend/src/v2/compiler/testdata/hello_world_with_retry.json b/backend/src/v2/compiler/testdata/hello_world_with_retry.json
index ca96d621208..97383c5c9ee 100644
--- a/backend/src/v2/compiler/testdata/hello_world_with_retry.json
+++ b/backend/src/v2/compiler/testdata/hello_world_with_retry.json
@@ -59,6 +59,24 @@
             "taskInfo": {
               "name": "hello-world"
             }
+          },
+          "hello-world-non-retry": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world-2"
+            }
           }
         }
       },
@@ -80,4 +98,5 @@
       }
     }
   }
-}
\ No newline at end of file
+}
+
diff --git a/backend/src/v2/compiler/testdata/hello_world_with_retry_all_args.json b/backend/src/v2/compiler/testdata/hello_world_with_retry_all_args.json
new file mode 100644
index 00000000000..ba3de5fbf10
--- /dev/null
+++ b/backend/src/v2/compiler/testdata/hello_world_with_retry_all_args.json
@@ -0,0 +1,103 @@
+{
+  "pipelineSpec": {
+    "components": {
+      "comp-hello-world": {
+        "executorLabel": "exec-hello-world",
+        "inputDefinitions": {
+          "parameters": {
+            "text": {
+              "type": "STRING"
+            }
+          }
+        }
+      }
+    },
+    "deploymentSpec": {
+      "executors": {
+        "exec-hello-world": {
+          "container": {
+            "args": [
+              "--text",
+              "{{$.inputs.parameters['text']}}"
+            ],
+            "command": [
+              "sh",
+              "-ec",
+              "program_path=$(mktemp)\nprintf \"%s\" \"$0\" > \"$program_path\"\npython3 -u \"$program_path\" \"$@\"\n",
+              "def hello_world(text):\n    print(text)\n    return text\n\nimport argparse\n_parser = argparse.ArgumentParser(prog='Hello world', description='')\n_parser.add_argument(\"--text\", dest=\"text\", type=str, required=True, default=argparse.SUPPRESS)\n_parsed_args = vars(_parser.parse_args())\n\n_outputs = hello_world(**_parsed_args)\n"
+            ],
+            "image": "python:3.9"
+          }
+        }
+      }
+    },
+    "pipelineInfo": {
+      "name": "namespace/n1/pipeline/hello-world"
+    },
+    "root": {
+      "dag": {
+        "tasks": {
+          "hello-world": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "retryPolicy": {
+              "backoffFactor": 2,
+              "backoffMaxDuration": "3600s",
+              "backoffDuration": "1s",
+              "maxRetryCount": 2
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world"
+            }
+          },
+          "hello-world-non-retry": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world-2"
+            }
+          }
+        }
+      },
+      "inputDefinitions": {
+        "parameters": {
+          "text": {
+            "type": "STRING"
+          }
+        }
+      }
+    },
+    "schemaVersion": "2.0.0",
+    "sdkVersion": "kfp-1.6.5"
+  },
+  "runtimeConfig": {
+    "parameters": {
+      "text": {
+        "stringValue": "hi there"
+      }
+    }
+  }
+}
+
diff --git a/samples/v2/pipeline_with_retry.py b/samples/v2/pipeline_with_retry.py
new file mode 100644
index 00000000000..401e2c58510
--- /dev/null
+++ b/samples/v2/pipeline_with_retry.py
@@ -0,0 +1,31 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def verify_retries(retries: str) -> bool:
+    if retries != '2':
+        raise Exception('Number of retries has not reached two yet.')
+    return True
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op(text: str) -> str:
+    print(text)
+    return text
+
+
+@dsl.pipeline()
+def retry_pipeline():
+    task1 = verify_retries(retries="{{retries}}").set_retry(num_retries=2)
+    task2 = print_op(text='test').set_retry(num_retries=2, backoff_duration="1 second", backoff_factor=1, backoff_max_duration="1 second")
+    task3 = print_op(text='test').set_retry(num_retries=0)
+
+if __name__ == '__main__':
+    compiler.Compiler.compile(
+        pipeline_func=retry_pipeline,
+        package_path='pipeline_with_retry.yaml')
+
diff --git a/samples/v2/sample_test.py b/samples/v2/sample_test.py
index 7b24865e107..d170e514d89 100644
--- a/samples/v2/sample_test.py
+++ b/samples/v2/sample_test.py
@@ -40,6 +40,7 @@
 import subdagio
 import two_step_pipeline_containerized
 import yaml
+import pipeline_with_retry
 
 _MINUTE = 60  # seconds
 _DEFAULT_TIMEOUT = 5 * _MINUTE
@@ -170,6 +171,7 @@ def test(self):
                      .loop_with_after_dependency_set),
             TestCase(
                 pipeline_func=collected_parameters.collected_param_pipeline),
+            TestCase(pipeline_func=pipeline_with_retry.retry_pipeline),
         ]
 
         with ThreadPoolExecutor() as executor:


Reviews:
user: login: gmfrasca
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T17:54:38Z
user: login: gmfrasca
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T18:10:00Z
user: login: gmfrasca
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T18:14:03Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T20:14:46Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T20:15:24Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T20:15:44Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-28T19:22:32Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-28T19:24:49Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-28T19:29:30Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T21:31:38Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T21:35:44Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T17:48:30Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T21:00:59Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T14:31:26Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T15:02:15Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T15:14:56Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T15:42:39Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T15:51:16Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:24:43Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:26:41Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T22:08:24Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T22:09:30Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:49:31Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:49:44Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:50:54Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:52:13Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:55:08Z
user: login: mprahl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-07T15:56:07Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T16:50:21Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T16:50:27Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T16:51:41Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T16:51:49Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T16:53:51Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T17:02:34Z
user: login: mprahl
body: /lgtm
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-07T17:45:41Z
user: login: gmfrasca
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T17:52:28Z
user: login: gmfrasca
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T17:52:44Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:18:34Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:18:53Z
user: login: gmfrasca
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T18:31:52Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:44:38Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:44:49Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:44:54Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:45:05Z
user: login: alyssacgoins
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T18:45:08Z
user: login: gmfrasca
body: /lgtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T19:08:10Z

Review comments:
diff_hunk: @@ -331,21 +331,25 @@ func hashValue(value interface{}) (string, error) {
 }
 
 const (
-	paramComponent        = "component"      // component spec
-	paramTask             = "task"           // task spec
-	paramContainer        = "container"      // container spec
-	paramImporter         = "importer"       // importer spec
-	paramRuntimeConfig    = "runtime-config" // job runtime config, pipeline level inputs
-	paramParentDagID      = "parent-dag-id"
-	paramExecutionID      = "execution-id"
-	paramIterationCount   = "iteration-count"
-	paramIterationIndex   = "iteration-index"
-	paramExecutorInput    = "executor-input"
-	paramDriverType       = "driver-type"
-	paramCachedDecision   = "cached-decision"   // indicate hit cache or not
-	paramPodSpecPatch     = "pod-spec-patch"    // a strategic patch merged with the pod spec
-	paramCondition        = "condition"         // condition = false -> skip the task
-	paramKubernetesConfig = "kubernetes-config" // stores Kubernetes config
+	paramComponent               = "component"      // component spec
+	paramTask                    = "task"           // task spec
+	paramContainer               = "container"      // container spec
+	paramImporter                = "importer"       // importer spec
+	paramRuntimeConfig           = "runtime-config" // job runtime config, pipeline level inputs
+	paramParentDagID             = "parent-dag-id"
+	paramExecutionID             = "execution-id"
+	paramIterationCount          = "iteration-count"
+	paramIterationIndex          = "iteration-index"
+	paramExecutorInput           = "executor-input"
+	paramDriverType              = "driver-type"
+	paramCachedDecision          = "cached-decision"            // indicate hit cache or not
+	paramPodSpecPatch            = "pod-spec-patch"             // a strategic patch merged with the pod spec
+	paramCondition               = "condition"                  // condition = false -> skip the task
+	paramKubernetesConfig        = "kubernetes-config"          // stores Kubernetes config
+	paramRetryMaxCount           = "retry-max-count"            // retry max count
path: backend/src/v2/compiler/argocompiler/argo.go
user: login: gmfrasca
body: let's expand on the comments to tell what each of these params do, ie "retry backoff factor" -> "Amount to multiply duration between each retry during back-off" (or remove the comment if the purpose is obvious)
created_at: 2025-04-25T17:54:38Z
author_association: MEMBER
diff_hunk: @@ -580,6 +637,26 @@ func extendPodMetadata(
 	}
 }
 
+func (c *workflowCompiler) getTaskRetryStrategyFromInput(maxCount string, backOffDuration string, backOffPolicy string,
+	backOffMaxDuration string) *wfapi.RetryStrategy {
+	backoff := &wfapi.Backoff{
+		Factor: &intstr.IntOrString{
+			Type:   intstr.String,
+			StrVal: backOffPolicy,
+		},
+		MaxDuration: backOffMaxDuration,
+		Duration:    backOffDuration,
+	}
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: ```suggestion
func (c *workflowCompiler) getTaskRetryStrategyFromInput(maxCount string, backOffDuration string, backOffFactor string,
	backOffMaxDuration string) *wfapi.RetryStrategy {
	backoff := &wfapi.Backoff{
		Factor: &intstr.IntOrString{
			Type:   intstr.String,
			StrVal: backOffFactor,
		},
		MaxDuration: backOffMaxDuration,
		Duration:    backOffDuration,
	}
```
created_at: 2025-04-25T18:09:59Z
author_association: MEMBER
diff_hunk: @@ -325,11 +349,18 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 		args = append(args, "--publish_logs", value)
 	}
 	executor := &wfapi.Template{
-		Name:          nameContainerImpl,
-		RetryStrategy: c.getTaskRetryStrategy(name),
+		Name: nameContainerImpl,
+		RetryStrategy: c.getTaskRetryStrategyFromInput(inputParameter(paramRetryMaxCount),
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: since the `getTaskRetryStrategy(name)` function is now unused (as far as I can tell), we should remove it
created_at: 2025-04-25T18:14:03Z
author_association: MEMBER
diff_hunk: @@ -331,21 +331,25 @@ func hashValue(value interface{}) (string, error) {
 }
 
 const (
-	paramComponent        = "component"      // component spec
-	paramTask             = "task"           // task spec
-	paramContainer        = "container"      // container spec
-	paramImporter         = "importer"       // importer spec
-	paramRuntimeConfig    = "runtime-config" // job runtime config, pipeline level inputs
-	paramParentDagID      = "parent-dag-id"
-	paramExecutionID      = "execution-id"
-	paramIterationCount   = "iteration-count"
-	paramIterationIndex   = "iteration-index"
-	paramExecutorInput    = "executor-input"
-	paramDriverType       = "driver-type"
-	paramCachedDecision   = "cached-decision"   // indicate hit cache or not
-	paramPodSpecPatch     = "pod-spec-patch"    // a strategic patch merged with the pod spec
-	paramCondition        = "condition"         // condition = false -> skip the task
-	paramKubernetesConfig = "kubernetes-config" // stores Kubernetes config
+	paramComponent               = "component"      // component spec
+	paramTask                    = "task"           // task spec
+	paramContainer               = "container"      // container spec
+	paramImporter                = "importer"       // importer spec
+	paramRuntimeConfig           = "runtime-config" // job runtime config, pipeline level inputs
+	paramParentDagID             = "parent-dag-id"
+	paramExecutionID             = "execution-id"
+	paramIterationCount          = "iteration-count"
+	paramIterationIndex          = "iteration-index"
+	paramExecutorInput           = "executor-input"
+	paramDriverType              = "driver-type"
+	paramCachedDecision          = "cached-decision"            // indicate hit cache or not
+	paramPodSpecPatch            = "pod-spec-patch"             // a strategic patch merged with the pod spec
+	paramCondition               = "condition"                  // condition = false -> skip the task
+	paramKubernetesConfig        = "kubernetes-config"          // stores Kubernetes config
+	paramRetryMaxCount           = "retry-max-count"            // retry max count
path: backend/src/v2/compiler/argocompiler/argo.go
user: login: alyssacgoins
body: Added some more in-detail comments - is that a good level of info?
created_at: 2025-04-25T20:14:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -580,6 +637,26 @@ func extendPodMetadata(
 	}
 }
 
+func (c *workflowCompiler) getTaskRetryStrategyFromInput(maxCount string, backOffDuration string, backOffPolicy string,
+	backOffMaxDuration string) *wfapi.RetryStrategy {
+	backoff := &wfapi.Backoff{
+		Factor: &intstr.IntOrString{
+			Type:   intstr.String,
+			StrVal: backOffPolicy,
+		},
+		MaxDuration: backOffMaxDuration,
+		Duration:    backOffDuration,
+	}
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Yep that variable naming makes much more sense
created_at: 2025-04-25T20:15:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -325,11 +349,18 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 		args = append(args, "--publish_logs", value)
 	}
 	executor := &wfapi.Template{
-		Name:          nameContainerImpl,
-		RetryStrategy: c.getTaskRetryStrategy(name),
+		Name: nameContainerImpl,
+		RetryStrategy: c.getTaskRetryStrategyFromInput(inputParameter(paramRetryMaxCount),
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Removed :+1:
created_at: 2025-04-25T20:15:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -257,11 +257,25 @@ func (c *workflowCompiler) containerExecutorTask(name string, inputs containerEx
 		Template: c.addContainerExecutorTemplate(name, refName),
 		When:     when,
 		Arguments: wfapi.Arguments{
-			Parameters: []wfapi.Parameter{
-				{Name: paramPodSpecPatch, Value: wfapi.AnyStringPtr(inputs.podSpecPatch)},
-				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")},
-			},
-		},
+			Parameters: []wfapi.Parameter{{
+				Name:  paramPodSpecPatch,
+				Value: wfapi.AnyStringPtr(inputs.podSpecPatch),
+			}, {
+				Name:  paramCachedDecision,
+				Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false"),
+			}, {
+				Name:  paramRetryMaxCount,
+				Value: wfapi.AnyStringPtr(c.getTaskRetryParameterValue(name, paramRetryMaxCount)),
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: Optional: I think it'd be cleaner if instead of `getTaskRetryParameterValue` there was a method that returns a slice of `wfapi.Parameter` with all of the retry parameters that are set. This means it could return an empty slice if `set_retry` wasn't called and we can then rely on default values in the arguments/parameters. This would also simplify the code but not needing to have a switch statement on the parameter name.

If you did this approach, I imagine the parameters would added like so:

```go
task.Arguments = append(task.Arguments, c.getTaskRetryParameterValues(name)...)
```
created_at: 2025-04-28T19:22:32Z
author_association: COLLABORATOR
diff_hunk: @@ -292,6 +301,10 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 			Parameters: []wfapi.Parameter{
 				{Name: paramPodSpecPatch},
 				{Name: paramCachedDecision, Default: wfapi.AnyStringPtr("false")},
+				{Name: paramRetryMaxCount},
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: Could you set the defaults here so that the task doesn't need to set them and we can just always rely on the template having the correct defaults?
created_at: 2025-04-28T19:24:49Z
author_association: COLLABORATOR
diff_hunk: @@ -517,38 +548,30 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameterValue(name string, parameter string) string {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
-
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
+		return "0"
 	}
 
-	var argoMaxDuration string
-	backoffMaxDuration := retryPolicy.GetBackoffMaxDuration()
-	if backoffMaxDuration != nil {
-		argoMaxDuration = strconv.FormatInt(backoffMaxDuration.Seconds, 10)
-	}
-	backoff := &wfapi.Backoff{
-		Factor: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: int32(retryPolicy.GetBackoffFactor()),
-		},
-		MaxDuration: argoMaxDuration,
-		Duration:    argoBackOffDuration,
-	}
-	return &wfapi.RetryStrategy{
-		Limit: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: retryPolicy.MaxRetryCount,
-		},
-		Backoff: backoff,
+	value := "0"
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: It seems like `paramRetryMaxCount` should default to `1`. Is there special behavior if it's set to `0`?
created_at: 2025-04-28T19:29:29Z
author_association: COLLABORATOR
diff_hunk: @@ -257,11 +257,25 @@ func (c *workflowCompiler) containerExecutorTask(name string, inputs containerEx
 		Template: c.addContainerExecutorTemplate(name, refName),
 		When:     when,
 		Arguments: wfapi.Arguments{
-			Parameters: []wfapi.Parameter{
-				{Name: paramPodSpecPatch, Value: wfapi.AnyStringPtr(inputs.podSpecPatch)},
-				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")},
-			},
-		},
+			Parameters: []wfapi.Parameter{{
+				Name:  paramPodSpecPatch,
+				Value: wfapi.AnyStringPtr(inputs.podSpecPatch),
+			}, {
+				Name:  paramCachedDecision,
+				Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false"),
+			}, {
+				Name:  paramRetryMaxCount,
+				Value: wfapi.AnyStringPtr(c.getTaskRetryParameterValue(name, paramRetryMaxCount)),
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Added - currently deciding how to refactor further.
created_at: 2025-04-30T21:31:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -292,6 +301,10 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 			Parameters: []wfapi.Parameter{
 				{Name: paramPodSpecPatch},
 				{Name: paramCachedDecision, Default: wfapi.AnyStringPtr("false")},
+				{Name: paramRetryMaxCount},
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-04-30T21:35:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -517,38 +548,30 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameterValue(name string, parameter string) string {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
-
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
+		return "0"
 	}
 
-	var argoMaxDuration string
-	backoffMaxDuration := retryPolicy.GetBackoffMaxDuration()
-	if backoffMaxDuration != nil {
-		argoMaxDuration = strconv.FormatInt(backoffMaxDuration.Seconds, 10)
-	}
-	backoff := &wfapi.Backoff{
-		Factor: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: int32(retryPolicy.GetBackoffFactor()),
-		},
-		MaxDuration: argoMaxDuration,
-		Duration:    argoBackOffDuration,
-	}
-	return &wfapi.RetryStrategy{
-		Limit: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: retryPolicy.MaxRetryCount,
-		},
-		Backoff: backoff,
+	value := "0"
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Initially I was concerned with defaulting this value to 1, because I was setting a retryStrategy on every single template regardless of whether retry parameters were input - and a default "1" value would potentially cause every single template to retry. However, now that I'm using a 2-template approach, (retry and non-retry) there is no chance of this happening, so a default of "1" makes more sense - I have updated this.
created_at: 2025-05-01T17:48:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -257,10 +263,11 @@ func (c *workflowCompiler) containerExecutorTask(name string, inputs containerEx
 		Template: c.addContainerExecutorTemplate(name, refName),
 		When:     when,
 		Arguments: wfapi.Arguments{
-			Parameters: []wfapi.Parameter{
+			Parameters: append([]wfapi.Parameter{
 				{Name: paramPodSpecPatch, Value: wfapi.AnyStringPtr(inputs.podSpecPatch)},
-				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")},
-			},
+				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")}},
+				*c.getTaskRetryParameters(name, templateParameterInstructions{false, false, true})...,
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: This is the only time that `setValue` is set and `setDefault` and `setInputPath` aren't utilized in that code path. For readability, let's include the `setValue` branch directly in in the `containerExecutorTask` method or break it out to its own function.
created_at: 2025-05-02T21:00:59Z
author_association: COLLABORATOR
diff_hunk: @@ -257,10 +263,11 @@ func (c *workflowCompiler) containerExecutorTask(name string, inputs containerEx
 		Template: c.addContainerExecutorTemplate(name, refName),
 		When:     when,
 		Arguments: wfapi.Arguments{
-			Parameters: []wfapi.Parameter{
+			Parameters: append([]wfapi.Parameter{
 				{Name: paramPodSpecPatch, Value: wfapi.AnyStringPtr(inputs.podSpecPatch)},
-				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")},
-			},
+				{Name: paramCachedDecision, Value: wfapi.AnyStringPtr(inputs.cachedDecision), Default: wfapi.AnyStringPtr("false")}},
+				*c.getTaskRetryParameters(name, templateParameterInstructions{false, false, true})...,
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Created separate function `getTaskRetryParametersWithValue()` to handle setValue logic.  I believe this was a cleaner approach than including the setValue logic directly within the  `containerExecutorTask` method, but let me know what you think.
created_at: 2025-05-05T14:31:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -37,6 +37,7 @@
 from kubernetes import client, config, utils
 import yaml
 from modelcar import modelcar
+from samples.v2 import pipeline_with_retry, pipeline_with_retry_all_args, pipeline_with_zero_retry
path: samples/v2/sample_test.py
user: login: mprahl
body: I think this will fix your CI failure.
```suggestion
import pipeline_with_retry, pipeline_with_retry_all_args, pipeline_with_zero_retry
```
created_at: 2025-05-05T15:02:15Z
author_association: COLLABORATOR
diff_hunk: @@ -527,38 +536,55 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string, setDefault bool, setPath bool) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
-
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	for i := range parameters {
+		parameterType := parameters[i].Name
+		// If specified, set "Value" field with input parameter path (e.g. '{{inputs.parameters.retry-max-count}}')
+		if setPath {
+			parameters[i].Value = wfapi.AnyStringPtr(inputParameter(parameters[i].Name))
+		}
+		// If specified, set "Default" field with default parameter value.
+		if setDefault {
+			parameters[i].Default = wfapi.AnyStringPtr("0")
+			if parameterType == paramRetryMaxCount {
+				parameters[i].Default = wfapi.AnyStringPtr("1")
+			}
+		}
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: It seems like getTaskRetryParameters is also setting defaults. I'd prefer either of these options:

1. The parameters are always required and just handle setting default values in `getTaskRetryParameters`.
2. Modify `getTaskRetryParameters` to not return a parameter if it wasn't set in the task.
created_at: 2025-05-05T15:14:56Z
author_association: COLLABORATOR
diff_hunk: @@ -161,6 +162,9 @@ def test(self):
             TestCase(pipeline_func=modelcar.pipeline_modelcar_test),
             TestCase(pipeline_func=parallel_consume_upstream.loop_consume_upstream),
             TestCase(pipeline_func=parallel_after_dependency.loop_with_after_dependency_set),
+            TestCase(pipeline_func=pipeline_with_retry.retry_pipeline),
+            TestCase(pipeline_func=pipeline_with_retry_all_args.retry_pipeline),
+            TestCase(pipeline_func=pipeline_with_zero_retry.retry_pipeline),
path: samples/v2/sample_test.py
user: login: mprahl
body: How do these tests pass since `run_test_case` asserts that the test ran successfully?
created_at: 2025-05-05T15:42:39Z
author_association: COLLABORATOR
diff_hunk: @@ -527,38 +536,55 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string, setDefault bool, setPath bool) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
-
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	for i := range parameters {
+		parameterType := parameters[i].Name
+		// If specified, set "Value" field with input parameter path (e.g. '{{inputs.parameters.retry-max-count}}')
+		if setPath {
+			parameters[i].Value = wfapi.AnyStringPtr(inputParameter(parameters[i].Name))
+		}
+		// If specified, set "Default" field with default parameter value.
+		if setDefault {
+			parameters[i].Default = wfapi.AnyStringPtr("0")
+			if parameterType == paramRetryMaxCount {
+				parameters[i].Default = wfapi.AnyStringPtr("1")
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: I think you might have been right in your original choice that `0` is the correct default for this. I misread the code here in Argo Workflows:
```go
if retryStrategy.Limit != nil && limit != nil && int32(len(node.Children)) > *limit {
```
created_at: 2025-05-05T15:51:15Z
author_association: COLLABORATOR
diff_hunk: @@ -37,6 +37,7 @@
 from kubernetes import client, config, utils
 import yaml
 from modelcar import modelcar
+from samples.v2 import pipeline_with_retry, pipeline_with_retry_all_args, pipeline_with_zero_retry
path: samples/v2/sample_test.py
user: login: alyssacgoins
body: Thanks for pointing that out, updated :+1:
created_at: 2025-05-05T16:24:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,55 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string, setDefault bool, setPath bool) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
-
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	for i := range parameters {
+		parameterType := parameters[i].Name
+		// If specified, set "Value" field with input parameter path (e.g. '{{inputs.parameters.retry-max-count}}')
+		if setPath {
+			parameters[i].Value = wfapi.AnyStringPtr(inputParameter(parameters[i].Name))
+		}
+		// If specified, set "Default" field with default parameter value.
+		if setDefault {
+			parameters[i].Default = wfapi.AnyStringPtr("0")
+			if parameterType == paramRetryMaxCount {
+				parameters[i].Default = wfapi.AnyStringPtr("1")
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated back to a single default value of 0
created_at: 2025-05-05T16:26:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -161,6 +162,9 @@ def test(self):
             TestCase(pipeline_func=modelcar.pipeline_modelcar_test),
             TestCase(pipeline_func=parallel_consume_upstream.loop_consume_upstream),
             TestCase(pipeline_func=parallel_after_dependency.loop_with_after_dependency_set),
+            TestCase(pipeline_func=pipeline_with_retry.retry_pipeline),
+            TestCase(pipeline_func=pipeline_with_retry_all_args.retry_pipeline),
+            TestCase(pipeline_func=pipeline_with_zero_retry.retry_pipeline),
path: samples/v2/sample_test.py
user: login: alyssacgoins
body: Facepalm moment here, I've updated the tests so that they don't raise an error (and now my build is passing too :slightly_smiling_face:) 
created_at: 2025-05-05T22:08:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,55 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string, setDefault bool, setPath bool) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
-
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	for i := range parameters {
+		parameterType := parameters[i].Name
+		// If specified, set "Value" field with input parameter path (e.g. '{{inputs.parameters.retry-max-count}}')
+		if setPath {
+			parameters[i].Value = wfapi.AnyStringPtr(inputParameter(parameters[i].Name))
+		}
+		// If specified, set "Default" field with default parameter value.
+		if setDefault {
+			parameters[i].Default = wfapi.AnyStringPtr("0")
+			if parameterType == paramRetryMaxCount {
+				parameters[i].Default = wfapi.AnyStringPtr("1")
+			}
+		}
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: I went with option 1 - if I understood correctly - I separated the logic for setting input parameter path and setting default value - they are each in separate functions now
created_at: 2025-05-05T22:09:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return &parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) *[]wfapi.Parameter {
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: We don't need to use a pointer here on the returned slice.
```suggestion
func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
```
created_at: 2025-05-07T15:49:30Z
author_association: COLLABORATOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: We don't need to use a pointer here on the returned slice.
```suggestion
func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
```
created_at: 2025-05-07T15:49:44Z
author_association: COLLABORATOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
+		return &[]wfapi.Parameter{}
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: Optional: You can keep it as returning nil once you stop returning a slice pointer as a nil slice can still be read from (e.g. range over it) just not written to until it's initialized in Go.
created_at: 2025-05-07T15:50:54Z
author_association: COLLABORATOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return &parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) *[]wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of all retry inputs containing non-nil values.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(parameters,
+		wfapi.Parameter{Name: paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor())})
+	if retryPolicy.GetBackoffMaxDuration() != nil {
+		retryBackOffMaxDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffMaxDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffMaxDuration,
+				Value: retryBackOffMaxDuration})
+	}
+	return &parameters
+}
 
-	var argoMaxDuration string
-	backoffMaxDuration := retryPolicy.GetBackoffMaxDuration()
-	if backoffMaxDuration != nil {
-		argoMaxDuration = strconv.FormatInt(backoffMaxDuration.Seconds, 10)
-	}
-	backoff := &wfapi.Backoff{
-		Factor: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: int32(retryPolicy.GetBackoffFactor()),
-		},
-		MaxDuration: argoMaxDuration,
-		Duration:    argoBackOffDuration,
+func (c *workflowCompiler) addParameterDefault(parameters []wfapi.Parameter, defaultValue string) *[]wfapi.Parameter {
path: backend/src/v2/compiler/argocompiler/container.go
user: login: mprahl
body: No need to return a pointer here.
```suggestion
func (c *workflowCompiler) addParameterDefault(parameters []wfapi.Parameter, defaultValue string) []wfapi.Parameter {
```
created_at: 2025-05-07T15:52:12Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,31 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op_retries(retries: str) -> str:
+    print(retries)
+    if retries != '2':
+        raise Exception('Number of retries has not reached three.')
path: samples/v2/pipeline_with_retry.py
user: login: mprahl
body: ```suggestion
        raise Exception('Number of retries has not reached two yet.')
```
created_at: 2025-05-07T15:55:08Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,31 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op_retries(retries: str) -> str:
path: samples/v2/pipeline_with_retry.py
user: login: mprahl
body: Optional: It doesn't really print anything anymore :laughing: 
created_at: 2025-05-07T15:56:07Z
author_association: COLLABORATOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return &parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) *[]wfapi.Parameter {
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T16:50:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T16:50:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
+		return &[]wfapi.Parameter{}
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Makes sense, thanks for pointing that out - reverted back to returning nil
created_at: 2025-05-07T16:51:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) *[]wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
-		return nil
-	}
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return &parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) *[]wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return &[]wfapi.Parameter{}
+	}
+	// Create slice of all retry inputs containing non-nil values.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(parameters,
+		wfapi.Parameter{Name: paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor())})
+	if retryPolicy.GetBackoffMaxDuration() != nil {
+		retryBackOffMaxDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffMaxDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffMaxDuration,
+				Value: retryBackOffMaxDuration})
+	}
+	return &parameters
+}
 
-	var argoMaxDuration string
-	backoffMaxDuration := retryPolicy.GetBackoffMaxDuration()
-	if backoffMaxDuration != nil {
-		argoMaxDuration = strconv.FormatInt(backoffMaxDuration.Seconds, 10)
-	}
-	backoff := &wfapi.Backoff{
-		Factor: &intstr.IntOrString{
-			Type:   intstr.Int,
-			IntVal: int32(retryPolicy.GetBackoffFactor()),
-		},
-		MaxDuration: argoMaxDuration,
-		Duration:    argoBackOffDuration,
+func (c *workflowCompiler) addParameterDefault(parameters []wfapi.Parameter, defaultValue string) *[]wfapi.Parameter {
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T16:51:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,31 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op_retries(retries: str) -> str:
+    print(retries)
+    if retries != '2':
+        raise Exception('Number of retries has not reached three.')
path: samples/v2/pipeline_with_retry.py
user: login: alyssacgoins
body: Fixed
created_at: 2025-05-07T16:53:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,31 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op_retries(retries: str) -> str:
path: samples/v2/pipeline_with_retry.py
user: login: alyssacgoins
body: Refactored this component to `verify_retries()` which returns a boolean value
created_at: 2025-05-07T17:02:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,30 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def verify_retries(retries: str) -> bool:
+    if retries != '2':
+        raise Exception('Number of retries has not reached two yet.')
+    return True
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op(text: str) -> str:
+    print(text)
+    return text
+
+
+@dsl.pipeline()
+def retry_pipeline():
+    task1 = verify_retries(retries="{{retries}}").set_retry(num_retries=2)
+    task2 = print_op(text='test').set_retry(num_retries=2, backoff_duration="1 second", backoff_factor=1, backoff_max_duration="1 second")
+    task3 = print_op(text='test').set_retry(num_retries=0)
+
+if __name__ == '__main__':
+    compiler.Compiler.compile(
+        pipeline_func=retry_pipeline,
+        package_path='pipeline_with_retry.yaml')
path: samples/v2/pipeline_with_retry.py
user: login: gmfrasca
body: code formatting/lint: add newline to end
created_at: 2025-05-07T17:52:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,102 @@
+{
+  "pipelineSpec": {
+    "components": {
+      "comp-hello-world": {
+        "executorLabel": "exec-hello-world",
+        "inputDefinitions": {
+          "parameters": {
+            "text": {
+              "type": "STRING"
+            }
+          }
+        }
+      }
+    },
+    "deploymentSpec": {
+      "executors": {
+        "exec-hello-world": {
+          "container": {
+            "args": [
+              "--text",
+              "{{$.inputs.parameters['text']}}"
+            ],
+            "command": [
+              "sh",
+              "-ec",
+              "program_path=$(mktemp)\nprintf \"%s\" \"$0\" > \"$program_path\"\npython3 -u \"$program_path\" \"$@\"\n",
+              "def hello_world(text):\n    print(text)\n    return text\n\nimport argparse\n_parser = argparse.ArgumentParser(prog='Hello world', description='')\n_parser.add_argument(\"--text\", dest=\"text\", type=str, required=True, default=argparse.SUPPRESS)\n_parsed_args = vars(_parser.parse_args())\n\n_outputs = hello_world(**_parsed_args)\n"
+            ],
+            "image": "python:3.9"
+          }
+        }
+      }
+    },
+    "pipelineInfo": {
+      "name": "namespace/n1/pipeline/hello-world"
+    },
+    "root": {
+      "dag": {
+        "tasks": {
+          "hello-world": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "retryPolicy": {
+              "backoffFactor": 2,
+              "backoffMaxDuration": "3600s",
+              "backoffDuration": "1s",
+              "maxRetryCount": 2
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world"
+            }
+          },
+          "hello-world-non-retry": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world-2"
+            }
+          }
+        }
+      },
+      "inputDefinitions": {
+        "parameters": {
+          "text": {
+            "type": "STRING"
+          }
+        }
+      }
+    },
+    "schemaVersion": "2.0.0",
+    "sdkVersion": "kfp-1.6.5"
+  },
+  "runtimeConfig": {
+    "parameters": {
+      "text": {
+        "stringValue": "hi there"
+      }
+    }
+  }
+}
path: backend/src/v2/compiler/testdata/hello_world_with_retry_all_args.json
user: login: gmfrasca
body: code formatting/lint: add newline to end

created_at: 2025-05-07T17:52:43Z
author_association: MEMBER
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: ```suggestion
	parameters := append(
	    []wfapi.Parameter{},
     	    wfapi.Parameter{
		Name: paramRetryMaxCount,
		Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount),
	    },
	)
```
created_at: 2025-05-07T18:04:31Z
author_association: MEMBER
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: ```suggestion
		parameters = append(
		    parameters,
		    wfapi.Parameter{
			Name: paramRetryBackOffDuration,
			Value: retryBackOffDuration,
		    },
		)
```
created_at: 2025-05-07T18:10:05Z
author_association: MEMBER
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(parameters,
+		wfapi.Parameter{Name: paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor())})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: ```suggestion
	parameters = append(
	    parameters,
	    wfapi.Parameter{
		Name: paramRetryBackOffFactor,
		Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor()),
	    },
	)
```
created_at: 2025-05-07T18:12:38Z
author_association: MEMBER
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(parameters,
+		wfapi.Parameter{Name: paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor())})
+	if retryPolicy.GetBackoffMaxDuration() != nil {
+		retryBackOffMaxDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffMaxDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffMaxDuration,
+				Value: retryBackOffMaxDuration})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: ```suggestion
		parameters = append(
		    parameters,
		    wfapi.Parameter{
		        Name: paramRetryBackOffMaxDuration,
			Value: retryBackOffMaxDuration,
	   	    },
		)
```
created_at: 2025-05-07T18:13:36Z
author_association: MEMBER
diff_hunk: @@ -289,8 +290,8 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 	nameContainerImpl := "system-container-impl"
 	taskRetrySpec := c.getTaskRetryPolicySpec(name)
 	if taskRetrySpec != nil {
-		nameContainerExecutor = name + "-" + nameContainerExecutor
-		nameContainerImpl = name + "-" + "system-container-impl"
+		nameContainerExecutor = "retry" + "-" + nameContainerExecutor
+		nameContainerImpl = "retry" + "-" + "system-container-impl"
path: backend/src/v2/compiler/argocompiler/container.go
user: login: gmfrasca
body: ```suggestion
		nameContainerExecutor = "retry-" + nameContainerExecutor
		nameContainerImpl = "retry-" + nameContainerImpl
```
created_at: 2025-05-07T18:17:34Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,30 @@
+import os
+
+from kfp import dsl
+
+from kfp import compiler
+
+_KFP_PACKAGE_PATH = os.getenv('KFP_PACKAGE_PATH')
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def verify_retries(retries: str) -> bool:
+    if retries != '2':
+        raise Exception('Number of retries has not reached two yet.')
+    return True
+
+@dsl.component(kfp_package_path=_KFP_PACKAGE_PATH)
+def print_op(text: str) -> str:
+    print(text)
+    return text
+
+
+@dsl.pipeline()
+def retry_pipeline():
+    task1 = verify_retries(retries="{{retries}}").set_retry(num_retries=2)
+    task2 = print_op(text='test').set_retry(num_retries=2, backoff_duration="1 second", backoff_factor=1, backoff_max_duration="1 second")
+    task3 = print_op(text='test').set_retry(num_retries=0)
+
+if __name__ == '__main__':
+    compiler.Compiler.compile(
+        pipeline_func=retry_pipeline,
+        package_path='pipeline_with_retry.yaml')
path: samples/v2/pipeline_with_retry.py
user: login: alyssacgoins
body: Fixed
created_at: 2025-05-07T18:18:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+{
+  "pipelineSpec": {
+    "components": {
+      "comp-hello-world": {
+        "executorLabel": "exec-hello-world",
+        "inputDefinitions": {
+          "parameters": {
+            "text": {
+              "type": "STRING"
+            }
+          }
+        }
+      }
+    },
+    "deploymentSpec": {
+      "executors": {
+        "exec-hello-world": {
+          "container": {
+            "args": [
+              "--text",
+              "{{$.inputs.parameters['text']}}"
+            ],
+            "command": [
+              "sh",
+              "-ec",
+              "program_path=$(mktemp)\nprintf \"%s\" \"$0\" > \"$program_path\"\npython3 -u \"$program_path\" \"$@\"\n",
+              "def hello_world(text):\n    print(text)\n    return text\n\nimport argparse\n_parser = argparse.ArgumentParser(prog='Hello world', description='')\n_parser.add_argument(\"--text\", dest=\"text\", type=str, required=True, default=argparse.SUPPRESS)\n_parsed_args = vars(_parser.parse_args())\n\n_outputs = hello_world(**_parsed_args)\n"
+            ],
+            "image": "python:3.9"
+          }
+        }
+      }
+    },
+    "pipelineInfo": {
+      "name": "namespace/n1/pipeline/hello-world"
+    },
+    "root": {
+      "dag": {
+        "tasks": {
+          "hello-world": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "retryPolicy": {
+              "backoffFactor": 2,
+              "backoffMaxDuration": "3600s",
+              "backoffDuration": "1s",
+              "maxRetryCount": 2
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world"
+            }
+          },
+          "hello-world-non-retry": {
+            "cachingOptions": {
+              "enableCache": true
+            },
+            "componentRef": {
+              "name": "comp-hello-world"
+            },
+            "inputs": {
+              "parameters": {
+                "text": {
+                  "componentInputParameter": "text"
+                }
+              }
+            },
+            "taskInfo": {
+              "name": "hello-world-2"
+            }
+          }
+        }
+      },
+      "inputDefinitions": {
+        "parameters": {
+          "text": {
+            "type": "STRING"
+          }
+        }
+      }
+    },
+    "schemaVersion": "2.0.0",
+    "sdkVersion": "kfp-1.6.5"
+  },
+  "runtimeConfig": {
+    "parameters": {
+      "text": {
+        "stringValue": "hi there"
+      }
+    }
+  }
+}
path: backend/src/v2/compiler/testdata/hello_world_with_retry_all_args.json
user: login: alyssacgoins
body: Fixed. Applied this change to hello_world_with_retry.json as well
created_at: 2025-05-07T18:18:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T18:44:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T18:44:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(parameters,
+		wfapi.Parameter{Name: paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor())})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T18:44:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,38 +536,61 @@ func (c *workflowCompiler) getTaskRetryPolicySpec(name string) *pipelinespec.Pip
 	return taskSpec.RetryPolicy
 }
 
-func (c *workflowCompiler) getTaskRetryStrategy(name string) *wfapi.RetryStrategy {
+func (c *workflowCompiler) getTaskRetryParameters(name string) []wfapi.Parameter {
 	retryPolicy := c.getTaskRetryPolicySpec(name)
 	if retryPolicy == nil {
 		return nil
 	}
+	// Create slice of parameters with "Name" field set.
+	parameters := []wfapi.Parameter{
+		{Name: paramRetryMaxCount},
+		{Name: paramRetryBackOffDuration},
+		{Name: paramRetryBackOffFactor},
+		{Name: paramRetryBackOffMaxDuration}}
+	return parameters
+}
 
-	argoBackOffDuration := "0"
-	backoffDuration := retryPolicy.GetBackoffDuration()
-	if backoffDuration != nil {
-		argoBackOffDuration = strconv.FormatInt(backoffDuration.Seconds, 10)
-	}
+func (c *workflowCompiler) getTaskRetryParametersWithValues(name string) []wfapi.Parameter {
+	retryPolicy := c.getTaskRetryPolicySpec(name)
+	if retryPolicy == nil {
+		return []wfapi.Parameter{}
+	}
+	// Create slice of all non-nil retryPolicy fields.
+	parameters := append([]wfapi.Parameter{},
+		wfapi.Parameter{Name: paramRetryMaxCount,
+			Value: wfapi.AnyStringPtr(retryPolicy.MaxRetryCount)})
+	if retryPolicy.GetBackoffDuration() != nil {
+		retryBackOffDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffDuration,
+				Value: retryBackOffDuration})
+	}
+	parameters = append(parameters,
+		wfapi.Parameter{Name: paramRetryBackOffFactor,
+			Value: wfapi.AnyStringPtr(retryPolicy.GetBackoffFactor())})
+	if retryPolicy.GetBackoffMaxDuration() != nil {
+		retryBackOffMaxDuration := wfapi.AnyStringPtr(retryPolicy.GetBackoffMaxDuration().Seconds)
+		parameters = append(parameters,
+			wfapi.Parameter{Name: paramRetryBackOffMaxDuration,
+				Value: retryBackOffMaxDuration})
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T18:45:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -289,8 +290,8 @@ func (c *workflowCompiler) addContainerExecutorTemplate(name string, refName str
 	nameContainerImpl := "system-container-impl"
 	taskRetrySpec := c.getTaskRetryPolicySpec(name)
 	if taskRetrySpec != nil {
-		nameContainerExecutor = name + "-" + nameContainerExecutor
-		nameContainerImpl = name + "-" + "system-container-impl"
+		nameContainerExecutor = "retry" + "-" + nameContainerExecutor
+		nameContainerImpl = "retry" + "-" + "system-container-impl"
path: backend/src/v2/compiler/argocompiler/container.go
user: login: alyssacgoins
body: Updated
created_at: 2025-05-07T18:45:08Z
author_association: CONTRIBUTOR

PR comments:
user: login: google-oss-prow[bot]
created_at: 2025-04-25T14:36:20Z
body: Hi @alyssacgoins. Thanks for your PR.

I'm waiting for a [kubeflow](https://github.com/orgs/kubeflow/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://www.kubeflow.org/docs/about/contributing/) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubeflow%2Fpipelines).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-25T14:36:34Z
body: 🚫 This command cannot be processed. Only organization members or owners can use the commands.
author_association: NONE
user: login: gmfrasca
created_at: 2025-04-25T17:46:11Z
body: Thanks for the PR @alyssacgoins !  Looks like there's some extra merge and dev commits in the history - Could you please rebase/squash your commits into a single (or more, if logical) commit(s)?  Thanks!
author_association: MEMBER
user: login: alyssacgoins
created_at: 2025-04-25T19:45:51Z
body: > Thanks for the PR @alyssacgoins ! Looks like there's some extra merge and dev commits in the history - Could you please rebase/squash your commits into a single (or more, if logical) commit(s)? Thanks!

Yep, squashed down to one commit :+1:
author_association: CONTRIBUTOR
user: login: mprahl
created_at: 2025-04-28T19:51:02Z
body: @alyssacgoins have you tested the case where a pipeline fails and `set_retry` isn't set on the component? I'm wondering how the default parameter values of `"0"` are handled by Argo Workflows. Looking at the Argo Workflows code, it executes a different code path if retryStrategy is not nil and since default values are being passed, it is no longer nil due to Argo Workflows usage of pointers on values and thus `omitempty` on JSON doesn't take effect if a default Go value is passed (e.g. "" for string or 0 for int).
author_association: COLLABORATOR
user: login: mprahl
created_at: 2025-04-28T19:59:29Z
body: I think we should have at least one integration test that uses `set_retry` with all the options to ensure Argo Workflows accepts the values passed in as parameters by the KFP Argo compiler.
author_association: COLLABORATOR
user: login: alyssacgoins
created_at: 2025-04-30T20:39:14Z
body: > @alyssacgoins have you tested the case where a pipeline fails and `set_retry` isn't set on the component? I'm wondering how the default parameter values of `"0"` are handled by Argo Workflows. Looking at the Argo Workflows code, it executes a different code path if retryStrategy is not nil and since default values are being passed, it is no longer nil due to Argo Workflows usage of pointers on values and thus `omitempty` on JSON doesn't take effect if a default Go value is passed (e.g. "" for string or 0 for int).

I added this test case (samples/v2/pipeline_failure_no_retry.py) - though now I'm wondering if it's necessary because I implemented both a retry-enabled and non-retry-enabled template (therefore retryStrategy would be nil on the template produced by this test)
author_association: CONTRIBUTOR
user: login: alyssacgoins
created_at: 2025-04-30T20:40:44Z
body: > I think we should have at least one integration test that uses `set_retry` with all the options to ensure Argo Workflows accepts the values passed in as parameters by the KFP Argo compiler.

Added - samples/v2/pipeline_with_retry_all_args.py
author_association: CONTRIBUTOR
user: login: mprahl
created_at: 2025-05-07T19:11:08Z
body: /lgtm
/approve
author_association: COLLABORATOR
user: login: HumairAK
created_at: 2025-05-07T19:13:31Z
body: /approve
author_association: COLLABORATOR
user: login: google-oss-prow[bot]
created_at: 2025-05-07T19:13:38Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubeflow/pipelines/pull/11861#issuecomment-2859940033" title="Approved">HumairAK</a>*, *<a href="https://github.com/kubeflow/pipelines/pull/11861#issuecomment-2859934347" title="Approved">mprahl</a>*

The full list of commands accepted by this bot can be found [here](https://oss.gprow.dev/command-help?repo=kubeflow%2Fpipelines).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[backend/OWNERS](https://github.com/kubeflow/pipelines/blob/master/backend/OWNERS)~~ [HumairAK,mprahl]
- ~~[samples/OWNERS](https://github.com/kubeflow/pipelines/blob/master/samples/OWNERS)~~ [HumairAK]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE