Skip to content

Commit 1a01f6b

Browse files
aThorp96tekton-robot
authored andcommitted
fix: Fix remote task params default-value substitution
Fixes #8637 When substituting Params in a StepAction was initially implemented, the defaults were taken from the `TaskRun.Spec.TaskSpec.Params`. This works when the Task is defined in place, however if the TaskRun does not define the Task inline and instead references a remote Task from `TaskRun.Spec.TaskRef`, then any param defaults failed to be applied. When `GetStepActionFunc` is called from the TaskRun reconciler, it's passed a TaskSpec which comes from either the `TaskRun.Spec.TaskSpec` or a remotely-resolved TaskRef. By checking this TaskSpec for the Param defaults, instead of `TaskRun.Spec.TaskSpec`, we can ensure the defaults will always be applied when they're specified
1 parent 73b7953 commit 1a01f6b

File tree

5 files changed

+181
-13
lines changed

5 files changed

+181
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
apiVersion: tekton.dev/v1
2+
kind: Task
3+
metadata:
4+
# This has to be explicit instead of `generateName`, since it will be referenced
5+
# by the TaskRun
6+
name: example-default-task-param
7+
spec:
8+
params:
9+
- name: input
10+
default: "No input provided, but that's okay!"
11+
steps:
12+
- name: echo-input
13+
image: mirror.gcr.io/ubuntu
14+
script: |
15+
echo "$(params.input)"
16+
---
17+
apiVersion: tekton.dev/v1
18+
kind: TaskRun
19+
metadata:
20+
generateName: default-task-params-run-
21+
spec:
22+
taskRef:
23+
name: example-default-task-param
24+
# # Uncomment this block to override the default param value!
25+
# params:
26+
# - name: input
27+
# value: "You can supply the param from the TaskRun if the default not what you want"

