Skip to content

Commit

Permalink
Custom task without api version return validation error
Browse files Browse the repository at this point in the history
This commit closes #6459. For a customtask reference in a pipelinetask,
if the Kind is non-empty and not "Task" or "ClusterTask", then it should
be a Custom task and api version should be set. If not then validation
webhook should return error. TaskRun's taskRef validation will be
handled separately in #6557

Signed-off-by: Yongxuan Zhang yongxuanzhang@google.com
  • Loading branch information
Yongxuanzhang committed Apr 20, 2023
1 parent 50fa5f1 commit d3fc30d
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,8 @@ spec:

This creates a `Run/CustomRun` of a custom task of type `Example` in the `example.dev` API group with the version `v1alpha1`.

Validation error will be returned if `apiVersion` or `kind` is missing.

You can also specify the `name` of a custom task resource object previously defined in the cluster.

```yaml
Expand Down
23 changes: 21 additions & 2 deletions pkg/apis/pipeline/v1/pipeline_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func TestPipelineTask_Validate_Failure(t *testing.T) {
expectedError apis.FieldError
wc func(context.Context) context.Context
}{{
name: "invalid custom task without Kind",
name: "custom task reference in taskref missing apiversion Kind",
p: PipelineTask{
Name: "invalid-custom-task",
TaskRef: &TaskRef{APIVersion: "example.com"},
Expand All @@ -320,7 +320,26 @@ func TestPipelineTask_Validate_Failure(t *testing.T) {
Message: `invalid value: custom task ref must specify kind`,
Paths: []string{"taskRef.kind"},
},
}}
}, {
name: "custom task reference in taskspec missing kind",
p: PipelineTask{Name: "foo", TaskSpec: &EmbeddedTask{
TypeMeta: runtime.TypeMeta{
APIVersion: "example.com",
}}},
expectedError: *apis.ErrInvalidValue("custom task spec must specify kind", "taskSpec.kind"),
}, {
name: "custom task reference in taskref missing apiversion",
p: PipelineTask{Name: "foo", TaskRef: &TaskRef{Kind: "Example", Name: ""}},
expectedError: *apis.ErrInvalidValue("custom task ref must specify apiVersion", "taskRef.apiVersion"),
}, {
name: "custom task reference in taskspec missing apiversion",
p: PipelineTask{Name: "foo", TaskSpec: &EmbeddedTask{
TypeMeta: runtime.TypeMeta{
Kind: "Example",
}}},
expectedError: *apis.ErrInvalidValue("custom task spec must specify apiVersion", "taskSpec.apiVersion"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
Expand Down
11 changes: 10 additions & 1 deletion pkg/apis/pipeline/v1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,20 @@ func (pt PipelineTask) Validate(ctx context.Context) (errs *apis.FieldError) {
errs = errs.Also(pt.validateRefOrSpec())

errs = errs.Also(pt.validateEmbeddedOrType())

// taskKinds contains the kinds when the apiVersion is not set, they are not custom tasks,
// if apiVersion is set they are custom tasks.
taskKinds := map[TaskKind]bool{
"": true,
NamespacedTaskKind: true,
}
// Pipeline task having taskRef/taskSpec with APIVersion is classified as custom task
switch {
case pt.TaskRef != nil && !taskKinds[pt.TaskRef.Kind]:
errs = errs.Also(pt.validateCustomTask())
case pt.TaskRef != nil && pt.TaskRef.APIVersion != "":
errs = errs.Also(pt.validateCustomTask())
case pt.TaskSpec != nil && !taskKinds[TaskKind(pt.TaskSpec.Kind)]:
errs = errs.Also(pt.validateCustomTask())
case pt.TaskSpec != nil && pt.TaskSpec.APIVersion != "":
errs = errs.Also(pt.validateCustomTask())
default:
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/pipeline/v1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ func TestPipeline_Validate_Success(t *testing.T) {
}},
},
},
}, {
name: "valid Task without apiversion",
p: &Pipeline{
ObjectMeta: metav1.ObjectMeta{Name: "pipeline"},
Spec: PipelineSpec{
Tasks: []PipelineTask{{Name: "foo", TaskRef: &TaskRef{Name: "bar", Kind: NamespacedTaskKind}}},
},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
20 changes: 19 additions & 1 deletion pkg/apis/pipeline/v1beta1/pipeline_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func TestPipelineTask_Validate_Failure(t *testing.T) {
expectedError apis.FieldError
wc func(context.Context) context.Context
}{{
name: "invalid custom task without Kind",
name: "custom task reference in taskref missing apiversion Kind",
p: PipelineTask{
Name: "invalid-custom-task",
TaskRef: &TaskRef{APIVersion: "example.com"},
Expand All @@ -348,6 +348,24 @@ func TestPipelineTask_Validate_Failure(t *testing.T) {
Message: `invalid value: custom task ref must specify kind`,
Paths: []string{"taskRef.kind"},
},
}, {
name: "custom task reference in taskspec missing kind",
p: PipelineTask{Name: "foo", TaskSpec: &EmbeddedTask{
TypeMeta: runtime.TypeMeta{
APIVersion: "example.com",
}}},
expectedError: *apis.ErrInvalidValue("custom task spec must specify kind", "taskSpec.kind"),
}, {
name: "custom task reference in taskref missing apiversion",
p: PipelineTask{Name: "foo", TaskRef: &TaskRef{Kind: "Example", Name: ""}},
expectedError: *apis.ErrInvalidValue("custom task ref must specify apiVersion", "taskRef.apiVersion"),
}, {
name: "custom task reference in taskspec missing apiversion",
p: PipelineTask{Name: "foo", TaskSpec: &EmbeddedTask{
TypeMeta: runtime.TypeMeta{
Kind: "Example",
}}},
expectedError: *apis.ErrInvalidValue("custom task spec must specify apiVersion", "taskSpec.apiVersion"),
}, {
name: "invalid bundle without bundle name",
p: PipelineTask{
Expand Down
12 changes: 11 additions & 1 deletion pkg/apis/pipeline/v1beta1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,22 @@ func (pt PipelineTask) Validate(ctx context.Context) (errs *apis.FieldError) {
if pt.Resources != nil {
errs = errs.Also(apis.ErrDisallowedFields("resources"))
}

// taskKinds contains the kinds when the apiVersion is not set, they are not custom tasks,
// if apiVersion is set they are custom tasks.
taskKinds := map[TaskKind]bool{
"": true,
NamespacedTaskKind: true,
ClusterTaskKind: true,
}
cfg := config.FromContextOrDefaults(ctx)
// Pipeline task having taskRef/taskSpec with APIVersion is classified as custom task
switch {
case pt.TaskRef != nil && !taskKinds[pt.TaskRef.Kind]:
errs = errs.Also(pt.validateCustomTask())
case pt.TaskRef != nil && pt.TaskRef.APIVersion != "":
errs = errs.Also(pt.validateCustomTask())
case pt.TaskSpec != nil && !taskKinds[TaskKind(pt.TaskSpec.Kind)]:
errs = errs.Also(pt.validateCustomTask())
case pt.TaskSpec != nil && pt.TaskSpec.APIVersion != "":
errs = errs.Also(pt.validateCustomTask())
// If EnableTektonOCIBundles feature flag is on, validate bundle specifications
Expand Down
16 changes: 16 additions & 0 deletions pkg/apis/pipeline/v1beta1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ func TestPipeline_Validate_Success(t *testing.T) {
}},
},
},
}, {
name: "valid Task without apiversion",
p: &Pipeline{
ObjectMeta: metav1.ObjectMeta{Name: "pipeline"},
Spec: PipelineSpec{
Tasks: []PipelineTask{{Name: "foo", TaskRef: &TaskRef{Name: "bar", Kind: NamespacedTaskKind}}},
},
},
}, {
name: "valid Cluster Task without apiversion",
p: &Pipeline{
ObjectMeta: metav1.ObjectMeta{Name: "pipeline"},
Spec: PipelineSpec{
Tasks: []PipelineTask{{Name: "foo", TaskRef: &TaskRef{Name: "task", Kind: ClusterTaskKind}}},
},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit d3fc30d

Please sign in to comment.