Skip to content

Commit

Permalink
refactoring get condition status for pipeline
Browse files Browse the repository at this point in the history
GetPipelineConditionStatus based on a new structure PipelineRunFacts

PR # 3254 applied refactoring and introduced PipelineRunFacts to simplify PipelineRun.
This is a follow up PR to further simplify GetPipelineConditionStatus.
  • Loading branch information
pritidesai committed Oct 2, 2020
1 parent 09c7fe2 commit a59fa82
Showing 1 changed file with 70 additions and 51 deletions.
121 changes: 70 additions & 51 deletions pkg/reconciler/pipelinerun/resources/pipelinerunstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package resources

import (
"fmt"
"reflect"

"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag"
Expand Down Expand Up @@ -193,53 +192,30 @@ func (facts *PipelineRunFacts) GetPipelineConditionStatus(pr *v1beta1.PipelineRu
}
}

allTasks := []string{}
withStatusTasks := []string{}
skipTasks := []v1beta1.SkippedTask{}
failedTasks := int(0)
cancelledTasks := int(0)
reason := v1beta1.PipelineRunReasonSuccessful.String()

// Check to see if all tasks are success or skipped
//
// The completion reason is also calculated here, but it will only be used
// if all tasks are completed.
//
// The pipeline run completion reason is set from the taskrun completion reason
// according to the following logic:
//
// - All successful: ReasonSucceeded
// - Some successful, some skipped: ReasonCompleted
// - Some cancelled, none failed: ReasonCancelled
// - At least one failed: ReasonFailed
for _, rprt := range facts.State {
allTasks = append(allTasks, rprt.PipelineTask.Name)
switch {
case rprt.IsSuccessful():
withStatusTasks = append(withStatusTasks, rprt.PipelineTask.Name)
case rprt.Skip(facts):
withStatusTasks = append(withStatusTasks, rprt.PipelineTask.Name)
skipTasks = append(skipTasks, v1beta1.SkippedTask{Name: rprt.PipelineTask.Name})
// At least one is skipped and no failure yet, mark as completed
if reason == v1beta1.PipelineRunReasonSuccessful.String() {
reason = v1beta1.PipelineRunReasonCompleted.String()
}
case rprt.IsCancelled():
cancelledTasks++
withStatusTasks = append(withStatusTasks, rprt.PipelineTask.Name)
if reason != v1beta1.PipelineRunReasonFailed.String() {
reason = v1beta1.PipelineRunReasonCancelled.String()
}
case rprt.IsFailure():
withStatusTasks = append(withStatusTasks, rprt.PipelineTask.Name)
failedTasks++
reason = v1beta1.PipelineRunReasonFailed.String()
}
}
// report the count in PipelineRun Status
// get the count of successful tasks, failed tasks, cancelled tasks, skipped task, and incomplete tasks
sTasks, fTasks, cTasks, skTasks, iTasks := facts.getPipelineTasksCount()
// completed task is a collection of successful, failed, cancelled tasks (skipped tasks are reported separately)
cmTasks := sTasks + fTasks + cTasks

if reflect.DeepEqual(allTasks, withStatusTasks) {
// The completion reason is set from the TaskRun completion reason
// by default, set it to ReasonRunning
reason := v1beta1.PipelineRunReasonRunning.String()

if facts.checkAllTasksDone() {
status := corev1.ConditionTrue
if failedTasks > 0 || cancelledTasks > 0 {
reason := v1beta1.PipelineRunReasonSuccessful.String()
// Set reason to ReasonCompleted - At least one is skipped
if skTasks > 0 {
reason = v1beta1.PipelineRunReasonCompleted.String()
}
// Set reason to ReasonFailed - At least one failed
if fTasks > 0 {
reason = v1beta1.PipelineRunReasonFailed.String()
status = corev1.ConditionFalse
// Set reason to ReasonCancelled - At least one is cancelled and no failure yet
} else if cTasks > 0 {
reason = v1beta1.PipelineRunReasonCancelled.String()
status = corev1.ConditionFalse
}
logger.Infof("All TaskRuns have finished for PipelineRun %s so it has finished", pr.Name)
Expand All @@ -248,28 +224,29 @@ func (facts *PipelineRunFacts) GetPipelineConditionStatus(pr *v1beta1.PipelineRu
Status: status,
Reason: reason,
Message: fmt.Sprintf("Tasks Completed: %d (Failed: %d, Cancelled %d), Skipped: %d",
len(allTasks)-len(skipTasks), failedTasks, cancelledTasks, len(skipTasks)),
cmTasks, fTasks, cTasks, skTasks),
}
}

// Hasn't timed out; not all tasks have finished.... Must keep running then....
// transition pipeline into stopping state when one of the tasks(dag/final) cancelled or one of the dag tasks failed
// for a pipeline with final tasks, single dag task failure does not transition to interim stopping state
// pipeline stays in running state until all final tasks are done before transitioning to failed state
if cancelledTasks > 0 || (failedTasks > 0 && facts.CheckFinalTasksDone()) {
if cTasks > 0 || (fTasks > 0 && facts.CheckFinalTasksDone()) {
reason = v1beta1.PipelineRunReasonStopping.String()
} else {
reason = v1beta1.PipelineRunReasonRunning.String()
}

// return the status
return &apis.Condition{
Type: apis.ConditionSucceeded,
Status: corev1.ConditionUnknown,
Reason: reason,
Message: fmt.Sprintf("Tasks Completed: %d (Failed: %d, Cancelled %d), Incomplete: %d, Skipped: %d",
len(withStatusTasks)-len(skipTasks), failedTasks, cancelledTasks, len(allTasks)-len(withStatusTasks), len(skipTasks)),
cmTasks, fTasks, cTasks, iTasks, skTasks),
}
}

// GetSkippedTasks constructs a list of SkippedTask struct to be included in the PipelineRun Status
func (facts *PipelineRunFacts) GetSkippedTasks() []v1beta1.SkippedTask {
skipped := []v1beta1.SkippedTask{}
for _, rprt := range facts.State {
Expand Down Expand Up @@ -329,6 +306,48 @@ func (state PipelineRunState) GetTaskRunsStatus(pr *v1beta1.PipelineRun) map[str
return status
}

// checkAllTasksDone returns true if all DAG and finally tasks have been visited and executed
// tasks could have either executed successfully or failed or cancelled or skipped
func (facts *PipelineRunFacts) checkAllTasksDone() bool {
for _, t := range facts.State {
if t.TaskRun == nil {
if t.Skip(facts) {
continue
}
return false
}
if !t.IsDone() && !t.IsCancelled() {
return false
}
}
return true
}

// getPipelineTasksCount returns the count of successful tasks, failed tasks, cancelled tasks, skipped task, and incomplete tasks
func (facts *PipelineRunFacts) getPipelineTasksCount() (int, int, int, int, int) {
s, f, c, sk, i := 0, 0, 0, 0, 0
for _, t := range facts.State {
switch {
// increment success counter since the task is successful
case t.IsSuccessful():
s++
// increment failure counter since the task has failed
case t.IsFailure():
f++
// increment cancelled counter since the task is cancelled
case t.IsCancelled():
c++
// increment skip counter since the task is skipped
case t.Skip(facts):
sk++
// increment incomplete counter since the task is pending and not executed yet
default:
i++
}
}
return s, f, c, sk, i
}

func (facts *PipelineRunFacts) isDAGTask(pipelineTaskName string) bool {
if _, ok := facts.TasksGraph.Nodes[pipelineTaskName]; ok {
return true
Expand Down

0 comments on commit a59fa82

Please sign in to comment.