Skip to content

Commit 30e389b

Browse files
l-qingtekton-robot
authored andcommitted
fix: ensure default type for params in remote tasks to prevent pipeline failures
fix #7775 In the existing logic, resources used for ConvertTo should have default values set. Otherwise, there could be issues with incorrect parameter types being set (e.g., an array type being treated as a string type). However, resources fetched from remote sources haven't undergone the SetDefaults operation. If we directly invoke the ConvertTo operation, it might result in erroneous outcomes. For instance, a v1beta1 ClusterTask that undergoes a direct ConvertTo to convert the resource into a v1 Task for validation might be mistakenly considered invalid. Additionally, even if a v1beta1 Task passes validation, the process of converting it to a v1 Task could still incorrectly set default parameter types, leading to errors during execution.
1 parent faccef8 commit 30e389b

File tree

6 files changed

+75
-15
lines changed

6 files changed

+75
-15
lines changed

pkg/reconciler/pipelinerun/pipelinerun_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15923,7 +15923,7 @@ spec:
1592315923
func TestReconcile_verifyResolved_V1beta1Pipeline_NoError(t *testing.T) {
1592415924
resolverName := "foobar"
1592515925

15926-
ts := parse.MustParseV1beta1Task(t, `
15926+
ts := parse.MustParseV1beta1TaskAndSetDefaults(t, `
1592715927
metadata:
1592815928
name: test-task
1592915929
namespace: foo
@@ -15947,7 +15947,7 @@ spec:
1594715947
t.Fatal("fail to marshal task", err)
1594815948
}
1594915949

15950-
ps := parse.MustParseV1beta1Pipeline(t, fmt.Sprintf(`
15950+
ps := parse.MustParseV1beta1PipelineAndSetDefaults(t, fmt.Sprintf(`
1595115951
metadata:
1595215952
name: test-pipeline
1595315953
namespace: foo
@@ -16260,7 +16260,7 @@ spec:
1626016260
func TestReconcile_verifyResolved_V1Pipeline_NoError(t *testing.T) {
1626116261
resolverName := "foobar"
1626216262

16263-
ts := parse.MustParseV1Task(t, `
16263+
ts := parse.MustParseV1TaskAndSetDefaults(t, `
1626416264
metadata:
1626516265
name: test-task
1626616266
namespace: foo
@@ -16284,7 +16284,7 @@ spec:
1628416284
t.Fatal("fail to marshal task", err)
1628516285
}
1628616286

16287-
ps := parse.MustParseV1Pipeline(t, fmt.Sprintf(`
16287+
ps := parse.MustParseV1PipelineAndSetDefaults(t, fmt.Sprintf(`
1628816288
metadata:
1628916289
name: test-pipeline
1629016290
namespace: foo
@@ -16416,7 +16416,7 @@ func TestReconcile_verifyResolved_V1Pipeline_Error(t *testing.T) {
1641616416
resolverName := "foobar"
1641716417

1641816418
// Case1: unsigned Pipeline refers to unsigned Task
16419-
unsignedTask := parse.MustParseV1beta1Task(t, `
16419+
unsignedTask := parse.MustParseV1beta1TaskAndSetDefaults(t, `
1642016420
metadata:
1642116421
name: test-task
1642216422
namespace: foo

pkg/reconciler/pipelinerun/resources/pipelineref.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ func resolvePipeline(ctx context.Context, resolver remote.Resolver, name string,
136136
func readRuntimeObjectAsPipeline(ctx context.Context, namespace string, obj runtime.Object, k8s kubernetes.Interface, tekton clientset.Interface, refSource *v1.RefSource, verificationPolicies []*v1alpha1.VerificationPolicy) (*v1.Pipeline, *trustedresources.VerificationResult, error) {
137137
switch obj := obj.(type) {
138138
case *v1beta1.Pipeline:
139+
obj.SetDefaults(ctx)
139140
// Verify the Pipeline once we fetch from the remote resolution, mutating, validation and conversion of the pipeline should happen after the verification, since signatures are based on the remote pipeline contents
140141
vr := trustedresources.VerifyResource(ctx, obj, k8s, refSource, verificationPolicies)
141142
// Issue a dry-run request to create the remote Pipeline, so that it can undergo validation from validating admission webhooks
@@ -154,6 +155,9 @@ func readRuntimeObjectAsPipeline(ctx context.Context, namespace string, obj runt
154155
}
155156
return p, &vr, nil
156157
case *v1.Pipeline:
158+
// This SetDefaults is currently not necessary, but for consistency, it is recommended to add it.
159+
// Avoid forgetting to add it in the future when there is a v2 version, causing similar problems.
160+
obj.SetDefaults(ctx)
157161
vr := trustedresources.VerifyResource(ctx, obj, k8s, refSource, verificationPolicies)
158162
// Issue a dry-run request to create the remote Pipeline, so that it can undergo validation from validating admission webhooks
159163
// without actually creating the Pipeline on the cluster

pkg/reconciler/pipelinerun/resources/pipelineref_test.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,31 +316,31 @@ func TestGetPipelineFunc_RemoteResolution(t *testing.T) {
316316
"apiVersion: tekton.dev/v1beta1",
317317
pipelineYAMLString,
318318
}, "\n"),
319-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLString),
319+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLString),
320320
}, {
321321
name: "v1 pipeline",
322322
pipelineYAML: strings.Join([]string{
323323
"kind: Pipeline",
324324
"apiVersion: tekton.dev/v1",
325325
pipelineYAMLString,
326326
}, "\n"),
327-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLString),
327+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLString),
328328
}, {
329329
name: "v1 remote pipeline without defaults",
330330
pipelineYAML: strings.Join([]string{
331331
"kind: Pipeline",
332332
"apiVersion: tekton.dev/v1",
333333
pipelineYAMLStringWithoutDefaults,
334334
}, "\n"),
335-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLStringWithoutDefaults),
335+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLStringWithoutDefaults),
336336
}, {
337337
name: "v1 remote pipeline with different namespace",
338338
pipelineYAML: strings.Join([]string{
339339
"kind: Pipeline",
340340
"apiVersion: tekton.dev/v1",
341341
pipelineYAMLStringNamespaceFoo,
342342
}, "\n"),
343-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLStringNamespaceFoo),
343+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLStringNamespaceFoo),
344344
}}
345345
for _, tc := range testcases {
346346
t.Run(tc.name, func(t *testing.T) {
@@ -433,7 +433,7 @@ func TestGetPipelineFunc_RemoteResolution_ReplacedParams(t *testing.T) {
433433
ctx, clients := getFakePipelineClient(t)
434434
cfg := config.FromContextOrDefaults(ctx)
435435
ctx = config.ToContext(ctx, cfg)
436-
pipeline := parse.MustParseV1Pipeline(t, pipelineYAMLString)
436+
pipeline := parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLString)
437437
pipelineRef := &v1.PipelineRef{
438438
ResolverRef: v1.ResolverRef{
439439
Resolver: "git",
@@ -1315,9 +1315,21 @@ var pipelineYAMLString = `
13151315
metadata:
13161316
name: foo
13171317
spec:
1318+
params:
1319+
- name: array
1320+
# type: array
1321+
default:
1322+
- "bar"
1323+
- "bar"
13181324
tasks:
13191325
- name: task1
13201326
taskSpec:
1327+
params:
1328+
- name: array
1329+
# type: array
1330+
default:
1331+
- "bar"
1332+
- "bar"
13211333
steps:
13221334
- name: step1
13231335
image: ubuntu

pkg/reconciler/taskrun/resources/taskref.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ func resolveStepAction(ctx context.Context, resolver remote.Resolver, name, name
233233
func readRuntimeObjectAsTask(ctx context.Context, namespace string, obj runtime.Object, k8s kubernetes.Interface, tekton clientset.Interface, refSource *v1.RefSource, verificationPolicies []*v1alpha1.VerificationPolicy) (*v1.Task, *trustedresources.VerificationResult, error) {
234234
switch obj := obj.(type) {
235235
case *v1beta1.Task:
236+
obj.SetDefaults(ctx)
236237
// Verify the Task once we fetch from the remote resolution, mutating, validation and conversion of the task should happen after the verification, since signatures are based on the remote task contents
237238
vr := trustedresources.VerifyResource(ctx, obj, k8s, refSource, verificationPolicies)
238239
// Issue a dry-run request to create the remote Task, so that it can undergo validation from validating admission webhooks
@@ -251,6 +252,7 @@ func readRuntimeObjectAsTask(ctx context.Context, namespace string, obj runtime.
251252
}
252253
return t, &vr, nil
253254
case *v1beta1.ClusterTask:
255+
obj.SetDefaults(ctx)
254256
t, err := convertClusterTaskToTask(ctx, *obj)
255257
// Issue a dry-run request to create the remote Task, so that it can undergo validation from validating admission webhooks
256258
// without actually creating the Task on the cluster
@@ -259,6 +261,9 @@ func readRuntimeObjectAsTask(ctx context.Context, namespace string, obj runtime.
259261
}
260262
return t, nil, err
261263
case *v1.Task:
264+
// This SetDefaults is currently not necessary, but for consistency, it is recommended to add it.
265+
// Avoid forgetting to add it in the future when there is a v2 version, causing similar problems.
266+
obj.SetDefaults(ctx)
262267
vr := trustedresources.VerifyResource(ctx, obj, k8s, refSource, verificationPolicies)
263268
// Issue a dry-run request to create the remote Task, so that it can undergo validation from validating admission webhooks
264269
// without actually creating the Task on the cluster

pkg/reconciler/taskrun/resources/taskref_test.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,31 +1062,31 @@ func TestGetTaskFunc_RemoteResolution(t *testing.T) {
10621062
"apiVersion: tekton.dev/v1beta1",
10631063
taskYAMLString,
10641064
}, "\n"),
1065-
wantTask: parse.MustParseV1Task(t, taskYAMLString),
1065+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString),
10661066
}, {
10671067
name: "v1beta1 cluster task",
10681068
taskYAML: strings.Join([]string{
10691069
"kind: ClusterTask",
10701070
"apiVersion: tekton.dev/v1beta1",
10711071
taskYAMLString,
10721072
}, "\n"),
1073-
wantTask: parse.MustParseV1Task(t, taskYAMLString),
1073+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString),
10741074
}, {
10751075
name: "v1 task",
10761076
taskYAML: strings.Join([]string{
10771077
"kind: Task",
10781078
"apiVersion: tekton.dev/v1",
10791079
taskYAMLString,
10801080
}, "\n"),
1081-
wantTask: parse.MustParseV1Task(t, taskYAMLString),
1081+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString),
10821082
}, {
10831083
name: "v1 task without defaults",
10841084
taskYAML: strings.Join([]string{
10851085
"kind: Task",
10861086
"apiVersion: tekton.dev/v1",
10871087
remoteTaskYamlWithoutDefaults,
10881088
}, "\n"),
1089-
wantTask: parse.MustParseV1Task(t, remoteTaskYamlWithoutDefaults),
1089+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, remoteTaskYamlWithoutDefaults),
10901090
}}
10911091
for _, tc := range testcases {
10921092
t.Run(tc.name, func(t *testing.T) {
@@ -1189,7 +1189,7 @@ func TestGetTaskFunc_RemoteResolution_ReplacedParams(t *testing.T) {
11891189
ctx := context.Background()
11901190
cfg := config.FromContextOrDefaults(ctx)
11911191
ctx = config.ToContext(ctx, cfg)
1192-
task := parse.MustParseV1Task(t, taskYAMLString)
1192+
task := parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString)
11931193
taskRef := &v1.TaskRef{
11941194
ResolverRef: v1.ResolverRef{
11951195
Resolver: "git",
@@ -1899,6 +1899,12 @@ var taskYAMLString = `
18991899
metadata:
19001900
name: foo
19011901
spec:
1902+
params:
1903+
- name: array
1904+
# type: array
1905+
default:
1906+
- "bar"
1907+
- "bar"
19021908
steps:
19031909
- name: step1
19041910
image: ubuntu

test/parse/yaml.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package parse
1515

1616
import (
17+
"context"
1718
"testing"
1819

1920
v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
@@ -67,6 +68,14 @@ kind: Task
6768
return &task
6869
}
6970

71+
// MustParseV1beta1TaskAndSetDefaults takes YAML and parses it into a *v1beta1.Task and sets defaults
72+
func MustParseV1beta1TaskAndSetDefaults(t *testing.T, yaml string) *v1beta1.Task {
73+
t.Helper()
74+
task := MustParseV1beta1Task(t, yaml)
75+
task.SetDefaults(context.Background())
76+
return task
77+
}
78+
7079
// MustParseCustomRun takes YAML and parses it into a *v1beta1.CustomRun
7180
func MustParseCustomRun(t *testing.T, yaml string) *v1beta1.CustomRun {
7281
t.Helper()
@@ -89,6 +98,14 @@ kind: Task
8998
return &task
9099
}
91100

101+
// MustParseV1TaskAndSetDefaults takes YAML and parses it into a *v1.Task and sets defaults
102+
func MustParseV1TaskAndSetDefaults(t *testing.T, yaml string) *v1.Task {
103+
t.Helper()
104+
task := MustParseV1Task(t, yaml)
105+
task.SetDefaults(context.Background())
106+
return task
107+
}
108+
92109
// MustParseClusterTask takes YAML and parses it into a *v1beta1.ClusterTask
93110
func MustParseClusterTask(t *testing.T, yaml string) *v1beta1.ClusterTask {
94111
t.Helper()
@@ -133,6 +150,14 @@ kind: Pipeline
133150
return &pipeline
134151
}
135152

153+
// MustParseV1beta1PipelineAndSetDefaults takes YAML and parses it into a *v1beta1.Pipeline and sets defaults
154+
func MustParseV1beta1PipelineAndSetDefaults(t *testing.T, yaml string) *v1beta1.Pipeline {
155+
t.Helper()
156+
p := MustParseV1beta1Pipeline(t, yaml)
157+
p.SetDefaults(context.Background())
158+
return p
159+
}
160+
136161
// MustParseV1Pipeline takes YAML and parses it into a *v1.Pipeline
137162
func MustParseV1Pipeline(t *testing.T, yaml string) *v1.Pipeline {
138163
t.Helper()
@@ -144,6 +169,14 @@ kind: Pipeline
144169
return &pipeline
145170
}
146171

172+
// MustParseV1PipelineAndSetDefaults takes YAML and parses it into a *v1.Pipeline and sets defaults
173+
func MustParseV1PipelineAndSetDefaults(t *testing.T, yaml string) *v1.Pipeline {
174+
t.Helper()
175+
p := MustParseV1Pipeline(t, yaml)
176+
p.SetDefaults(context.Background())
177+
return p
178+
}
179+
147180
// MustParseVerificationPolicy takes YAML and parses it into a *v1alpha1.VerificationPolicy
148181
func MustParseVerificationPolicy(t *testing.T, yaml string) *v1alpha1.VerificationPolicy {
149182
t.Helper()

0 commit comments

Comments
 (0)