diff --git a/pkg/apis/pipeline/v1/pipelinerun_types.go b/pkg/apis/pipeline/v1/pipelinerun_types.go index 83511e74e9c..2d82445f227 100644 --- a/pkg/apis/pipeline/v1/pipelinerun_types.go +++ b/pkg/apis/pipeline/v1/pipelinerun_types.go @@ -656,6 +656,10 @@ type PipelineTaskRunSpec struct { // Compute resources to use for this TaskRun ComputeResources *corev1.ResourceRequirements `json:"computeResources,omitempty"` + + // Timeout is the Time duration to wait for the TaskRun associated with the PipelineTask to complete. + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` } // GetTaskRunSpec returns the task specific spec for a given @@ -678,6 +682,7 @@ func (pr *PipelineRun) GetTaskRunSpec(pipelineTaskName string) PipelineTaskRunSp s.SidecarSpecs = task.SidecarSpecs s.Metadata = task.Metadata s.ComputeResources = task.ComputeResources + s.Timeout = task.Timeout } } return s diff --git a/pkg/apis/pipeline/v1/pipelinerun_validation.go b/pkg/apis/pipeline/v1/pipelinerun_validation.go index 16330aa2153..246d04f87b4 100644 --- a/pkg/apis/pipeline/v1/pipelinerun_validation.go +++ b/pkg/apis/pipeline/v1/pipelinerun_validation.go @@ -117,8 +117,23 @@ func (ps *PipelineRunSpec) Validate(ctx context.Context) (errs *apis.FieldError) wsNames[ws.Name] = idx } } + + // Validate task-specific timeouts in taskRunSpecs + for _, trs := range ps.TaskRunSpecs { + if trs.Timeout != nil { + if trs.Timeout.Duration < 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", trs.Timeout.Duration.String()), fmt.Sprintf("taskRunSpecs[%s].timeout", trs.PipelineTaskName))) + } + if ps.Timeouts != nil && ps.Timeouts.Pipeline != nil && trs.Timeout.Duration > ps.Timeouts.Pipeline.Duration { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be <= pipeline duration", trs.Timeout.Duration.String()), fmt.Sprintf("taskRunSpecs[%s].timeout", trs.PipelineTaskName))) + } + } + } + for idx, trs := range ps.TaskRunSpecs { - errs = errs.Also(validateTaskRunSpec(ctx, trs).ViaIndex(idx).ViaField("taskRunSpecs")) + if err := validateTaskRunSpec(ctx, trs); err != nil { + errs = errs.Also(err.ViaFieldIndex("taskRunSpecs", idx)) + } } if ps.TaskRunTemplate.PodTemplate != nil { @@ -310,6 +325,16 @@ func (ps *PipelineRunSpec) validatePipelineTimeout(timeout time.Duration, errorM errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s %s", ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String(), errorMsg), "timeouts.finally")) } } + + // Validate task-specific timeouts in taskRunSpecs + for _, trs := range ps.TaskRunSpecs { + if trs.Timeout != nil { + if trs.Timeout.Duration > timeout && timeout != config.NoTimeoutDuration { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be <= pipeline duration", trs.Timeout.Duration.String()), fmt.Sprintf("taskRunSpecs[%s].timeout", trs.PipelineTaskName))) + } + } + } + return errs } diff --git a/pkg/apis/pipeline/v1/pipelinerun_validation_test.go b/pkg/apis/pipeline/v1/pipelinerun_validation_test.go index 42a38ed7c3f..100f3ede1e3 100644 --- a/pkg/apis/pipeline/v1/pipelinerun_validation_test.go +++ b/pkg/apis/pipeline/v1/pipelinerun_validation_test.go @@ -1202,6 +1202,43 @@ func TestPipelineRun_InvalidTimeouts(t *testing.T) { }, }, want: apis.ErrInvalidValue("-48h0m0s should be >= 0", "spec.timeouts.pipeline"), + }, { + name: "negative task-specific timeout", + pr: v1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pipelinelinename", + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + Name: "prname", + }, + TaskRunSpecs: []v1.PipelineTaskRunSpec{{ + PipelineTaskName: "task1", + Timeout: &metav1.Duration{Duration: -1 * time.Hour}, + }}, + }, + }, + want: apis.ErrInvalidValue("-1h0m0s should be >= 0", "spec.taskRunSpecs[task1].timeout"), + }, { + name: "task-specific timeout exceeds pipeline timeout", + pr: v1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pipelinelinename", + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + Name: "prname", + }, + Timeouts: &v1.TimeoutFields{ + Pipeline: &metav1.Duration{Duration: 1 * time.Hour}, + }, + TaskRunSpecs: []v1.PipelineTaskRunSpec{{ + PipelineTaskName: "task1", + Timeout: &metav1.Duration{Duration: 2 * time.Hour}, + }}, + }, + }, + want: apis.ErrInvalidValue("2h0m0s should be <= pipeline duration", "spec.taskRunSpecs[task1].timeout"), }, { name: "negative pipeline tasks Timeout", pr: v1.PipelineRun{ diff --git a/pkg/remoteresolution/resolver/hub/resolver_test.go b/pkg/remoteresolution/resolver/hub/resolver_test.go index da8f4ea2021..d0cce4339d8 100644 --- a/pkg/remoteresolution/resolver/hub/resolver_test.go +++ b/pkg/remoteresolution/resolver/hub/resolver_test.go @@ -60,7 +60,16 @@ func TestValidate(t *testing.T) { version: "bar", catalog: "baz", hubType: ArtifactHubType, - }, { + }, + { + testName: "stepaction validation", + kind: "stepaction", + resourceName: "foo", + version: "bar", + catalog: "baz", + hubType: ArtifactHubType, + }, + { testName: "tekton type validation", kind: "task", resourceName: "foo", @@ -134,7 +143,7 @@ func TestValidateConflictingKindName(t *testing.T) { hubType string }{ { - kind: "not-taskpipeline", + kind: "not-taskpipelineorstepaction", name: "foo", version: "bar", catalog: "baz", diff --git a/pkg/resolution/resolver/hub/resolver.go b/pkg/resolution/resolver/hub/resolver.go index db2d9d29413..7062088d353 100644 --- a/pkg/resolution/resolver/hub/resolver.go +++ b/pkg/resolution/resolver/hub/resolver.go @@ -359,8 +359,8 @@ func validateParams(ctx context.Context, paramsMap map[string]string, tektonHubU missingParams = append(missingParams, ParamVersion) } if kind, ok := paramsMap[ParamKind]; ok { - if kind != "task" && kind != "pipeline" { - return errors.New("kind param must be task or pipeline") + if kind != "task" && kind != "pipeline" && kind != "stepaction" { + return errors.New("kind param must be either a task, pipeline or a stepaction") } } if hubType, ok := paramsMap[ParamType]; ok { diff --git a/pkg/resolution/resolver/hub/resolver_test.go b/pkg/resolution/resolver/hub/resolver_test.go index 82335875d7d..48aa4a13f4d 100644 --- a/pkg/resolution/resolver/hub/resolver_test.go +++ b/pkg/resolution/resolver/hub/resolver_test.go @@ -62,7 +62,16 @@ func TestValidateParams(t *testing.T) { version: "bar", catalog: "baz", hubType: ArtifactHubType, - }, { + }, + { + testName: "stepaction validation", + kind: "stepaction", + resourceName: "foo", + version: "bar", + catalog: "baz", + hubType: ArtifactHubType, + }, + { testName: "tekton type validation", kind: "task", resourceName: "foo", @@ -148,7 +157,7 @@ func TestValidateParamsConflictingKindName(t *testing.T) { hubType string }{ { - kind: "not-taskpipeline", + kind: "not-taskpipelineorstepaction", name: "foo", version: "bar", catalog: "baz",