Skip to content

Commit

Permalink
Cancel PipelineRun only after its children Tasks are stopped
Browse files Browse the repository at this point in the history
  • Loading branch information
renzodavid9 committed Jul 25, 2024
1 parent f693b0e commit 010cf70
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 17 deletions.
7 changes: 7 additions & 0 deletions pkg/apis/pipeline/v1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ func (pr *PipelineRun) IsCancelled() bool {
return pr.Spec.Status == PipelineRunSpecStatusCancelled
}

// IsCancelling returns true if the PipelineRun's status indicates it is cancelling.
func (pr *PipelineRun) IsCancelling() bool {
return pr.Status.GetCondition(apis.ConditionSucceeded).Reason == PipelineRunReasonCancelling.String()
}

// IsGracefullyCancelled returns true if the PipelineRun's spec status is set to CancelledRunFinally state
func (pr *PipelineRun) IsGracefullyCancelled() bool {
return pr.Spec.Status == PipelineRunSpecStatusCancelledRunFinally
Expand Down Expand Up @@ -340,6 +345,8 @@ const (
// This reason may be found with a corev1.ConditionFalse status, if the cancellation was processed successfully
// This reason may be found with a corev1.ConditionUnknown status, if the cancellation is being processed or failed
PipelineRunReasonCancelled PipelineRunReason = "Cancelled"
// PipelineRunReasonCancelling is the reason set when the PipelineRun is cancelling
PipelineRunReasonCancelling PipelineRunReason = "Cancelling"
// PipelineRunReasonPending is the reason set when the PipelineRun is in the pending state
PipelineRunReasonPending PipelineRunReason = "PipelineRunPending"
// PipelineRunReasonTimedOut is the reason set when the PipelineRun has timed out
Expand Down
68 changes: 53 additions & 15 deletions pkg/reconciler/pipelinerun/cancel.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
clientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
listersv1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1"
"go.uber.org/zap"
jsonpatch "gomodules.xyz/jsonpatch/v2"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -91,23 +92,10 @@ func cancelTaskRun(ctx context.Context, taskRunName string, namespace string, cl
return err
}

// cancelPipelineRun marks the PipelineRun as cancelled and any resolved TaskRun(s) too.
// cancelPipelineRun patch the PipelineRun active children Tasks as cancelled.
func cancelPipelineRun(ctx context.Context, logger *zap.SugaredLogger, pr *v1.PipelineRun, clientSet clientset.Interface) error {
errs := cancelPipelineTaskRuns(ctx, logger, pr, clientSet)

// If we successfully cancelled all the TaskRuns and Runs, we can consider the PipelineRun cancelled.
if len(errs) == 0 {
reason := v1.PipelineRunReasonCancelled

pr.Status.SetCondition(&apis.Condition{
Type: apis.ConditionSucceeded,
Status: corev1.ConditionFalse,
Reason: reason.String(),
Message: fmt.Sprintf("PipelineRun %q was cancelled", pr.Name),
})
// update pr completed time
pr.Status.CompletionTime = &metav1.Time{Time: time.Now()}
} else {
if len(errs) > 0 {
e := strings.Join(errs, "\n")
// Indicate that we failed to cancel the PipelineRun
pr.Status.SetCondition(&apis.Condition{
Expand All @@ -118,6 +106,15 @@ func cancelPipelineRun(ctx context.Context, logger *zap.SugaredLogger, pr *v1.Pi
})
return fmt.Errorf("error(s) from cancelling TaskRun(s) from PipelineRun %s: %s", pr.Name, e)
}

// Mark the PipelineRun as Cancelling
pr.Status.SetCondition(&apis.Condition{
Type: apis.ConditionSucceeded,
Status: corev1.ConditionUnknown,
Reason: v1.PipelineRunReasonCancelling.String(),
Message: fmt.Sprintf("PipelineRun %q is cancelling", pr.Name),
})

return nil
}

Expand Down Expand Up @@ -201,3 +198,44 @@ func gracefullyCancelPipelineRun(ctx context.Context, logger *zap.SugaredLogger,
}
return nil
}

func childrenTasksStopped(ctx context.Context, pr *v1.PipelineRun, trLister listersv1.TaskRunNamespaceLister) (bool, error) {
trNames, customRunNames, err := getChildObjectsFromPRStatusForTaskNames(ctx, pr.Status, sets.NewString())
if err != nil {
return false, err
}

for _, trName := range trNames {
tr, err := trLister.Get(trName)
if err != nil {
return false, err
}

if tr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() {
return false, nil
}
}

for _, customRunName := range customRunNames {
tr, err := trLister.Get(customRunName)
if err != nil {
return false, err
}

if tr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() {
return false, nil
}
}

return true, nil
}

func markPipelineRunAsCancelled(pr *v1.PipelineRun) {
pr.Status.SetCondition(&apis.Condition{
Type: apis.ConditionSucceeded,
Status: corev1.ConditionFalse,
Reason: v1.PipelineRunReasonCancelled.String(),
Message: fmt.Sprintf("PipelineRun %q was cancelled", pr.Name),
})
pr.Status.CompletionTime = &metav1.Time{Time: time.Now()}
}
20 changes: 18 additions & 2 deletions pkg/reconciler/pipelinerun/pipelinerun.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,25 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, pr *v1.PipelineRun) pkgr
return c.finishReconcileUpdateEmitEvents(ctx, pr, before, err)
}

// If the pipelinerun is cancelled, cancel tasks and update status
// If the pipelinerun is cancelling, we check if all its child tasks stopped. If so, we change the pipeline status
if pr.IsCancelling() {
trLister := c.taskRunLister.TaskRuns(pr.Namespace)
tasksStopped, err := childrenTasksStopped(ctx, pr, trLister)
if err != nil {
logger.Errorf("Error checking if pipelinerun is cancelling: %v", err.Error())
return c.finishReconcileUpdateEmitEvents(ctx, pr, before, err)
}

if tasksStopped {
markPipelineRunAsCancelled(pr)
}

return c.finishReconcileUpdateEmitEvents(ctx, pr, before, err)
}

// If the pipelinerun is cancelled, cancel all its child tasks
if pr.IsCancelled() {
err := cancelPipelineRun(ctx, logger, pr, c.PipelineClientSet)
err = cancelPipelineRun(ctx, logger, pr, c.PipelineClientSet)
return c.finishReconcileUpdateEmitEvents(ctx, pr, before, err)
}

Expand Down

0 comments on commit 010cf70

Please sign in to comment.