Skip to content

Commit

Permalink
Puts the new timeouts field under feature gates
Browse files Browse the repository at this point in the history
  • Loading branch information
souleb committed May 27, 2021
1 parent 72458c6 commit 4fc27b2
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 204 deletions.
10 changes: 6 additions & 4 deletions examples/v1beta1/pipelineruns/no-ci/pipeline-timeout.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ spec:
- sleep 90s
args:
- "$(inputs.params.MESSAGE)"
---

---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
Expand Down Expand Up @@ -54,8 +54,7 @@ spec:
- name: NIGHT_GREETINGS
value: "Good Night, Bob!"

---

---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
Expand All @@ -64,7 +63,10 @@ spec:
# 1 hour and half timeout for the pipeline
# 1 hour and fifteen minutes for the pipeline tasks
# 15 minutes for the finally tasks
timeouts:
#
# This field requires enable-api-fields: "alpha" flag
# Check https://github.com/tektoncd/pipeline/blob/main/config/config-feature-flags.yaml
timeouts:
pipeline: 1h30m
tasks: 1h15m
pipelineSpec:
Expand Down
2 changes: 0 additions & 2 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions internal/builder/v1beta1/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,32 +529,32 @@ func PipelineRunNilTimeout(prs *v1beta1.PipelineRunSpec) {
prs.Timeout = nil
}

func initTimeouts(prs *v1beta1.PipelineRunSpec) {
if prs.Timeouts == nil {
prs.Timeouts = &v1beta1.TimeoutFields{}
}
}

// PipelineRunTasksTimeout sets the timeout to the PipelineRunSpec.
func PipelineRunTasksTimeout(duration time.Duration) PipelineRunSpecOp {
return func(prs *v1beta1.PipelineRunSpec) {
if prs.Timeouts == nil {
prs.Timeouts = &v1beta1.TimeoutFields{}
}
initTimeouts(prs)
prs.Timeouts.Tasks = &metav1.Duration{Duration: duration}
}
}

// PipelineRunFinallyTimeout sets the timeout to the PipelineRunSpec.
func PipelineRunFinallyTimeout(duration time.Duration) PipelineRunSpecOp {
return func(prs *v1beta1.PipelineRunSpec) {
if prs.Timeouts == nil {
prs.Timeouts = &v1beta1.TimeoutFields{}
}
initTimeouts(prs)
prs.Timeouts.Finally = &metav1.Duration{Duration: duration}
}
}

// PipelineRunPipelineTimeout sets the timeout to the PipelineRunSpec.
func PipelineRunPipelineTimeout(duration time.Duration) PipelineRunSpecOp {
return func(prs *v1beta1.PipelineRunSpec) {
if prs.Timeouts == nil {
prs.Timeouts = &v1beta1.TimeoutFields{}
}
initTimeouts(prs)
prs.Timeouts.Pipeline = &metav1.Duration{Duration: duration}
}
}
Expand Down
41 changes: 40 additions & 1 deletion pkg/apis/pipeline/v1beta1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions pkg/apis/pipeline/v1beta1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ type PipelineRunSpec struct {
// Used for cancelling a pipelinerun (and maybe more later on)
// +optional
Status PipelineRunSpecStatus `json:"status,omitempty"`
// This is an alpha field. You must set the "enable-api-fields" feature flag to "alpha"
// for this field to be supported.
//
// Time after which the Pipeline times out.
// Currently three keys are accepted in the map
// pipeline, tasks and finally
Expand All @@ -201,9 +204,12 @@ type PipelineRunSpec struct {
}

type TimeoutFields struct {
// Pipeline sets the maximum allowed duration for execution of the entire pipeline. The sum of individual timeouts for tasks and finally must not exceed this value.
Pipeline *metav1.Duration `json:"pipeline,omitempty"`
Tasks *metav1.Duration `json:"tasks,omitempty"`
Finally *metav1.Duration `json:"finally,omitempty"`
// Tasks sets the maximum allowed duration of this pipeline's tasks
Tasks *metav1.Duration `json:"tasks,omitempty"`
// Finally sets the maximum allowed duration of this pipeline's finally
Finally *metav1.Duration `json:"finally,omitempty"`
}

// PipelineRunSpecStatus defines the pipelinerun spec status the user can provide
Expand Down
39 changes: 22 additions & 17 deletions pkg/apis/pipeline/v1beta1/pipelinerun_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/tektoncd/pipeline/pkg/apis/config"
"github.com/tektoncd/pipeline/pkg/apis/validate"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
)

