diff --git a/pkg/apis/pipeline/v1beta1/pipelinerun_types.go b/pkg/apis/pipeline/v1beta1/pipelinerun_types.go index 6e9823ee67b..46e0202a5ee 100644 --- a/pkg/apis/pipeline/v1beta1/pipelinerun_types.go +++ b/pkg/apis/pipeline/v1beta1/pipelinerun_types.go @@ -220,6 +220,32 @@ type PipelineRunStatus struct { PipelineRunStatusFields `json:",inline"` } +// PipelineRunSucceededReason represents a reason for the pipeline run "Succeeded" condition +type PipelineRunSucceededReason string + +const ( + // PipelineRunSucceededStartedReason is the reason set when the PipelineRun has just started + PipelineRunSucceededStartedReason PipelineRunSucceededReason = "Started" + // PipelineRunSucceededRunningReason is the reason set when the PipelineRun is running + PipelineRunSucceededRunningReason PipelineRunSucceededReason = "Running" + // PipelineRunSucceededSuccessfulReason is the reason set when the PipelineRun completed successfully + PipelineRunSucceededSuccessfulReason PipelineRunSucceededReason = "Succeeded" + // PipelineRunSucceededCompletedReason is the reason set when the PipelineRun completed successfully with one or more skipped Tasks + PipelineRunSucceededCompletedReason PipelineRunSucceededReason = "Completed" + // PipelineRunSucceededFailedReason is the reason set when the PipelineRun completed with a failure + PipelineRunSucceededFailedReason PipelineRunSucceededReason = "Failed" + // PipelineRunSucceededCancelledReason is the reason set when the PipelineRun cancelled by the user + // 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 + PipelineRunSucceededCancelledReason PipelineRunSucceededReason = "Cancelled" + // PipelineRunSucceededTimedOutdReason is the reason set when the PipelineRun has timed out + PipelineRunSucceededTimedOutdReason PipelineRunSucceededReason = "PipelineRunTimeout" +) + +func (t PipelineRunSucceededReason) String() string { + return string(t) +} + var pipelineRunCondSet = apis.NewBatchConditionSet() // GetCondition returns the Condition matching the given type. @@ -236,7 +262,12 @@ func (pr *PipelineRunStatus) InitializeConditions() { if pr.StartTime.IsZero() { pr.StartTime = &metav1.Time{Time: time.Now()} } - pipelineRunCondSet.Manage(pr).InitializeConditions() + conditionManager := pipelineRunCondSet.Manage(pr) + conditionManager.InitializeConditions() + // Ensure the started reason is set for the "Succeeded" condition + initialCondition := conditionManager.GetCondition(apis.ConditionSucceeded) + initialCondition.Reason = TaskRunSucceededStartedReason.String() + conditionManager.SetCondition(*initialCondition) } // SetCondition sets the condition, unsetting previous conditions with the same diff --git a/pkg/apis/pipeline/v1beta1/run_interface.go b/pkg/apis/pipeline/v1beta1/run_interface.go index ddaf2ffb979..303ab4af4b7 100644 --- a/pkg/apis/pipeline/v1beta1/run_interface.go +++ b/pkg/apis/pipeline/v1beta1/run_interface.go @@ -23,20 +23,48 @@ import ( // RunsToCompletionStatus is implemented by TaskRun.Status and PipelineRun.Status type RunsToCompletionStatus interface { + + // GetCondition returns the Condition for the specified ConditionType GetCondition(t apis.ConditionType) *apis.Condition + + // InitializeConditions is used to set up the initial conditions for the + // RunsToCompletion when it's initially started InitializeConditions() + + // SetCondition is used to set up a conditions for the specified ConditionType SetCondition(newCond *apis.Condition) } // RunsToCompletion is implemented by TaskRun and PipelineRun type RunsToCompletion interface { + + // GetTypeMeta returns the TypeMeta GetTypeMeta() *metav1.TypeMeta + + // GetObjectMeta returns the ObjectMeta GetObjectMeta() *metav1.ObjectMeta + + // GetOwnerReference returns the RunsToCompletion as owner reference for any related object GetOwnerReference() metav1.OwnerReference + + // GetStatus returns the status as RunsToCompletionStatus GetStatus() RunsToCompletionStatus + + // IsDone returns true once the reconcile work on the resource is complete + // except for postrun actions (stop timeout timer, emit events, record metrics) IsDone() bool + + // HasStarted returns true after the RunsToCompletion has been reconciled for + // the first time. It must be true after InitializeConditions has been invoked + // on the associated RunsToCompletionStatus HasStarted() bool + + // IsCancelled returns true if the user marked the RunsToCompletion for cancellation IsCancelled() bool + + // HasTimedOut returns true once the RunsToCompletion has passed its maximum run time HasTimedOut() bool + + // GetRunKey returns the RunsToCompletion keuy which is equeued in the controller queue GetRunKey() string } diff --git a/pkg/apis/pipeline/v1beta1/taskrun_types.go b/pkg/apis/pipeline/v1beta1/taskrun_types.go index 3e52a29054d..c7db48965ec 100644 --- a/pkg/apis/pipeline/v1beta1/taskrun_types.go +++ b/pkg/apis/pipeline/v1beta1/taskrun_types.go @@ -72,10 +72,6 @@ const ( // TaskRunSpecStatusCancelled indicates that the user wants to cancel the task, // if not already cancelled or terminated TaskRunSpecStatusCancelled = "TaskRunCancelled" - - // TaskRunReasonCancelled indicates that the TaskRun has been cancelled - // because it was requested so by the user - TaskRunReasonCancelled = "TaskRunCancelled" ) // TaskRunInputs holds the input values that this task was invoked with. @@ -102,6 +98,43 @@ type TaskRunStatus struct { TaskRunStatusFields `json:",inline"` } +// TaskRunSucceededReason is an enum used to store all TaskRun reason for +// the Succeeded condition that are controlled by the TaskRun itself. Failure +// reasons that emerge from underlying resources are not included here +type TaskRunSucceededReason string + +const ( + // TaskRunSucceededStartedReason is the reason set when the TaskRun has just started + TaskRunSucceededStartedReason TaskRunSucceededReason = "Started" + // TaskRunSucceededRunningReason is the reason set when the TaskRun is running + TaskRunSucceededRunningReason TaskRunSucceededReason = "Running" + // TaskRunSucceededSuccessfulReason is the reason set when the TaskRun completed successfully + TaskRunSucceededSuccessfulReason TaskRunSucceededReason = "Succeeded" + // TaskRunSucceededFailedReason is the reason set when the TaskRun completed with a failure + TaskRunSucceededFailedReason TaskRunSucceededReason = "Failed" + // TaskRunSucceededCancelledReason is the reason set when the Taskrun is cancelled by the user + TaskRunSucceededCancelledReason TaskRunSucceededReason = "TaskRunCancelled" + // TaskRunSucceededTimedOutReason is the reason set when the Taskrun has timed out + TaskRunSucceededTimedOutReason TaskRunSucceededReason = "TaskRunTimeout" +) + +func (t TaskRunSucceededReason) String() string { + return string(t) +} + +// GetStartedReason returns the reason set to the "Succeeded" condition when +// InitializeConditions is invoked +func (trs *TaskRunStatus) GetStartedReason() string { + return TaskRunSucceededStartedReason.String() +} + +// GetRunningReason returns the reason set to the "Succeeded" condition when +// the RunsToCompletion starts running. This is used indicate that the resource +// could be validated is starting to perform its job. +func (trs *TaskRunStatus) GetRunningReason() string { + return TaskRunSucceededRunningReason.String() +} + // MarkResourceNotConvertible adds a Warning-severity condition to the resource noting // that it cannot be converted to a higher version. func (trs *TaskRunStatus) MarkResourceNotConvertible(err *CannotConvertError) { @@ -116,11 +149,11 @@ func (trs *TaskRunStatus) MarkResourceNotConvertible(err *CannotConvertError) { // MarkResourceFailed sets the ConditionSucceeded condition to ConditionFalse // based on an error that occurred and a reason -func (trs *TaskRunStatus) MarkResourceFailed(reason string, err error) { +func (trs *TaskRunStatus) MarkResourceFailed(reason TaskRunSucceededReason, err error) { taskRunCondSet.Manage(trs).SetCondition(apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionFalse, - Reason: reason, + Reason: reason.String(), Message: err.Error(), }) } @@ -211,7 +244,12 @@ func (trs *TaskRunStatus) InitializeConditions() { if trs.StartTime.IsZero() { trs.StartTime = &metav1.Time{Time: time.Now()} } - taskRunCondSet.Manage(trs).InitializeConditions() + conditionManager := taskRunCondSet.Manage(trs) + conditionManager.InitializeConditions() + // Ensure the started reason is set for the "Succeeded" condition + initialCondition := conditionManager.GetCondition(apis.ConditionSucceeded) + initialCondition.Reason = TaskRunSucceededStartedReason.String() + conditionManager.SetCondition(*initialCondition) } // SetCondition sets the condition, unsetting previous conditions with the same diff --git a/pkg/pod/status.go b/pkg/pod/status.go index 8a372129cf7..7f84996153b 100644 --- a/pkg/pod/status.go +++ b/pkg/pod/status.go @@ -45,13 +45,6 @@ const ( // that taskrun failed runtime validation ReasonFailedValidation = "TaskRunValidationFailed" - // ReasonRunning indicates that the reason for the inprogress status is that the TaskRun - // is just starting to be reconciled - ReasonRunning = "Running" - - // ReasonTimedOut indicates that the TaskRun has taken longer than its configured timeout - ReasonTimedOut = "TaskRunTimeout" - // ReasonExceededResourceQuota indicates that the TaskRun failed to create a pod due to // a ResourceQuota in the namespace ReasonExceededResourceQuota = "ExceededResourceQuota" @@ -68,13 +61,6 @@ const ( // is that the creation of the pod backing the TaskRun failed ReasonPodCreationFailed = "PodCreationFailed" - // ReasonSucceeded indicates that the reason for the finished status is that all of the steps - // completed successfully - ReasonSucceeded = "Succeeded" - - // ReasonFailed indicates that the reason for the failure status is unknown or that one of the steps failed - ReasonFailed = "Failed" - //timeFormat is RFC3339 with millisecond timeFormat = "2006-01-02T15:04:05.000Z07:00" ) @@ -114,7 +100,7 @@ func MakeTaskRunStatus(logger *zap.SugaredLogger, tr v1beta1.TaskRun, pod *corev trs.SetCondition(&apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionUnknown, - Reason: ReasonRunning, + Reason: v1beta1.TaskRunSucceededRunningReason.String(), Message: "Not all Steps in the Task have finished executing", }) } @@ -197,14 +183,14 @@ func updateCompletedTaskRun(trs *v1beta1.TaskRunStatus, pod *corev1.Pod) { trs.SetCondition(&apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionFalse, - Reason: ReasonFailed, + Reason: v1beta1.TaskRunSucceededFailedReason.String(), Message: msg, }) } else { trs.SetCondition(&apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionTrue, - Reason: ReasonSucceeded, + Reason: v1beta1.TaskRunSucceededSuccessfulReason.String(), Message: "All Steps have completed executing", }) } @@ -219,7 +205,7 @@ func updateIncompleteTaskRun(trs *v1beta1.TaskRunStatus, pod *corev1.Pod) { trs.SetCondition(&apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionUnknown, - Reason: ReasonRunning, + Reason: v1beta1.TaskRunSucceededRunningReason.String(), Message: "Not all Steps in the Task have finished executing", }) case corev1.PodPending: diff --git a/pkg/reconciler/events/cloudevent/cloudevent.go b/pkg/reconciler/events/cloudevent/cloudevent.go index 780fce0879b..6bc42f3cdfb 100644 --- a/pkg/reconciler/events/cloudevent/cloudevent.go +++ b/pkg/reconciler/events/cloudevent/cloudevent.go @@ -134,14 +134,25 @@ func getEventType(runObject v1beta1.RunsToCompletion) (*TektonEventType, error) var eventType TektonEventType switch { case c.IsUnknown(): - // TBD We should have different event types here, e.g. started, running - // That requires having either knowledge about the previous condition or - // TaskRun and PipelineRun using dedicated "Reasons" or "Conditions" switch t.Kind { case "TaskRun": - eventType = TektonTaskRunUnknownV1 + switch c.Reason { + case v1beta1.TaskRunSucceededStartedReason.String(): + eventType = TektonTaskRunStartedV1 + case v1beta1.TaskRunSucceededRunningReason.String(): + eventType = TektonTaskRunRunningV1 + default: + eventType = TektonTaskRunUnknownV1 + } case "PipelineRun": - eventType = TektonPipelineRunUnknownV1 + switch c.Reason { + case v1beta1.PipelineRunSucceededStartedReason.String(): + eventType = TektonPipelineRunStartedV1 + case v1beta1.PipelineRunSucceededRunningReason.String(): + eventType = TektonPipelineRunRunningV1 + default: + eventType = TektonPipelineRunUnknownV1 + } } case c.IsFalse(): switch t.Kind { diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go index 43d9d621683..2d089e2724d 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/pipelinerun/pipelinerun.go @@ -94,10 +94,6 @@ const ( // pipelineRunAgentName defines logging agent name for PipelineRun Controller pipelineRunAgentName = "pipeline-controller" - - // Event reasons - eventReasonFailed = "PipelineRunFailed" - eventReasonSucceeded = "PipelineRunSucceeded" ) type configStore interface { @@ -203,7 +199,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { default: if err := c.tracker.Track(pr.GetTaskRunRef(), pr); err != nil { c.Logger.Errorf("Failed to create tracker for TaskRuns for PipelineRun %s: %v", pr.Name, err) - c.Recorder.Event(pr, corev1.EventTypeWarning, eventReasonFailed, "Failed to create tracker for TaskRuns for PipelineRun") + c.Recorder.Event(pr, corev1.EventTypeWarning, v1beta1.PipelineRunSucceededFailedReason.String(), "Failed to create tracker for TaskRuns for PipelineRun") return err } @@ -227,7 +223,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { if !equality.Semantic.DeepEqual(original.Status, pr.Status) { if _, err := c.updateStatus(pr); err != nil { c.Logger.Warn("Failed to update PipelineRun status", zap.Error(err)) - c.Recorder.Event(pr, corev1.EventTypeWarning, eventReasonFailed, "PipelineRun failed to update") + c.Recorder.Event(pr, corev1.EventTypeWarning, v1beta1.PipelineRunSucceededFailedReason.String(), "PipelineRun failed to update") return multierror.Append(merr, err) } updated = true @@ -240,7 +236,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { if !reflect.DeepEqual(original.ObjectMeta.Labels, pr.ObjectMeta.Labels) || !reflect.DeepEqual(original.ObjectMeta.Annotations, pr.ObjectMeta.Annotations) { if _, err := c.updateLabelsAndAnnotations(pr); err != nil { c.Logger.Warn("Failed to update PipelineRun labels/annotations", zap.Error(err)) - c.Recorder.Event(pr, corev1.EventTypeWarning, eventReasonFailed, "PipelineRun failed to update labels/annotations") + c.Recorder.Event(pr, corev1.EventTypeWarning, v1beta1.PipelineRunSucceededFailedReason.String(), "PipelineRun failed to update labels/annotations") return multierror.Append(merr, err) } updated = true @@ -533,7 +529,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1beta1.PipelineRun) err if pipelineState.IsDone() && pr.IsDone() { c.timeoutHandler.Release(pr) - c.Recorder.Event(pr, corev1.EventTypeNormal, eventReasonSucceeded, "PipelineRun completed successfully.") + c.Recorder.Event(pr, corev1.EventTypeNormal, v1beta1.PipelineRunSucceededSuccessfulReason.String(), "PipelineRun completed successfully.") return nil } diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go index 20b0609b597..641b992e453 100644 --- a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go +++ b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution.go @@ -37,28 +37,6 @@ import ( ) const ( - // ReasonRunning indicates that the reason for the inprogress status is that the TaskRun - // is just starting to be reconciled - ReasonRunning = "Running" - - // ReasonFailed indicates that the reason for the failure status is that one of the TaskRuns failed - ReasonFailed = "Failed" - - // ReasonCancelled indicates that the reason for the cancelled status is that one of the TaskRuns cancelled - ReasonCancelled = "Cancelled" - - // ReasonSucceeded indicates that the reason for the finished status is that all of the TaskRuns - // completed successfully - ReasonSucceeded = "Succeeded" - - // ReasonCompleted indicates that the reason for the finished status is that all of the TaskRuns - // completed successfully but with some conditions checking failed - ReasonCompleted = "Completed" - - // ReasonTimedOut indicates that the PipelineRun has taken longer than its configured - // timeout - ReasonTimedOut = "PipelineRunTimeout" - // ReasonConditionCheckFailed indicates that the reason for the failure status is that the // condition check associated to the pipeline task evaluated to false ReasonConditionCheckFailed = "ConditionCheckFailed" @@ -74,6 +52,7 @@ func (e *TaskNotFoundError) Error() string { return fmt.Sprintf("Couldn't retrieve Task %q: %s", e.Name, e.Msg) } +// ConditionNotFoundError is used to track failures to the type ConditionNotFoundError struct { Name string Msg string @@ -145,7 +124,7 @@ func (t ResolvedPipelineRunTask) IsCancelled() bool { return false } - return c.IsFalse() && c.Reason == v1beta1.TaskRunSpecStatusCancelled + return c.IsFalse() && c.Reason == v1beta1.TaskRunSucceededCancelledReason.String() } // ToMap returns a map that maps pipeline task name to the resolved pipeline run task @@ -192,7 +171,7 @@ func (state PipelineRunState) GetNextTasks(candidateTasks map[string]struct{}) [ if _, ok := candidateTasks[t.PipelineTask.Name]; ok && t.TaskRun != nil { status := t.TaskRun.Status.GetCondition(apis.ConditionSucceeded) if status != nil && status.IsFalse() { - if !(t.TaskRun.IsCancelled() || status.Reason == v1beta1.TaskRunSpecStatusCancelled || status.Reason == ReasonConditionCheckFailed) { + if !(t.TaskRun.IsCancelled() || status.Reason == v1beta1.TaskRunSucceededCancelledReason.String() || status.Reason == ReasonConditionCheckFailed) { if len(t.TaskRun.Status.RetriesStatus) < t.PipelineTask.Retries { tasks = append(tasks, t) } @@ -417,7 +396,7 @@ func GetPipelineConditionStatus(pr *v1beta1.PipelineRun, state PipelineRunState, return &apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionFalse, - Reason: ReasonTimedOut, + Reason: v1beta1.PipelineRunSucceededTimedOutdReason.String(), Message: fmt.Sprintf("PipelineRun %q failed to finish within %q", pr.Name, pr.Spec.Timeout.Duration.String()), } } @@ -429,7 +408,7 @@ func GetPipelineConditionStatus(pr *v1beta1.PipelineRun, state PipelineRunState, return &apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionFalse, - Reason: ReasonCancelled, + Reason: v1beta1.PipelineRunSucceededCancelledReason.String(), Message: fmt.Sprintf("TaskRun %s has cancelled", rprt.TaskRun.Name), } } @@ -439,7 +418,7 @@ func GetPipelineConditionStatus(pr *v1beta1.PipelineRun, state PipelineRunState, return &apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionFalse, - Reason: ReasonFailed, + Reason: v1beta1.PipelineRunSucceededFailedReason.String(), Message: fmt.Sprintf("TaskRun %s has failed", rprt.TaskRun.Name), } } @@ -463,9 +442,9 @@ func GetPipelineConditionStatus(pr *v1beta1.PipelineRun, state PipelineRunState, if reflect.DeepEqual(allTasks, successOrSkipTasks) { logger.Infof("All TaskRuns have finished for PipelineRun %s so it has finished", pr.Name) - reason := ReasonSucceeded + reason := v1beta1.PipelineRunSucceededSuccessfulReason.String() if skipTasks != 0 { - reason = ReasonCompleted + reason = v1beta1.PipelineRunSucceededCompletedReason.String() } return &apis.Condition{ @@ -481,7 +460,7 @@ func GetPipelineConditionStatus(pr *v1beta1.PipelineRun, state PipelineRunState, return &apis.Condition{ Type: apis.ConditionSucceeded, Status: corev1.ConditionUnknown, - Reason: ReasonRunning, + Reason: v1beta1.PipelineRunSucceededRunningReason.String(), Message: fmt.Sprintf("Tasks Completed: %d, Incomplete: %d, Skipped: %d", len(successOrSkipTasks)-skipTasks, len(allTasks)-len(successOrSkipTasks), skipTasks), } } diff --git a/pkg/reconciler/taskrun/taskrun.go b/pkg/reconciler/taskrun/taskrun.go index 7636ba71189..ce75e49c5b5 100644 --- a/pkg/reconciler/taskrun/taskrun.go +++ b/pkg/reconciler/taskrun/taskrun.go @@ -172,7 +172,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { if tr.IsCancelled() { before := tr.Status.GetCondition(apis.ConditionSucceeded) message := fmt.Sprintf("TaskRun %q was cancelled", tr.Name) - err := c.failTaskRun(tr, v1beta1.TaskRunReasonCancelled, message) + err := c.failTaskRun(tr, v1beta1.TaskRunSucceededCancelledReason, message) return c.finishReconcileUpdateEmitEvents(tr, original, before, err) } @@ -181,7 +181,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { if tr.HasTimedOut() { before := tr.Status.GetCondition(apis.ConditionSucceeded) message := fmt.Sprintf("TaskRun %q failed to finish within %q", tr.Name, tr.GetTimeout()) - err := c.failTaskRun(tr, podconvert.ReasonTimedOut, message) + err := c.failTaskRun(tr, v1beta1.TaskRunSucceededTimedOutReason, message) return c.finishReconcileUpdateEmitEvents(tr, original, before, err) } @@ -506,9 +506,9 @@ func (c *Reconciler) handlePodCreationError(tr *v1beta1.TaskRun, err error) { // If a pod is associated to the TaskRun, it stops it // failTaskRun function may return an error in case the pod could not be deleted // failTaskRun may update the local TaskRun status, but it won't push the updates to etcd -func (c *Reconciler) failTaskRun(tr *v1beta1.TaskRun, reason, message string) error { +func (c *Reconciler) failTaskRun(tr *v1beta1.TaskRun, reason v1beta1.TaskRunSucceededReason, message string) error { - c.Logger.Warn("stopping task run %q because of %q", tr.Name, reason) + c.Logger.Warn("stopping task run %q because of %q", tr.Name, reason.String()) tr.Status.MarkResourceFailed(reason, errors.New(message)) // update tr completed time