pkg/reconciler/taskrun/resources/taskref.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,15 @@ func GetTaskFunc(ctx context.Context, k8s kubernetes.Interface, tekton clientset
138138
// It also requires a kubeclient, tektonclient, requester in case it needs to find that task in
139139
// cluster or authorize against an external repository. It will figure out whether it needs to look in the cluster or in
140140
// a remote location to fetch the reference.
141-
func GetStepActionFunc(tekton clientset.Interface, k8s kubernetes.Interface, requester remoteresource.Requester, tr *v1.TaskRun, step *v1.Step) GetStepAction {
141+
func GetStepActionFunc(tekton clientset.Interface, k8s kubernetes.Interface, requester remoteresource.Requester, tr *v1.TaskRun, taskSpec v1.TaskSpec, step *v1.Step) GetStepAction {
142142
trName := tr.Name
143143
namespace := tr.Namespace
144144
if step.Ref != nil && step.Ref.Resolver != "" && requester != nil {
145145
// Return an inline function that implements GetStepAction by calling Resolver.Get with the specified StepAction type and
146146
// casting it to a StepAction.
147147
return func(ctx context.Context, name string) (*v1beta1.StepAction, *v1.RefSource, error) {
148148
// Perform params replacements for StepAction resolver params
149-
ApplyParameterSubstitutionInResolverParams(tr, step)
149+
ApplyParameterSubstitutionInResolverParams(tr, taskSpec, step)
150150
resolverPayload := remoteresource.ResolverPayload{
151151
Name: trName,
152152
Namespace: namespace,
@@ -167,14 +167,14 @@ func GetStepActionFunc(tekton clientset.Interface, k8s kubernetes.Interface, req
167167
}
168168

169169
// ApplyParameterSubstitutionInResolverParams applies parameter substitutions in resolver params for Step Ref.
170-
func ApplyParameterSubstitutionInResolverParams(tr *v1.TaskRun, step *v1.Step) {
170+
func ApplyParameterSubstitutionInResolverParams(tr *v1.TaskRun, taskSpec v1.TaskSpec, step *v1.Step) {
171171
stringReplacements := make(map[string]string)
172172
arrayReplacements := make(map[string][]string)
173173
objectReplacements := make(map[string]map[string]string)
174-
if tr.Spec.TaskSpec != nil {
175-
defaultSR, defaultAR, defaultOR := replacementsFromDefaultParams(tr.Spec.TaskSpec.Params)
176-
stringReplacements, arrayReplacements, objectReplacements = extendReplacements(stringReplacements, arrayReplacements, objectReplacements, defaultSR, defaultAR, defaultOR)
177-
}
174+
175+
defaultSR, defaultAR, defaultOR := replacementsFromDefaultParams(taskSpec.Params)
176+
stringReplacements, arrayReplacements, objectReplacements = extendReplacements(stringReplacements, arrayReplacements, objectReplacements, defaultSR, defaultAR, defaultOR)
177+
178178
paramSR, paramAR, paramOR := replacementsFromParams(tr.Spec.Params)
179179
stringReplacements, arrayReplacements, objectReplacements = extendReplacements(stringReplacements, arrayReplacements, objectReplacements, paramSR, paramAR, paramOR)
180180
step.Ref.Params = step.Ref.Params.ReplaceVariables(stringReplacements, arrayReplacements, objectReplacements)

pkg/reconciler/taskrun/resources/taskref_test.go

+48-5
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ func TestStepActionResolverParamReplacements(t *testing.T) {
347347
name string
348348
namespace string
349349
taskrun *v1.TaskRun
350+
taskSpec *v1.TaskSpec
350351
want *v1.Step
351352
}{{
352353
name: "default taskspec parms",
@@ -585,11 +586,53 @@ func TestStepActionResolverParamReplacements(t *testing.T) {
585586
},
586587
},
587588
},
589+
}, {
590+
name: "defaults from remote task",
591+
namespace: "default",
592+
taskrun: &v1.TaskRun{
593+
ObjectMeta: metav1.ObjectMeta{Name: "some-tr"},
594+
Spec: v1.TaskRunSpec{
595+
TaskRef: &v1.TaskRef{
596+
Name: "resolved-task-name",
597+
},
598+
},
599+
},
600+
taskSpec: &v1.TaskSpec{
601+
Params: []v1.ParamSpec{{
602+
Name: "resolver-param",
603+
Default: v1.NewStructuredValues("foo/bar"),
604+
}},
605+
Steps: []v1.Step{{
606+
Ref: &v1.Ref{
607+
ResolverRef: v1.ResolverRef{
608+
Resolver: "git",
609+
Params: []v1.Param{{
610+
Name: "pathInRepo",
611+
Value: *v1.NewStructuredValues("$(params.resolver-param)"),
612+
}},
613+
},
614+
},
615+
}},
616+
},
617+
want: &v1.Step{
618+
Ref: &v1.Ref{
619+
ResolverRef: v1.ResolverRef{
620+
Resolver: "git",
621+
Params: []v1.Param{{
622+
Name: "pathInRepo",
623+
Value: *v1.NewStructuredValues("foo/bar"),
624+
}},
625+
},
626+
},
627+
},
588628
}}
589629
for _, tc := range testcases {
590630
t.Run(tc.name, func(t *testing.T) {
591-
step := &tc.taskrun.Spec.TaskSpec.Steps[0]
592-
resources.ApplyParameterSubstitutionInResolverParams(tc.taskrun, step)
631+
if tc.taskSpec == nil {
632+
tc.taskSpec = tc.taskrun.Spec.TaskSpec
633+
}
634+
step := &tc.taskSpec.Steps[0]
635+
resources.ApplyParameterSubstitutionInResolverParams(tc.taskrun, *tc.taskSpec, step)
593636
if d := cmp.Diff(tc.want, step); tc.want != nil && d != "" {
594637
t.Error(diff.PrintWantGot(d))
595638
}
@@ -861,7 +904,7 @@ func TestGetStepActionFunc_Local(t *testing.T) {
861904
for _, tc := range testcases {
862905
t.Run(tc.name, func(t *testing.T) {
863906
tektonclient := fake.NewSimpleClientset(tc.localStepActions...)
864-
fn := resources.GetStepActionFunc(tektonclient, nil, nil, tc.taskRun, &tc.taskRun.Spec.TaskSpec.Steps[0])
907+
fn := resources.GetStepActionFunc(tektonclient, nil, nil, tc.taskRun, *tc.taskRun.Spec.TaskSpec, &tc.taskRun.Spec.TaskSpec.Steps[0])
865908

866909
stepAction, refSource, err := fn(ctx, tc.taskRun.Spec.TaskSpec.Steps[0].Ref.Name)
867910
if err != nil {
@@ -922,7 +965,7 @@ func TestGetStepActionFunc_RemoteResolution_Success(t *testing.T) {
922965
},
923966
}
924967
tektonclient := fake.NewSimpleClientset()
925-
fn := resources.GetStepActionFunc(tektonclient, nil, requester, tr, &tr.Spec.TaskSpec.Steps[0])
968+
fn := resources.GetStepActionFunc(tektonclient, nil, requester, tr, *tr.Spec.TaskSpec, &tr.Spec.TaskSpec.Steps[0])
926969

927970
resolvedStepAction, resolvedRefSource, err := fn(ctx, tr.Spec.TaskSpec.Steps[0].Ref.Name)
928971
if tc.wantErr {
@@ -983,7 +1026,7 @@ func TestGetStepActionFunc_RemoteResolution_Error(t *testing.T) {
9831026
},
9841027
}
9851028
tektonclient := fake.NewSimpleClientset()
986-
fn := resources.GetStepActionFunc(tektonclient, nil, requester, tr, &tr.Spec.TaskSpec.Steps[0])
1029+
fn := resources.GetStepActionFunc(tektonclient, nil, requester, tr, *tr.Spec.TaskSpec, &tr.Spec.TaskSpec.Steps[0])
9871030
if _, _, err := fn(ctx, tr.Spec.TaskSpec.Steps[0].Ref.Name); err == nil {
9881031
t.Fatalf("expected error due to invalid pipeline data but saw none")
9891032
}

pkg/reconciler/taskrun/resources/taskspec.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func GetStepActionsData(ctx context.Context, taskSpec v1.TaskSpec, taskRun *v1.T
107107
for i, step := range taskSpec.Steps {
108108
s := step.DeepCopy()
109109
if step.Ref != nil {
110-
getStepAction := GetStepActionFunc(tekton, k8s, requester, taskRun, s)
110+
getStepAction := GetStepActionFunc(tekton, k8s, requester, taskRun, taskSpec, s)
111111
stepAction, source, err := getStepAction(ctx, s.Ref.Name)
112112
if err != nil {
113113
return nil, err

test/taskrun_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -604,3 +604,101 @@ spec:
604604
t.Fatalf("expected 1 retry status, got %d", len(taskrun.Status.RetriesStatus))
605605
}
606606
}
607+
608+
func TestTaskRunResolveDefaultParameterSubstitutionOnStepAction(t *testing.T) {
609+
ctx := context.Background()
610+
ctx, cancel := context.WithCancel(ctx)
611+
defer cancel()
612+
613+
c, namespace := setup(ctx, t, requireAllGates(requireEnableStepActionsGate))
614+
615+
knativetest.CleanupOnInterrupt(func() { tearDown(ctx, t, c, namespace) }, t.Logf)
616+
defer tearDown(ctx, t, c, namespace)
617+
618+
t.Logf("Creating Task and TaskRun in namespace %s", namespace)
619+
task := parse.MustParseV1Task(t, fmt.Sprintf(`
620+
metadata:
621+
name: %s
622+
namespace: %s
623+
spec:
624+
params:
625+
- name: repository
626+
type: string
627+
default: https://github.com/tektoncd/catalog.git
628+
- name: revision
629+
type: string
630+
default: main
631+
steps:
632+
- name: clone
633+
ref:
634+
resolver: git
635+
params:
636+
- name: url
637+
value: "$(params.repository)"
638+
- name: pathInRepo
639+
value: /stepaction/git-clone/0.1/git-clone.yaml
640+
- name: revision
641+
value: "$(params.revision)"
642+
params:
643+
- name: output-path
644+
value: "/tmp"
645+
- name: url
646+
value: $(params.repository)
647+
- name: revision
648+
value: $(params.revision)
649+
`, helpers.ObjectNameForTest(t), namespace))
650+
if _, err := c.V1TaskClient.Create(ctx, task, metav1.CreateOptions{}); err != nil {
651+
t.Fatalf("Failed to create Task: %s", err)
652+
}
653+
654+
taskRunName := helpers.ObjectNameForTest(t)
655+
taskRun := parse.MustParseV1TaskRun(t, fmt.Sprintf(`
656+
metadata:
657+
name: %s
658+
namespace: %s
659+
spec:
660+
taskRef:
661+
name: %s
662+
retries: 1
663+
`, taskRunName, namespace, task.Name))
664+
if _, err := c.V1TaskRunClient.Create(ctx, taskRun, metav1.CreateOptions{}); err != nil {
665+
t.Fatalf("Failed to create TaskRun: %s", err)
666+
}
667+
668+
t.Logf("Waiting for TaskRun in namespace %s to complete", namespace)
669+
if err := WaitForTaskRunState(ctx, c, taskRunName, TaskRunSucceed(taskRunName), "TaskRunSucceed", v1Version); err != nil {
670+
t.Errorf("Error waiting for TaskRun to finish: %s", err)
671+
}
672+
673+
taskrun, err := c.V1TaskRunClient.Get(ctx, taskRunName, metav1.GetOptions{})
674+
if err != nil {
675+
t.Fatalf("Couldn't get expected TaskRun %s: %s", taskRunName, err)
676+
}
677+
678+
if !isSuccessful(t, taskrun.GetName(), taskrun.Status.Conditions) {
679+
t.Fatalf("task should have succeeded")
680+
}
681+
682+
expectedReason := "Succeeded"
683+
actualReason := taskrun.Status.GetCondition(apis.ConditionSucceeded).GetReason()
684+
if actualReason != expectedReason {
685+
t.Fatalf("expected TaskRun to have failed reason %s, got %s", expectedReason, actualReason)
686+
}
687+
688+
expectedStepState := []v1.StepState{{
689+
ContainerState: corev1.ContainerState{
690+
Terminated: &corev1.ContainerStateTerminated{
691+
ExitCode: 0,
692+
Reason: "Completed",
693+
},
694+
},
695+
TerminationReason: "Completed",
696+
Name: "clone",
697+
Container: "step-clone",
698+
}}
699+
ignoreTerminatedFields := cmpopts.IgnoreFields(corev1.ContainerStateTerminated{}, "StartedAt", "FinishedAt", "ContainerID", "Message")
700+
ignoreStepFields := cmpopts.IgnoreFields(v1.StepState{}, "ImageID", "Results", "Provenance")
701+
if d := cmp.Diff(taskrun.Status.Steps, expectedStepState, ignoreTerminatedFields, ignoreStepFields); d != "" {
702+
t.Fatalf("-got, +want: %v", d)
703+
}
704+
}

0 commit comments

Comments
 (0)