Expand Down Expand Up @@ -83,34 +84,30 @@ func (ps *PipelineRunSpec) Validate(ctx context.Context) (errs *apis.FieldError)
}
}

// This is an alpha feature and will fail validation if it's used in a pipelinerun spec
// when the enable-api-fields feature gate is anything but "alpha".
if ps.Timeouts != nil {
if ps.Timeout != nil {
// can't have both at the same time
errs = errs.Also(apis.ErrDisallowedFields("timeout", "timeouts"))
}

errs = errs.Also(ValidateEnabledAPIFields(ctx, "timeouts", config.AlphaAPIFields))

// tasks timeout should be a valid duration of at least 0.
if ps.Timeouts.Tasks != nil && ps.Timeouts.Tasks.Duration < 0 {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ps.Timeouts.Tasks.Duration.String()), "timeouts.tasks"))
}
errs = errs.Also(validateTimeoutDuration("tasks", ps.Timeouts.Tasks))

// finally timeout should be a valid duration of at least 0.
if ps.Timeouts.Finally != nil && ps.Timeouts.Finally.Duration < 0 {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ps.Timeouts.Finally.Duration.String()), "timeouts.finally"))
}
errs = errs.Also(validateTimeoutDuration("finally", ps.Timeouts.Finally))

if ps.Timeouts.Pipeline != nil {
// pipeline timeout should be a valid duration of at least 0.
if ps.Timeouts.Pipeline.Duration < 0 {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ps.Timeouts.Pipeline.Duration.String()), "timeouts.pipeline"))
}
// pipeline timeout should be a valid duration of at least 0.
errs = errs.Also(validateTimeoutDuration("pipeline", ps.Timeouts.Pipeline))

if ps.Timeouts.Pipeline != nil {
errs = errs.Also(ps.validatePipelineTimeout(ps.Timeouts.Pipeline.Duration, "should be <= pipeline duration"))

} else {
defaultTimeout := time.Duration(config.FromContextOrDefaults(ctx).Defaults.DefaultTimeoutMinutes)
errs = errs.Also(ps.validatePipelineTimeout(defaultTimeout, "should be <= default timeout duration"))

}
}

Expand All @@ -134,19 +131,27 @@ func (ps *PipelineRunSpec) Validate(ctx context.Context) (errs *apis.FieldError)
return errs
}

func validateTimeoutDuration(field string, d *metav1.Duration) (errs *apis.FieldError) {
if d != nil && d.Duration < 0 {
fieldPath := fmt.Sprintf("timeouts.%s", field)
return errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", d.Duration.String()), fieldPath))
}
return nil
}

func (ps *PipelineRunSpec) validatePipelineTimeout(timeout time.Duration, errorMsg string) (errs *apis.FieldError) {
if ps.Timeouts.Tasks != nil && ps.Timeouts.Tasks.Duration > timeout {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s "+errorMsg, ps.Timeouts.Tasks.Duration.String()), "timeouts.tasks"))
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s %s", ps.Timeouts.Tasks.Duration.String(), errorMsg), "timeouts.tasks"))
}

if ps.Timeouts.Finally != nil && ps.Timeouts.Finally.Duration > timeout {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s "+errorMsg, ps.Timeouts.Finally.Duration.String()), "timeouts.finally"))
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s %s", ps.Timeouts.Finally.Duration.String(), errorMsg), "timeouts.finally"))
}

if ps.Timeouts.Tasks != nil && ps.Timeouts.Finally != nil {
if ps.Timeouts.Tasks.Duration+ps.Timeouts.Finally.Duration > timeout {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s "+errorMsg, ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String()), "timeouts.tasks"))
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s "+errorMsg, ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String()), "timeouts.finally"))
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s %s", ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String(), errorMsg), "timeouts.tasks"))
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s + %s %s", ps.Timeouts.Tasks.Duration.String(), ps.Timeouts.Finally.Duration.String(), errorMsg), "timeouts.finally"))
}
}
return errs
Expand Down
Loading

0 comments on commit 4fc27b2

Please sign in to comment.