Skip to content

Commit cf4aa54

Browse files
committed
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 da12202 commit cf4aa54

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
@@ -15918,7 +15918,7 @@ spec:
1591815918
func TestReconcile_verifyResolved_V1beta1Pipeline_NoError(t *testing.T) {
1591915919
resolverName := "foobar"
1592015920

15921-
ts := parse.MustParseV1beta1Task(t, `
15921+
ts := parse.MustParseV1beta1TaskAndSetDefaults(t, `
1592215922
metadata:
1592315923
name: test-task
1592415924
namespace: foo
@@ -15942,7 +15942,7 @@ spec:
1594215942
t.Fatal("fail to marshal task", err)
1594315943
}
1594415944

15945-
ps := parse.MustParseV1beta1Pipeline(t, fmt.Sprintf(`
15945+
ps := parse.MustParseV1beta1PipelineAndSetDefaults(t, fmt.Sprintf(`
1594615946
metadata:
1594715947
name: test-pipeline
1594815948
namespace: foo
@@ -16252,7 +16252,7 @@ spec:
1625216252
func TestReconcile_verifyResolved_V1Pipeline_NoError(t *testing.T) {
1625316253
resolverName := "foobar"
1625416254

16255-
ts := parse.MustParseV1Task(t, `
16255+
ts := parse.MustParseV1TaskAndSetDefaults(t, `
1625616256
metadata:
1625716257
name: test-task
1625816258
namespace: foo
@@ -16276,7 +16276,7 @@ spec:
1627616276
t.Fatal("fail to marshal task", err)
1627716277
}
1627816278

16279-
ps := parse.MustParseV1Pipeline(t, fmt.Sprintf(`
16279+
ps := parse.MustParseV1PipelineAndSetDefaults(t, fmt.Sprintf(`
1628016280
metadata:
1628116281
name: test-pipeline
1628216282
namespace: foo
@@ -16405,7 +16405,7 @@ func TestReconcile_verifyResolved_V1Pipeline_Error(t *testing.T) {
1640516405
resolverName := "foobar"
1640616406

1640716407
// Case1: unsigned Pipeline refers to unsigned Task
16408-
unsignedTask := parse.MustParseV1beta1Task(t, `
16408+
unsignedTask := parse.MustParseV1beta1TaskAndSetDefaults(t, `
1640916409
metadata:
1641016410
name: test-task
1641116411
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
@@ -315,31 +315,31 @@ func TestGetPipelineFunc_RemoteResolution(t *testing.T) {
315315
"apiVersion: tekton.dev/v1beta1",
316316
pipelineYAMLString,
317317
}, "\n"),
318-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLString),
318+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLString),
319319
}, {
320320
name: "v1 pipeline",
321321
pipelineYAML: strings.Join([]string{
322322
"kind: Pipeline",
323323
"apiVersion: tekton.dev/v1",
324324
pipelineYAMLString,
325325
}, "\n"),
326-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLString),
326+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLString),
327327
}, {
328328
name: "v1 remote pipeline without defaults",
329329
pipelineYAML: strings.Join([]string{
330330
"kind: Pipeline",
331331
"apiVersion: tekton.dev/v1",
332332
pipelineYAMLStringWithoutDefaults,
333333
}, "\n"),
334-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLStringWithoutDefaults),
334+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLStringWithoutDefaults),
335335
}, {
336336
name: "v1 remote pipeline with different namespace",
337337
pipelineYAML: strings.Join([]string{
338338
"kind: Pipeline",
339339
"apiVersion: tekton.dev/v1",
340340
pipelineYAMLStringNamespaceFoo,
341341
}, "\n"),
342-
wantPipeline: parse.MustParseV1Pipeline(t, pipelineYAMLStringNamespaceFoo),
342+
wantPipeline: parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLStringNamespaceFoo),
343343
}}
344344
for _, tc := range testcases {
345345
t.Run(tc.name, func(t *testing.T) {
@@ -432,7 +432,7 @@ func TestGetPipelineFunc_RemoteResolution_ReplacedParams(t *testing.T) {
432432
ctx, clients := getFakePipelineClient(t)
433433
cfg := config.FromContextOrDefaults(ctx)
434434
ctx = config.ToContext(ctx, cfg)
435-
pipeline := parse.MustParseV1Pipeline(t, pipelineYAMLString)
435+
pipeline := parse.MustParseV1PipelineAndSetDefaults(t, pipelineYAMLString)
436436
pipelineRef := &v1.PipelineRef{
437437
ResolverRef: v1.ResolverRef{
438438
Resolver: "git",
@@ -1307,9 +1307,21 @@ var pipelineYAMLString = `
13071307
metadata:
13081308
name: foo
13091309
spec:
1310+
params:
1311+
- name: array
1312+
# type: array
1313+
default:
1314+
- "bar"
1315+
- "bar"
13101316
tasks:
13111317
- name: task1
13121318
taskSpec:
1319+
params:
1320+
- name: array
1321+
# type: array
1322+
default:
1323+
- "bar"
1324+
- "bar"
13131325
steps:
13141326
- name: step1
13151327
image: ubuntu

pkg/reconciler/taskrun/resources/taskref.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ func resolveStepAction(ctx context.Context, resolver remote.Resolver, name, name
192192
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) {
193193
switch obj := obj.(type) {
194194
case *v1beta1.Task:
195+
obj.SetDefaults(ctx)
195196
// 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
196197
vr := trustedresources.VerifyResource(ctx, obj, k8s, refSource, verificationPolicies)
197198
// Issue a dry-run request to create the remote Task, so that it can undergo validation from validating admission webhooks
@@ -210,6 +211,7 @@ func readRuntimeObjectAsTask(ctx context.Context, namespace string, obj runtime.
210211
}
211212
return t, &vr, nil
212213
case *v1beta1.ClusterTask:
214+
obj.SetDefaults(ctx)
213215
t, err := convertClusterTaskToTask(ctx, *obj)
214216
// Issue a dry-run request to create the remote Task, so that it can undergo validation from validating admission webhooks
215217
// without actually creating the Task on the cluster
@@ -218,6 +220,9 @@ func readRuntimeObjectAsTask(ctx context.Context, namespace string, obj runtime.
218220
}
219221
return t, nil, err
220222
case *v1.Task:
223+
// This SetDefaults is currently not necessary, but for consistency, it is recommended to add it.
224+
// Avoid forgetting to add it in the future when there is a v2 version, causing similar problems.
225+
obj.SetDefaults(ctx)
221226
vr := trustedresources.VerifyResource(ctx, obj, k8s, refSource, verificationPolicies)
222227
// Issue a dry-run request to create the remote Task, so that it can undergo validation from validating admission webhooks
223228
// 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
@@ -807,31 +807,31 @@ func TestGetTaskFunc_RemoteResolution(t *testing.T) {
807807
"apiVersion: tekton.dev/v1beta1",
808808
taskYAMLString,
809809
}, "\n"),
810-
wantTask: parse.MustParseV1Task(t, taskYAMLString),
810+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString),
811811
}, {
812812
name: "v1beta1 cluster task",
813813
taskYAML: strings.Join([]string{
814814
"kind: ClusterTask",
815815
"apiVersion: tekton.dev/v1beta1",
816816
taskYAMLString,
817817
}, "\n"),
818-
wantTask: parse.MustParseV1Task(t, taskYAMLString),
818+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString),
819819
}, {
820820
name: "v1 task",
821821
taskYAML: strings.Join([]string{
822822
"kind: Task",
823823
"apiVersion: tekton.dev/v1",
824824
taskYAMLString,
825825
}, "\n"),
826-
wantTask: parse.MustParseV1Task(t, taskYAMLString),
826+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString),
827827
}, {
828828
name: "v1 task without defaults",
829829
taskYAML: strings.Join([]string{
830830
"kind: Task",
831831
"apiVersion: tekton.dev/v1",
832832
remoteTaskYamlWithoutDefaults,
833833
}, "\n"),
834-
wantTask: parse.MustParseV1Task(t, remoteTaskYamlWithoutDefaults),
834+
wantTask: parse.MustParseV1TaskAndSetDefaults(t, remoteTaskYamlWithoutDefaults),
835835
}}
836836
for _, tc := range testcases {
837837
t.Run(tc.name, func(t *testing.T) {
@@ -934,7 +934,7 @@ func TestGetTaskFunc_RemoteResolution_ReplacedParams(t *testing.T) {
934934
ctx := context.Background()
935935
cfg := config.FromContextOrDefaults(ctx)
936936
ctx = config.ToContext(ctx, cfg)
937-
task := parse.MustParseV1Task(t, taskYAMLString)
937+
task := parse.MustParseV1TaskAndSetDefaults(t, taskYAMLString)
938938
taskRef := &v1.TaskRef{
939939
ResolverRef: v1.ResolverRef{
940940
Resolver: "git",
@@ -1639,6 +1639,12 @@ var taskYAMLString = `
16391639
metadata:
16401640
name: foo
16411641
spec:
1642+
params:
1643+
- name: array
1644+
# type: array
1645+
default:
1646+
- "bar"
1647+
- "bar"
16421648
steps:
16431649
- name: step1
16441650
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)