Skip to content

Commit

Permalink
refactor pipelineTask validation
Browse files Browse the repository at this point in the history
Introducing validate pipelineTask and pipelineTaskList reciever functions
to simplify the way tasks are validated for taskRef and taskSpec
  • Loading branch information
pritidesai committed Mar 23, 2021
1 parent 0593c7b commit 28c118f
Show file tree
Hide file tree
Showing 4 changed files with 402 additions and 267 deletions.
106 changes: 106 additions & 0 deletions pkg/apis/pipeline/v1beta1/pipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ limitations under the License.
package v1beta1

import (
"context"
"fmt"
"strings"

"github.com/google/go-containerregistry/pkg/name"
"github.com/tektoncd/pipeline/pkg/apis/config"

"github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -165,6 +170,18 @@ type PipelineTask struct {
Timeout *metav1.Duration `json:"timeout,omitempty"`
}

func (pt PipelineTask) validate() (errs *apis.FieldError) {
// can't have both taskRef and taskSpec at the same time
if pt.TaskRef != nil && pt.TaskSpec != nil {
errs = errs.Also(apis.ErrMultipleOneOf("taskRef", "taskSpec"))
}
// Check that one of TaskRef and TaskSpec is present
if pt.TaskRef == nil && pt.TaskSpec == nil {
errs = errs.Also(apis.ErrMissingOneOf("taskRef", "taskSpec"))
}
return errs
}

func (pt *PipelineTask) TaskSpecMetadata() PipelineTaskMetadata {
return pt.TaskSpec.Metadata
}
Expand All @@ -185,6 +202,70 @@ func (pt PipelineTask) ValidateName() *apis.FieldError {
return nil
}

// Validate custom task specifications - checking kind and fail if not yet supported features specified
func (pt PipelineTask) ValidateCustomTask() (errs *apis.FieldError) {
errs = errs.Also(pt.validate())
if pt.TaskRef.Kind == "" {
errs = errs.Also(apis.ErrInvalidValue("custom task ref must specify kind", "taskRef.kind"))
}
// Conditions are deprecated so the effort to support them with custom tasks is not justified.
// When expressions should be used instead.
if len(pt.Conditions) > 0 {
errs = errs.Also(apis.ErrInvalidValue("custom tasks do not support conditions - use when expressions instead", "conditions"))
}
// TODO(#3133): Support these features if possible.
if pt.Retries > 0 {
errs = errs.Also(apis.ErrInvalidValue("custom tasks do not support retries", "retries"))
}
if pt.Resources != nil {
errs = errs.Also(apis.ErrInvalidValue("custom tasks do not support PipelineResources", "resources"))
}
if pt.Timeout != nil {
errs = errs.Also(apis.ErrInvalidValue("custom tasks do not support timeout", "timeout"))
}
return errs
}

// Validate bundle specifications - checking name and bundle
func (pt PipelineTask) ValidateBundle() (errs *apis.FieldError) {
errs = errs.Also(pt.validate())
// bundle requires a TaskRef to be specified
if (pt.TaskRef != nil && pt.TaskRef.Bundle != "") && pt.TaskRef.Name == "" {
errs = errs.Also(apis.ErrMissingField("taskRef.name"))
}
// If a bundle url is specified, ensure it is parsable
if pt.TaskRef != nil && pt.TaskRef.Bundle != "" {
if _, err := name.ParseReference(pt.TaskRef.Bundle); err != nil {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("invalid bundle reference (%s)", err.Error()), "taskRef.bundle"))
}
}
return errs
}

// Validate pipeline task for taskRef and taskSpec
func (pt PipelineTask) Validate(ctx context.Context) (errs *apis.FieldError) {
errs = errs.Also(pt.validate())
// Validate TaskSpec if it's present
if pt.TaskSpec != nil {
errs = errs.Also(pt.TaskSpec.Validate(ctx).ViaField("taskSpec"))
}
if pt.TaskRef != nil {
if pt.TaskRef.Name != "" {
// TaskRef name must be a valid k8s name
if errSlice := validation.IsQualifiedName(pt.TaskRef.Name); len(errSlice) != 0 {
errs = errs.Also(apis.ErrInvalidValue(strings.Join(errSlice, ","), "name"))
}
} else {
errs = errs.Also(apis.ErrInvalidValue("taskRef must specify name", "taskRef.name"))
}
// fail if bundle is present when EnableTektonOCIBundles feature flag is off (as it won't be allowed nor used)
if pt.TaskRef.Bundle != "" {
errs = errs.Also(apis.ErrDisallowedFields("taskref.bundle"))
}
}
return errs
}

func (pt PipelineTask) Deps() []string {
deps := []string{}

Expand Down Expand Up @@ -266,6 +347,31 @@ func (l PipelineTaskList) Names() sets.String {
return names
}

// Validate a list of pipeline tasks including custom task and bundles
func (l PipelineTaskList) Validate(ctx context.Context, taskNames sets.String, path string) (errs *apis.FieldError) {
for i, t := range l {
// validate pipeline task name
errs = errs.Also(t.ValidateName().ViaFieldIndex(path, i))
// names cannot be duplicated - checking that pipelineTask names are unique
if _, ok := taskNames[t.Name]; ok {
errs = errs.Also(apis.ErrMultipleOneOf("name").ViaFieldIndex(path, i))
}
taskNames.Insert(t.Name)
cfg := config.FromContextOrDefaults(ctx)
// If EnableCustomTasks feature flag is on, validate custom task specifications
// pipeline task having taskRef with APIVersion is classified as custom task
if cfg.FeatureFlags.EnableCustomTasks && t.TaskRef != nil && t.TaskRef.APIVersion != "" {
errs = errs.Also(t.ValidateCustomTask().ViaFieldIndex(path, i))
// If EnableTektonOCIBundles feature flag is on, validate bundle specifications
} else if cfg.FeatureFlags.EnableTektonOCIBundles && t.TaskRef != nil && t.TaskRef.Bundle != "" {
errs = errs.Also(t.ValidateBundle().ViaFieldIndex(path, i))
} else {
errs = errs.Also(t.Validate(ctx).ViaFieldIndex(path, i))
}
}
return errs
}

// PipelineTaskParam is used to provide arbitrary string parameters to a Task.
type PipelineTaskParam struct {
Name string `json:"name"`
Expand Down
Loading

0 comments on commit 28c118f

Please sign in to comment.