diff --git a/pkg/reconciler/taskrun/cancel.go b/pkg/reconciler/taskrun/cancel.go new file mode 100644 index 00000000000..8eff915d7da --- /dev/null +++ b/pkg/reconciler/taskrun/cancel.go @@ -0,0 +1,77 @@ +/* +Copyright 2019 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package taskrun + +import ( + "fmt" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + podconvert "github.com/tektoncd/pipeline/pkg/pod" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "knative.dev/pkg/apis" +) + +type logger interface { + Warn(args ...interface{}) + Warnf(template string, args ...interface{}) + Infof(template string, args ...interface{}) +} + +func killTaskRun(tr *v1alpha1.TaskRun, clientSet kubernetes.Interface, + logger logger, reason, message string) error { + + logger.Warn("stopping task run %q because of %q", tr.Name, reason) + tr.Status.SetCondition(&apis.Condition{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: reason, + Message: message, + }) + + // update tr completed time + tr.Status.CompletionTime = &metav1.Time{Time: time.Now()} + + if tr.Status.PodName == "" { + logger.Warnf("task run %q has no pod running yet", tr.Name) + return nil + } + + // tr.Status.PodName will be empty if the pod was never successfully created. This condition + // can be reached, for example, by the pod never being schedulable due to limits imposed by + // a namespace's ResourceQuota. + err := clientSet.CoreV1().Pods(tr.Namespace).Delete(tr.Status.PodName, &metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + logger.Warnf("Failed to terminate pod: %v", err) + return err + } + return nil +} + +// cancelTaskRun marks the TaskRun as cancelled and delete pods linked to it. +func cancelTaskRun(tr *v1alpha1.TaskRun, clientSet kubernetes.Interface, logger logger) error { + message := fmt.Sprintf("TaskRun %q was cancelled", tr.Name) + return killTaskRun(tr, clientSet, logger, "TaskRunCancelled", message) +} + +func timeoutTaskRun(tr *v1alpha1.TaskRun, clientSet kubernetes.Interface, logger logger) error { + message := fmt.Sprintf("TaskRun %q failed to finish within %q", tr.Name, tr.Spec.Timeout.Duration.String()) + return killTaskRun(tr, clientSet, logger, podconvert.ReasonTimedOut, message) +} diff --git a/pkg/reconciler/taskrun/taskrun.go b/pkg/reconciler/taskrun/taskrun.go index 1f51b1e434d..0ab304c0ddf 100644 --- a/pkg/reconciler/taskrun/taskrun.go +++ b/pkg/reconciler/taskrun/taskrun.go @@ -164,22 +164,36 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { // If the TaskRun is cancelled, kill resources and update status if tr.IsCancelled() { before := tr.Status.GetCondition(apis.ConditionSucceeded) +<<<<<<< HEAD message := fmt.Sprintf("TaskRun %q was cancelled", tr.Name) err := c.failTaskRun(tr, v1beta1.TaskRunReasonCancelled, message) after := tr.Status.GetCondition(apis.ConditionSucceeded) reconciler.EmitEvent(c.Recorder, before, after, tr) return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil() +======= + err := cancelTaskRun(tr, c.KubeClientSet, c.Logger) + after := tr.Status.GetCondition(apis.ConditionSucceeded) + reconciler.EmitEvent(c.Recorder, before, after, tr) + return err +>>>>>>> Consolidate cancel and timeout logic } // Check if the TaskRun has timed out; if it is, this will set its status // accordingly. if tr.HasTimedOut() { before := tr.Status.GetCondition(apis.ConditionSucceeded) +<<<<<<< HEAD message := fmt.Sprintf("TaskRun %q failed to finish within %q", tr.Name, tr.GetTimeout()) err := c.failTaskRun(tr, podconvert.ReasonTimedOut, message) after := tr.Status.GetCondition(apis.ConditionSucceeded) reconciler.EmitEvent(c.Recorder, before, after, tr) return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil() +======= + err := timeoutTaskRun(tr, c.KubeClientSet, c.Logger) + after := tr.Status.GetCondition(apis.ConditionSucceeded) + reconciler.EmitEvent(c.Recorder, before, after, tr) + return err +>>>>>>> Consolidate cancel and timeout logic } // Reconcile this copy of the task run and then write back any status @@ -196,6 +210,10 @@ func (c *Reconciler) reconcile(ctx context.Context, tr *v1alpha1.TaskRun) error // and may not have had all of the assumed default specified. tr.SetDefaults(contexts.WithUpgradeViaDefaulting(ctx)) + if tr.Spec.Timeout == nil { + tr.Spec.Timeout = &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute} + } + if err := tr.ConvertTo(ctx, &v1beta1.TaskRun{}); err != nil { if ce, ok := err.(*v1beta1.CannotConvertError); ok { tr.Status.MarkResourceNotConvertible(ce)