diff --git a/docs/taskruns.md b/docs/taskruns.md index 580a7c0bcaa..af6970164b4 100644 --- a/docs/taskruns.md +++ b/docs/taskruns.md @@ -48,8 +48,10 @@ following fields: - [`inputs`] - Specifies [input parameters](#input-parameters) and [input resources](#providing-resources) - [`outputs`] - Specifies [output resources](#providing-resources) - - `timeout` - Specifies timeout after which the `TaskRun` will fail. Defaults - to 60 minutes. + - `timeout` - Specifies timeout after which the `TaskRun` will fail. If the value of + `timeout` is empty, the default timeout will be applied. If the value is set to 0, + there is no timeout. You can also follow the instruction [here](#Configuring-default-timeout) + to configure the default timeout. - [`nodeSelector`] - a selector which must be true for the pod to fit on a node. The selector which must match a node's labels for the pod to be scheduled on that node. More info: @@ -145,6 +147,13 @@ spec: value: https://github.com/pivotal-nader-ziada/gohelloworld ``` +### Configuring Default Timeout + +You can configure the default timeout by changing the value of `default-timeout-minutes` +in [`config/config-defaults.yaml`](./../config/config-defaults.yaml). The default timeout +is 60 minutes, if `default-timeout-minutes` is not available. There is no timeout by +default, if `default-timeout-minutes` is set to 0. + ### Service Account Specifies the `name` of a `ServiceAccount` resource object. Use the diff --git a/pkg/apis/config/default.go b/pkg/apis/config/default.go index f31ebff3a3e..e82f696093f 100644 --- a/pkg/apis/config/default.go +++ b/pkg/apis/config/default.go @@ -18,15 +18,16 @@ package config import ( "fmt" - "strconv" - corev1 "k8s.io/api/core/v1" + "strconv" + "time" ) const ( // ConfigName is the name of the configmap DefaultsConfigName = "config-defaults" DefaultTimeoutMinutes = 60 + NoTimeoutDuration = 0 * time.Minute defaultTimeoutMinutesKey = "default-timeout-minutes" ) diff --git a/pkg/reconciler/timeout_handler_test.go b/pkg/reconciler/timeout_handler_test.go index b8f1af0fa6a..20484817edf 100644 --- a/pkg/reconciler/timeout_handler_test.go +++ b/pkg/reconciler/timeout_handler_test.go @@ -291,15 +291,44 @@ func TestGetTimeout(t *testing.T) { inputDuration: &metav1.Duration{Duration: 2 * time.Second}, expectedDuration: 2 * time.Second, }, + { + description: "returns 0 if the timeout duration is set to 0 second/minute/hour", + inputDuration: &metav1.Duration{Duration: 0 * time.Second}, + expectedDuration: 0 * time.Minute, + }, + { + description: "returns -1 if it has timed out", + inputDuration: &metav1.Duration{Duration: -1 * time.Second}, + expectedDuration: -1 * time.Second, + }, + } + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + receivedDuration := GetTimeout(tc.inputDuration, 60) + if receivedDuration != tc.expectedDuration { + t.Errorf("expected %q received %q", tc.expectedDuration.String(), receivedDuration.String()) + } + }) + } + verifyTimeout(0, t) + verifyTimeout(60, t) +} + +func verifyTimeout(defaultTimeout int, t *testing.T) { + testCases := []struct { + description string + inputDuration *metav1.Duration + expectedDuration time.Duration + }{ { description: "returns an end time using the default timeout if a TaskRun's timeout is nil", inputDuration: nil, - expectedDuration: 60 * time.Minute, + expectedDuration: time.Duration(defaultTimeout) * time.Minute, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { - receivedDuration := GetTimeout(tc.inputDuration, 60) + receivedDuration := GetTimeout(tc.inputDuration, defaultTimeout) if receivedDuration != tc.expectedDuration { t.Errorf("expected %q received %q", tc.expectedDuration.String(), receivedDuration.String()) } diff --git a/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go b/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go index b02e7dbdc2a..a462fba7d8e 100644 --- a/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go @@ -26,6 +26,7 @@ import ( "github.com/knative/pkg/configmap" "github.com/knative/pkg/controller" "github.com/knative/pkg/tracker" + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" "github.com/tektoncd/pipeline/pkg/apis/pipeline" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" artifacts "github.com/tektoncd/pipeline/pkg/artifacts" @@ -442,16 +443,19 @@ func (c *Reconciler) createTaskRun(logger *zap.SugaredLogger, rprt *resources.Re var taskRunTimeout = &metav1.Duration{Duration: 0 * time.Second} if pr.Spec.Timeout != nil { - pTimeoutTime := pr.Status.StartTime.Add(pr.Spec.Timeout.Duration) - if time.Now().After(pTimeoutTime) { - // Just in case something goes awry and we're creating the TaskRun after it should have already timed out, - // set a timeout of 0. - taskRunTimeout = &metav1.Duration{Duration: time.Until(pTimeoutTime)} - if taskRunTimeout.Duration < 0 { - taskRunTimeout = &metav1.Duration{Duration: 0 * time.Second} + // If the value of the timeout is 0 for any resource, there is no timeout. + if pr.Spec.Timeout.Duration != apisconfig.NoTimeoutDuration { + pTimeoutTime := pr.Status.StartTime.Add(pr.Spec.Timeout.Duration) + if time.Now().After(pTimeoutTime) { + // Just in case something goes awry and we're creating the TaskRun after it should have already timed out, + // set a timeout of -1. + taskRunTimeout = &metav1.Duration{Duration: time.Until(pTimeoutTime)} + if taskRunTimeout.Duration < 0 { + taskRunTimeout = &metav1.Duration{Duration: -1 * time.Second} + } + } else { + taskRunTimeout = pr.Spec.Timeout } - } else { - taskRunTimeout = pr.Spec.Timeout } } else { taskRunTimeout = nil diff --git a/pkg/reconciler/v1alpha1/taskrun/taskrun.go b/pkg/reconciler/v1alpha1/taskrun/taskrun.go index b622a59a555..7841515acfd 100644 --- a/pkg/reconciler/v1alpha1/taskrun/taskrun.go +++ b/pkg/reconciler/v1alpha1/taskrun/taskrun.go @@ -542,6 +542,10 @@ func (c *Reconciler) checkTimeout(tr *v1alpha1.TaskRun, ts *v1alpha1.TaskSpec, d cfg := c.store.Load() timeout := reconciler.GetTimeout(tr.Spec.Timeout, cfg.ConfigDefault.DefaultTimeoutMinutes) + // If timeout is set to 0 or defaulted to 0, there is no timeout. + if timeout == apisconfig.NoTimeoutDuration { + return false, nil + } runtime := time.Since(tr.Status.StartTime.Time) c.Logger.Infof("Checking timeout for TaskRun %q (startTime %s, timeout %s, runtime %s)", tr.Name, tr.Status.StartTime, timeout, runtime)