Repository: kubeflow/kfp-tekton (language: python)

Issue:
Pipelineloop need to set maximum recursion stack in order to prevent endless loops that crashes the api-server
/kind bug

**What steps did you take and what happened:**
In recursion, there could be endless loops that creates too many pipelinerun and crashes the api-server in the master node. We need to set a maximum recursion stack to prevent this.


**What did you expect to happen:**


**Additional information:**
[Miscellaneous information that will assist in solving the issue.]


**Environment:**

* Python Version (use `python --version`):
* SDK Version: 
* Tekton Version (use `tkn version`):
* Kubernetes Version (use `kubectl version`):
* OS (e.g. from `/etc/os-release`):



Discussion / hints:
/assign 

Fix patch:
diff --git a/tekton-catalog/pipeline-loops/pkg/apis/pipelineloop/v1alpha1/pipelineloop_types.go b/tekton-catalog/pipeline-loops/pkg/apis/pipelineloop/v1alpha1/pipelineloop_types.go
index db47d29dbe..38644d99d7 100644
--- a/tekton-catalog/pipeline-loops/pkg/apis/pipelineloop/v1alpha1/pipelineloop_types.go
+++ b/tekton-catalog/pipeline-loops/pkg/apis/pipelineloop/v1alpha1/pipelineloop_types.go
@@ -97,6 +97,9 @@ const (
 	// PipelineLoopRunReasonFailed indicates that one of the TaskRuns created from the Run failed
 	PipelineLoopRunReasonFailed PipelineLoopRunReason = "Failed"
 
+	// PipelineLoopRunReasonRecursionLimitExceeded indicates that one of the TaskRuns created from the Run has exceeded stack limit
+	PipelineLoopRunReasonStackLimitExceeded PipelineLoopRunReason = "NestedCallStackLimitExceeded"
+
 	// PipelineLoopRunReasonSucceeded indicates that all of the TaskRuns created from the Run completed successfully
 	PipelineLoopRunReasonSucceeded PipelineLoopRunReason = "Succeeded"
 
diff --git a/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun.go b/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun.go
index 0d8a9925cf..8a1f1046b1 100644
--- a/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun.go
+++ b/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun.go
@@ -78,6 +78,10 @@ const (
 
 	// LabelKeyWorkflowRunId is the label identifier a pipelinerun is managed by the Kubeflow Pipeline persistent agent.
 	LabelKeyWorkflowRunId = "pipeline/runid"
+
+	DefaultNestedStackDepth = 30
+
+	MaxNestedStackDepthKey = "maxNestedStackDepth"
 )
 
 // Reconciler implements controller.Reconciler for Configuration resources.
@@ -211,6 +215,71 @@ func EnableCustomTaskFeatureFlag(ctx context.Context) context.Context {
 	return config.ToContext(ctx, c)
 }
 
+// Check if this pipelineLoop starts other pipelineLoop(s)
+func isNestedPipelineLoop(pipelineLoopSpec *pipelineloopv1alpha1.PipelineLoopSpec) bool {
+	if pipelineLoopSpec.PipelineSpec == nil {
+		return false
+	}
+	for _, t := range pipelineLoopSpec.PipelineSpec.Tasks {
+		if t.TaskSpec != nil {
+			if t.TaskSpec.Kind == "PipelineLoop" {
+				return true
+			}
+		} else if t.TaskRef != nil {
+			if t.TaskRef.Kind == "PipelineLoop" {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func getMaxNestedStackDepth(pipelineLoopMeta *metav1.ObjectMeta) (int, error) {
+	maxNestedStackDepth := pipelineLoopMeta.Annotations[MaxNestedStackDepthKey]
+	if maxNestedStackDepth != "" {
+		atoi, err := strconv.Atoi(maxNestedStackDepth)
+		return atoi, err
+	}
+	return DefaultNestedStackDepth, nil
+}
+
+func (c *Reconciler) setMaxNestedStackDepth(ctx context.Context, pipelineLoopSpec *pipelineloopv1alpha1.PipelineLoopSpec, run *v1alpha1.Run, depth int) {
+	logger := logging.FromContext(ctx)
+
+	if pipelineLoopSpec.PipelineSpec == nil {
+		return
+	}
+	for k, t := range pipelineLoopSpec.PipelineSpec.Tasks {
+		if t.TaskSpec != nil {
+			if t.TaskSpec.Kind == "PipelineLoop" {
+				if len(t.TaskSpec.Metadata.Annotations) == 0 {
+					t.TaskSpec.Metadata.Annotations = map[string]string{MaxNestedStackDepthKey: fmt.Sprint(depth)}
+				} else {
+					t.TaskSpec.Metadata.Annotations[MaxNestedStackDepthKey] = fmt.Sprint(depth)
+				}
+				pipelineLoopSpec.PipelineSpec.Tasks[k].TaskSpec.Metadata.Annotations = map[string]string{MaxNestedStackDepthKey: fmt.Sprint(depth)}
+			}
+		} else if t.TaskRef != nil {
+			if t.TaskRef.Kind == "PipelineLoop" {
+				tl, err := c.pipelineloopClientSet.CustomV1alpha1().PipelineLoops(run.Namespace).Get(ctx, t.TaskRef.Name, metav1.GetOptions{})
+				if err == nil && tl != nil {
+					if len(tl.ObjectMeta.Annotations) == 0 {
+						tl.ObjectMeta.Annotations = map[string]string{MaxNestedStackDepthKey: fmt.Sprint(depth)}
+					} else {
+						tl.ObjectMeta.Annotations[MaxNestedStackDepthKey] = fmt.Sprint(depth)
+					}
+					_, err := c.pipelineloopClientSet.CustomV1alpha1().PipelineLoops(run.Namespace).Update(ctx, tl, metav1.UpdateOptions{})
+					if err != nil {
+						logger.Errorf("Error while updating pipelineloop nested stack depth, %v", err)
+					}
+				} else if err != nil {
+					logger.Warnf("Unable to fetch pipelineLoop wiht name: %s error: %v", t.TaskRef.Name, err)
+				}
+			}
+		}
+	}
+}
+
 func (c *Reconciler) reconcile(ctx context.Context, run *v1alpha1.Run, status *pipelineloopv1alpha1.PipelineLoopRunStatus) error {
 	ctx = EnableCustomTaskFeatureFlag(ctx)
 	logger := logging.FromContext(ctx)
@@ -220,7 +289,6 @@ func (c *Reconciler) reconcile(ctx context.Context, run *v1alpha1.Run, status *p
 	if err != nil {
 		return nil
 	}
-
 	// Store the fetched PipelineLoopSpec on the Run for auditing
 	storePipelineLoopSpec(status, pipelineLoopSpec)
 
@@ -382,6 +450,21 @@ func (c *Reconciler) reconcile(ctx context.Context, run *v1alpha1.Run, status *p
 
 	// Create PipelineRun to run this iteration based on parallelism
 	for i := 0; i < actualParallelism-len(currentRunningPrs); i++ {
+		if isNestedPipelineLoop(pipelineLoopSpec) {
+			maxNestedStackDepth, err := getMaxNestedStackDepth(pipelineLoopMeta)
+			if err != nil {
+				logger.Errorf("Error parsing max nested stack depth value: %v", err.Error())
+				maxNestedStackDepth = DefaultNestedStackDepth
+			}
+			if maxNestedStackDepth > 0 {
+				maxNestedStackDepth = maxNestedStackDepth - 1
+				c.setMaxNestedStackDepth(ctx, pipelineLoopSpec, run, maxNestedStackDepth)
+			} else if maxNestedStackDepth <= 0 {
+				run.Status.MarkRunFailed(pipelineloopv1alpha1.PipelineLoopRunReasonStackLimitExceeded.String(), "nested stack depth limit reached.")
+				return nil
+			}
+		}
+
 		pr, err := c.createPipelineRun(ctx, logger, pipelineLoopSpec, run, nextIteration, iterationElements)
 		if err != nil {
 			return fmt.Errorf("error creating PipelineRun from Run %s: %w", run.Name, err)


Test patch:
diff --git a/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun_test.go b/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun_test.go
index b4701daadf..58dc99af50 100644
--- a/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun_test.go
+++ b/tekton-catalog/pipeline-loops/pkg/reconciler/pipelinelooprun/pipelinelooprun_test.go
@@ -383,6 +383,12 @@ var nestedPipeline = &v1beta1.Pipeline{
 	},
 }
 
+func setPipelineNestedStackDepth(pipeline *v1beta1.Pipeline, depth int) *v1beta1.Pipeline {
+	pl := pipeline.DeepCopy()
+	pl.Spec.Tasks[0].TaskSpec.Metadata.Annotations = map[string]string{MaxNestedStackDepthKey: fmt.Sprint(depth)}
+	return pl
+}
+
 var paraPipelineLoop = &pipelineloopv1alpha1.PipelineLoop{
 	ObjectMeta: metav1.ObjectMeta{Name: "para-pipelineloop", Namespace: "foo"},
 	Spec: pipelineloopv1alpha1.PipelineLoopSpec{
@@ -408,6 +414,12 @@ var nestedPipelineLoop = &pipelineloopv1alpha1.PipelineLoop{
 	},
 }
 
+func setPipelineLoopNestedStackDepth(pl *pipelineloopv1alpha1.PipelineLoop, depth int) *pipelineloopv1alpha1.PipelineLoop {
+	plCopy := pl.DeepCopy()
+	plCopy.Spec.PipelineSpec = &setPipelineNestedStackDepth(nestedPipeline, depth).Spec
+	return plCopy
+}
+
 var aPipelineLoopWithInlineTask = &pipelineloopv1alpha1.PipelineLoop{
 	ObjectMeta: metav1.ObjectMeta{Name: "a-pipelineloop-with-inline-task", Namespace: "foo"},
 	Spec: pipelineloopv1alpha1.PipelineLoopSpec{
@@ -528,6 +540,12 @@ var runNestedPipelineLoop = &v1alpha1.Run{
 	},
 }
 
+func setRunNestedStackDepth(run *v1alpha1.Run, depth int) *v1alpha1.Run {
+	r := run.DeepCopy()
+	r.Spec.Spec.Metadata.Annotations = map[string]string{MaxNestedStackDepthKey: fmt.Sprint(depth)}
+	return r
+}
+
 var paraRunPipelineLoop = &v1alpha1.Run{
 	ObjectMeta: metav1.ObjectMeta{
 		Name:      "run-pipelineloop",
@@ -1236,7 +1254,7 @@ var expectedNestedPipelineRun = &v1beta1.PipelineRun{
 		},
 	},
 	Spec: v1beta1.PipelineRunSpec{
-		PipelineSpec: &nestedPipeline.Spec,
+		PipelineSpec: &setPipelineNestedStackDepth(nestedPipeline, 29).Spec,
 		Params: []v1beta1.Param{{
 			Name:  "additional-parameter",
 			Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "stuff"},
@@ -1364,106 +1382,115 @@ func TestReconcilePipelineLoopRun(t *testing.T) {
 		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
 		expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunIteration1},
 		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-	},
-		{
-			name:                 "Reconcile a new run with a pipelineloop and a string params with whitespace separator",
-			pipeline:             aPipeline,
-			pipelineloop:         aPipelineLoop,
-			run:                  runPipelineLoopWithSpaceSeparatorParams,
-			pipelineruns:         []*v1beta1.PipelineRun{},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunIteration1},
-			expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-		}, {
-			name:                 "Reconcile a new run with iterateNumeric defined",
-			pipeline:             nPipeline,
-			pipelineloop:         nPipelineLoop,
-			run:                  runPipelineLoopWithIterateNumeric,
-			pipelineruns:         []*v1beta1.PipelineRun{},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunIterateNumeric1},
-			expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-		}, {
-			name:                 "Reconcile a new run with a pipelineloop that contains an inline task",
-			pipelineloop:         aPipelineLoopWithInlineTask,
-			run:                  runPipelineLoopWithInlineTask,
-			pipelineruns:         []*v1beta1.PipelineRun{},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunWithInlineTaskIteration1},
-			expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-		}, {
-			name:                 "Reconcile a new run with a pipelineloop that contains a workspace",
-			pipeline:             aPipeline,
-			pipelineloop:         wsPipelineLoop,
-			run:                  runWsPipelineLoop,
-			pipelineruns:         []*v1beta1.PipelineRun{},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunWithWorkSpace},
-			expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-		}, {
-			name:                 "Reconcile a run after all PipelineRuns have succeeded",
-			pipeline:             aPipeline,
-			pipelineloop:         aPipelineLoop,
-			run:                  loopRunning(runPipelineLoop),
-			pipelineruns:         []*v1beta1.PipelineRun{successful(expectedPipelineRunIteration1), successful(expectedPipelineRunIteration2)},
-			expectedStatus:       corev1.ConditionTrue,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonSucceeded,
-			expectedPipelineruns: []*v1beta1.PipelineRun{successful(expectedPipelineRunIteration1), successful(expectedPipelineRunIteration2)},
-			expectedEvents:       []string{"Normal Succeeded All PipelineRuns completed successfully"},
-		}, {
-			name:                 "Reconcile a run after the first PipelineRun has failed",
-			pipeline:             aPipeline,
-			pipelineloop:         aPipelineLoop,
-			run:                  loopRunning(runPipelineLoop),
-			pipelineruns:         []*v1beta1.PipelineRun{failed(expectedPipelineRunIteration1)},
-			expectedStatus:       corev1.ConditionFalse,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonFailed,
-			expectedPipelineruns: []*v1beta1.PipelineRun{failed(expectedPipelineRunIteration1)},
-			expectedEvents:       []string{"Warning Failed PipelineRun " + expectedPipelineRunIteration1.Name + " has failed"},
-		}, {
-			name:                 "Reconcile a run with retries after the first PipelineRun has failed",
-			pipeline:             aPipeline,
-			pipelineloop:         aPipelineLoop,
-			run:                  loopRunning(setRetries(runPipelineLoop, 1)),
-			pipelineruns:         []*v1beta1.PipelineRun{failed(expectedPipelineRunFailed)},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{setDeleted(failed(expectedPipelineRunFailed)), expectedPipelineRunRetry},
-		}, {
-			name:                 "Reconcile a new run with a pipelineloop with Parallelism specified",
-			pipeline:             paraPipeline,
-			pipelineloop:         paraPipelineLoop,
-			run:                  paraRunPipelineLoop,
-			pipelineruns:         []*v1beta1.PipelineRun{},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{expectedParaPipelineRun, expectedParaPipelineRun1},
-			expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-		}, {
-			name:                 "Reconcile a new run with a nested pipelineloop",
-			pipeline:             nestedPipeline,
-			pipelineloop:         nestedPipelineLoop,
-			run:                  runNestedPipelineLoop,
-			pipelineruns:         []*v1beta1.PipelineRun{},
-			expectedStatus:       corev1.ConditionUnknown,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
-			expectedPipelineruns: []*v1beta1.PipelineRun{expectedNestedPipelineRun},
-			expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
-		}, {
-			name:                 "Reconcile a run with condition pipelinerun, and the first PipelineRun condition check failed",
-			pipeline:             aPipeline,
-			pipelineloop:         aPipelineLoop,
-			run:                  loopRunning(conditionRunPipelineLoop),
-			pipelineruns:         []*v1beta1.PipelineRun{successfulWithSkipedTasks(expectedConditionPipelineRunIteration1)},
-			expectedStatus:       corev1.ConditionTrue,
-			expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonSucceeded,
-			expectedPipelineruns: []*v1beta1.PipelineRun{successfulWithSkipedTasks(expectedConditionPipelineRunIteration1)},
-			expectedEvents:       []string{"Normal Succeeded PipelineRuns completed successfully with the conditions are met"},
-		}}
+	}, {
+		name:                 "Reconcile a new run with a pipelineloop and a string params with whitespace separator",
+		pipeline:             aPipeline,
+		pipelineloop:         aPipelineLoop,
+		run:                  runPipelineLoopWithSpaceSeparatorParams,
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunIteration1},
+		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
+	}, {
+		name:                 "Reconcile a new run with iterateNumeric defined",
+		pipeline:             nPipeline,
+		pipelineloop:         nPipelineLoop,
+		run:                  runPipelineLoopWithIterateNumeric,
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunIterateNumeric1},
+		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
+	}, {
+		name:                 "Reconcile a new run with a pipelineloop that contains an inline task",
+		pipelineloop:         aPipelineLoopWithInlineTask,
+		run:                  runPipelineLoopWithInlineTask,
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunWithInlineTaskIteration1},
+		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
+	}, {
+		name:                 "Reconcile a new run with a pipelineloop that contains a workspace",
+		pipeline:             aPipeline,
+		pipelineloop:         wsPipelineLoop,
+		run:                  runWsPipelineLoop,
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{expectedPipelineRunWithWorkSpace},
+		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
+	}, {
+		name:                 "Reconcile a run after all PipelineRuns have succeeded",
+		pipeline:             aPipeline,
+		pipelineloop:         aPipelineLoop,
+		run:                  loopRunning(runPipelineLoop),
+		pipelineruns:         []*v1beta1.PipelineRun{successful(expectedPipelineRunIteration1), successful(expectedPipelineRunIteration2)},
+		expectedStatus:       corev1.ConditionTrue,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonSucceeded,
+		expectedPipelineruns: []*v1beta1.PipelineRun{successful(expectedPipelineRunIteration1), successful(expectedPipelineRunIteration2)},
+		expectedEvents:       []string{"Normal Succeeded All PipelineRuns completed successfully"},
+	}, {
+		name:                 "Reconcile a run after the first PipelineRun has failed",
+		pipeline:             aPipeline,
+		pipelineloop:         aPipelineLoop,
+		run:                  loopRunning(runPipelineLoop),
+		pipelineruns:         []*v1beta1.PipelineRun{failed(expectedPipelineRunIteration1)},
+		expectedStatus:       corev1.ConditionFalse,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonFailed,
+		expectedPipelineruns: []*v1beta1.PipelineRun{failed(expectedPipelineRunIteration1)},
+		expectedEvents:       []string{"Warning Failed PipelineRun " + expectedPipelineRunIteration1.Name + " has failed"},
+	}, {
+		name:                 "Reconcile a run with retries after the first PipelineRun has failed",
+		pipeline:             aPipeline,
+		pipelineloop:         aPipelineLoop,
+		run:                  loopRunning(setRetries(runPipelineLoop, 1)),
+		pipelineruns:         []*v1beta1.PipelineRun{failed(expectedPipelineRunFailed)},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{setDeleted(failed(expectedPipelineRunFailed)), expectedPipelineRunRetry},
+	}, {
+		name:                 "Reconcile a new run with a pipelineloop with Parallelism specified",
+		pipeline:             paraPipeline,
+		pipelineloop:         paraPipelineLoop,
+		run:                  paraRunPipelineLoop,
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{expectedParaPipelineRun, expectedParaPipelineRun1},
+		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
+	}, {
+		name:                 "Reconcile a new run with a nested pipelineloop",
+		pipeline:             nestedPipeline,
+		pipelineloop:         nestedPipelineLoop,
+		run:                  runNestedPipelineLoop,
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionUnknown,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonRunning,
+		expectedPipelineruns: []*v1beta1.PipelineRun{expectedNestedPipelineRun},
+		expectedEvents:       []string{"Normal Started", "Normal Running Iterations completed: 0"},
+	}, {
+		name:                 "Reconcile a new run with a recursive pipelineloop with max nested stack depth 0",
+		pipeline:             setPipelineNestedStackDepth(nestedPipeline, 0),
+		pipelineloop:         setPipelineLoopNestedStackDepth(nestedPipelineLoop, 0),
+		run:                  setRunNestedStackDepth(runNestedPipelineLoop, 0),
+		pipelineruns:         []*v1beta1.PipelineRun{},
+		expectedStatus:       corev1.ConditionFalse,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonStackLimitExceeded,
+		expectedPipelineruns: []*v1beta1.PipelineRun{},
+		expectedEvents:       []string{"Normal Started ", "Warning Failed nested stack depth limit reached."},
+	}, {
+		name:                 "Reconcile a run with condition pipelinerun, and the first PipelineRun condition check failed",
+		pipeline:             aPipeline,
+		pipelineloop:         aPipelineLoop,
+		run:                  loopRunning(conditionRunPipelineLoop),
+		pipelineruns:         []*v1beta1.PipelineRun{successfulWithSkipedTasks(expectedConditionPipelineRunIteration1)},
+		expectedStatus:       corev1.ConditionTrue,
+		expectedReason:       pipelineloopv1alpha1.PipelineLoopRunReasonSucceeded,
+		expectedPipelineruns: []*v1beta1.PipelineRun{successfulWithSkipedTasks(expectedConditionPipelineRunIteration1)},
+		expectedEvents:       []string{"Normal Succeeded PipelineRuns completed successfully with the conditions are met"},
+	}}
 
 	//testcases = testcases[1:3]