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 7, 2020
1 parent 9611f3e commit 3b63cf2
Showing 1 changed file with 74 additions and 51 deletions.
125 changes: 74 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 All @@ -39,6 +38,20 @@ type PipelineRunFacts struct {
FinalTasksGraph *dag.Graph
}

// pipelineRunStatusCount holds the count of successful, failed, cancelled, skipped, and incomplete tasks
type pipelineRunStatusCount struct {
// skipped tasks count
Skipped int
// successful tasks count
Succeeded int
// failed tasks count
Failed int
// cancelled tasks count
Cancelled int
// number of tasks which are still pending, have not executed
Incomplete int
}

// ToMap returns a map that maps pipeline task name to the resolved pipeline run task
func (state PipelineRunState) ToMap() map[string]*ResolvedPipelineRunTask {
m := make(map[string]*ResolvedPipelineRunTask)
Expand Down Expand Up @@ -204,53 +217,31 @@ 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
s := facts.getPipelineTasksCount()
// completed task is a collection of successful, failed, cancelled tasks (skipped tasks are reported separately)
cmTasks := s.Succeeded + s.Failed + s.Cancelled

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()

// check if the pipeline is finished executing all tasks i.e. no incomplete tasks
if s.Incomplete == 0 {
status := corev1.ConditionTrue
if failedTasks > 0 || cancelledTasks > 0 {
reason := v1beta1.PipelineRunReasonSuccessful.String()
// Set reason to ReasonCompleted - At least one is skipped
if s.Skipped > 0 {
reason = v1beta1.PipelineRunReasonCompleted.String()
}
// Set reason to ReasonFailed - At least one failed
if s.Failed > 0 {
reason = v1beta1.PipelineRunReasonFailed.String()
status = corev1.ConditionFalse
// Set reason to ReasonCancelled - At least one is cancelled and no failure yet
} else if s.Cancelled > 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 @@ -259,28 +250,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, s.Failed, s.Cancelled, s.Skipped),
}
}

// 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 s.Cancelled > 0 || (s.Failed > 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, s.Failed, s.Cancelled, s.Incomplete, s.Skipped),
}
}

// GetSkippedTasks constructs a list of SkippedTask struct to be included in the PipelineRun Status
func (facts *PipelineRunFacts) GetSkippedTasks() []v1beta1.SkippedTask {
var skipped []v1beta1.SkippedTask
for _, rprt := range facts.State {
Expand Down Expand Up @@ -341,6 +333,37 @@ func (facts *PipelineRunFacts) checkFinalTasksDone() bool {
return facts.checkTasksDone(facts.FinalTasksGraph)
}

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

// check if a specified pipelineTask is defined under tasks(DAG) section
func (facts *PipelineRunFacts) isDAGTask(pipelineTaskName string) bool {
if _, ok := facts.TasksGraph.Nodes[pipelineTaskName]; ok {
Expand Down

0 comments on commit 3b63cf2

Please sign in to comment.