Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
debug

example passes

tests added

result ref coverage up

entrypointer tests added
  • Loading branch information
chitrangpatel committed Dec 14, 2023
1 parent 5e3b0dc commit 8bdd145
Show file tree
Hide file tree
Showing 10 changed files with 809 additions and 16 deletions.
66 changes: 66 additions & 0 deletions examples/v1/taskruns/alpha/stepaction-passing-results.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: step-action
spec:
params:
- name: param1
type: array
- name: param2
type: string
- name: param3
type: object
properties:
IMAGE_URL:
type: string
IMAGE_DIGEST:
type: string
image: bash:3.2
env:
- name: STRINGPARAM
value: $(params.param2)
args: [
"$(params.param1[*])",
"$(params.param1[0])",
"$(params.param3.IMAGE_URL)",
"$(params.param3.IMAGE_DIGEST)",
]
script: |
echo "$@"
echo "$STRINGPARAM"
---
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: step-action-run
spec:
TaskSpec:
steps:
- name: inline-step
results:
- name: result1
type: array
- name: result2
type: string
- name: result3
type: object
properties:
IMAGE_URL:
type: string
IMAGE_DIGEST:
type: string
image: alpine
script: |
echo -n "[\"image1\", \"image2\", \"image3\"]" | tee $(step.results.result1.path)
echo -n "foo" | tee $(step.results.result2.path)
echo -n "{\"IMAGE_URL\":\"ar.com\", \"IMAGE_DIGEST\":\"sha234\"}" | tee $(step.results.result3.path)
- name: action-runner
ref:
name: step-action
params:
- name: param1
value: $(steps.inline-step.results.result1[*])
- name: param2
value: $(steps.inline-step.results.result2)
- name: param3
value: $(steps.inline-step.results.result3[*])
2 changes: 1 addition & 1 deletion pkg/apis/pipeline/v1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ func taskContainsResult(resultExpression string, pipelineTaskNames sets.String,
for _, expression := range split {
if expression != "" {
value := stripVarSubExpression("$" + expression)
pipelineTaskName, _, _, _, err := parseExpression(value)
pipelineTaskName, _, _, _, _, err := parseExpression(value)

if err != nil {
return false
Expand Down
53 changes: 42 additions & 11 deletions pkg/apis/pipeline/v1/resultref.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ type ResultRef struct {
}

const (
resultExpressionFormat = "tasks.<taskName>.results.<resultName>"
resultExpressionFormat = "tasks.<taskName>.results.<resultName>"
stepResultExpressionFormat = "steps.<stepName>.results.<resultName>"
// Result expressions of the form <resultName>.<attribute> will be treated as object results.
// If a string result name contains a dot, brackets should be used to differentiate it from an object result.
// https://github.com/tektoncd/community/blob/main/teps/0075-object-param-and-result-types.md#collisions-with-builtin-variable-replacement
objectResultExpressionFormat = "tasks.<taskName>.results.<objectResultName>.<individualAttribute>"
objectResultExpressionFormat = "tasks.<taskName>.results.<objectResultName>.<individualAttribute>"
objectStepResultExpressionFormat = "steps.<stepName>.results.<objectResultName>.<individualAttribute>"
// ResultStepPart Constant used to define the "steps" part of a step result reference
ResultStepPart = "steps"
// ResultTaskPart Constant used to define the "tasks" part of a pipeline result reference
ResultTaskPart = "tasks"
// ResultFinallyPart Constant used to define the "finally" part of a pipeline result reference
Expand Down Expand Up @@ -69,9 +73,9 @@ var arrayIndexingRegex = regexp.MustCompile(arrayIndexing)
func NewResultRefs(expressions []string) []*ResultRef {
var resultRefs []*ResultRef
for _, expression := range expressions {
pipelineTask, result, index, property, err := parseExpression(expression)
pipelineTask, result, index, property, _, err := parseTaskExpression(expression)
// If the expression isn't a result but is some other expression,
// parseExpression will return an error, in which case we just skip that expression,
// parseTaskExpression will return an error, in which case we just skip that expression,
// since although it's not a result ref, it might be some other kind of reference
if err == nil {
resultRefs = append(resultRefs, &ResultRef{
Expand Down Expand Up @@ -105,6 +109,13 @@ func looksLikeResultRef(expression string) bool {
return len(subExpressions) >= 4 && (subExpressions[0] == ResultTaskPart || subExpressions[0] == ResultFinallyPart) && subExpressions[2] == ResultResultPart
}

// looksLikeStepResultRef attempts to check if the given string looks like it contains any
// step result references. Returns true if it does, false otherwise
func looksLikeStepResultRef(expression string) bool {
subExpressions := strings.Split(expression, ".")
return len(subExpressions) >= 4 && subExpressions[0] == ResultStepPart && subExpressions[2] == ResultResultPart
}

func validateString(value string) []string {
expressions := VariableSubstitutionRegex.FindAllString(value, -1)
if expressions == nil {
Expand Down Expand Up @@ -138,24 +149,44 @@ func stripVarSubExpression(expression string) string {
// - Input: tasks.myTask.results.resultName.foo.bar
// - Output: "", "", nil, "", error
// TODO: may use regex for each type to handle possible reference formats
func parseExpression(substitutionExpression string) (string, string, *int, string, error) {
if looksLikeResultRef(substitutionExpression) {
func parseExpression(substitutionExpression string) (string, string, *int, string, ParamType, error) {
if looksLikeResultRef(substitutionExpression) || looksLikeStepResultRef(substitutionExpression) {
subExpressions := strings.Split(substitutionExpression, ".")
// For string result: tasks.<taskName>.results.<stringResultName>
// For string step result: steps.<stepName>.results.<stringResultName>
// For array result: tasks.<taskName>.results.<arrayResultName>[index]
// For array step result: steps.<stepName>.results.<arrayResultName>[index]
if len(subExpressions) == 4 {
resultName, stringIdx := ParseResultName(subExpressions[3])
if stringIdx != "" && stringIdx != "*" {
if stringIdx != "" {
if stringIdx == "*" {
return subExpressions[1], resultName, nil, "", ParamTypeArray, nil
}
intIdx, _ := strconv.Atoi(stringIdx)
return subExpressions[1], resultName, &intIdx, "", nil
return subExpressions[1], resultName, &intIdx, "", ParamTypeArray, nil
}
return subExpressions[1], resultName, nil, "", nil
return subExpressions[1], resultName, nil, "", ParamTypeString, nil
} else if len(subExpressions) == 5 {
// For object type result: tasks.<taskName>.results.<objectResultName>.<individualAttribute>
return subExpressions[1], subExpressions[3], nil, subExpressions[4], nil
// For object type step result: steps.<stepName>.results.<objectResultName>.<individualAttribute>
return subExpressions[1], subExpressions[3], nil, subExpressions[4], ParamTypeObject, nil
}
}
return "", "", nil, "", fmt.Errorf("must be one of the form 1). %q; 2). %q", resultExpressionFormat, objectResultExpressionFormat)
return "", "", nil, "", ParamTypeString, fmt.Errorf("must be one of the form 1). %q; 2). %q; 3). %q; 4). %q", resultExpressionFormat, objectResultExpressionFormat, stepResultExpressionFormat, objectStepResultExpressionFormat)
}

func parseTaskExpression(substitutionExpression string) (string, string, *int, string, ParamType, error) {
if looksLikeResultRef(substitutionExpression) {
return parseExpression(substitutionExpression)
}
return "", "", nil, "", ParamTypeString, fmt.Errorf("must be one of the form 1). %q; 2). %q", resultExpressionFormat, objectResultExpressionFormat)
}

func ParseStepExpression(substitutionExpression string) (string, string, *int, string, ParamType, error) {
if looksLikeStepResultRef(substitutionExpression) {
return parseExpression(substitutionExpression)
}
return "", "", nil, "", ParamTypeString, fmt.Errorf("must be one of the form 1). %q; 2). %q", stepResultExpressionFormat, objectStepResultExpressionFormat)
}

// ParseResultName parse the input string to extract resultName and result index.
Expand Down
121 changes: 121 additions & 0 deletions pkg/apis/pipeline/v1/resultref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1_test

import (
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -295,6 +296,126 @@ func TestHasResultReference(t *testing.T) {
}
}

func TestParseStepExpression(t *testing.T) {
for _, tt := range []struct {
name string
param v1.Param
wantStep string
wantResult string
wantIdx int
wantAttribute string
wantParamType v1.ParamType
wantError error
}{{
name: "a string step result ref",
param: v1.Param{
Name: "param",
Value: *v1.NewStructuredValues("$(steps.sumSteps.results.sumResult)"),
},
wantStep: "sumSteps",
wantResult: "sumResult",
wantIdx: -1,
wantAttribute: "",
wantParamType: v1.ParamTypeString,
wantError: nil,
}, {
name: "an array step result ref with idx",
param: v1.Param{
Name: "param",
Value: *v1.NewStructuredValues("$(steps.sumSteps.results.sumResult[1000])"),
},
wantStep: "sumSteps",
wantResult: "sumResult",
wantIdx: 1000,
wantAttribute: "",
wantParamType: v1.ParamTypeArray,
wantError: nil,
}, {
name: "an array step result ref with [*]",
param: v1.Param{
Name: "param",
Value: *v1.NewStructuredValues("$(steps.sumSteps.results.sumResult[*])"),
},
wantStep: "sumSteps",
wantResult: "sumResult",
wantIdx: -1,
wantAttribute: "",
wantParamType: v1.ParamTypeArray,
wantError: nil,
}, {
name: "an object step result ref with attribute",
param: v1.Param{
Name: "param",
Value: *v1.NewStructuredValues("$(steps.sumSteps.results.sumResult.sum)"),
},
wantStep: "sumSteps",
wantResult: "sumResult",
wantIdx: -1,
wantAttribute: "sum",
wantParamType: v1.ParamTypeObject,
wantError: nil,
}, {
name: "an invalid step result ref",
param: v1.Param{
Name: "param",
Value: *v1.NewStructuredValues("$(steps.sumSteps.results.sumResult.foo.bar)"),
},
wantStep: "",
wantResult: "",
wantIdx: -1,
wantAttribute: "",
wantParamType: v1.ParamTypeString,
wantError: fmt.Errorf("must be one of the form 1). \"tasks.<taskName>.results.<resultName>\"; 2). \"tasks.<taskName>.results.<objectResultName>.<individualAttribute>\"; 3). \"steps.<stepName>.results.<resultName>\"; 4). \"steps.<stepName>.results.<objectResultName>.<individualAttribute>\""),
}, {
name: "not a step result ref",
param: v1.Param{
Name: "param",
Value: *v1.NewStructuredValues("$(tasks.sumSteps.results.sumResult.foo.bar)"),
},
wantStep: "",
wantResult: "",
wantIdx: -1,
wantAttribute: "",
wantParamType: v1.ParamTypeString,
wantError: fmt.Errorf("must be one of the form 1). \"steps.<stepName>.results.<resultName>\"; 2). \"steps.<stepName>.results.<objectResultName>.<individualAttribute>\""),
}} {
t.Run(tt.name, func(t *testing.T) {
expressions, _ := tt.param.GetVarSubstitutionExpressions()
gotStep, gotResult, gotIdx, gotAttribute, gotParamType, gotError := v1.ParseStepExpression(expressions[0])
if gotStep != tt.wantStep {
t.Errorf("ParseStepExpression() = %v, want %v", gotStep, tt.wantStep)
}
if gotResult != tt.wantResult {
t.Errorf("ParseStepExpression() = %v, want %v", gotResult, tt.wantResult)
}
if tt.wantIdx == -1 {
if gotIdx != nil {
t.Errorf("ParseStepExpression() = %v, want nil", gotIdx)
}
} else {
if *gotIdx != tt.wantIdx {
t.Errorf("ParseStepExpression() = %v, want %v", *gotIdx, tt.wantIdx)
}
}
if gotAttribute != tt.wantAttribute {
t.Errorf("ParseStepExpression() = %v, want %v", gotAttribute, tt.wantAttribute)
}
if gotParamType != tt.wantParamType {
t.Errorf("ParseStepExpression() = %v, want %v", gotParamType, tt.wantParamType)
}
if tt.wantError == nil {
if gotError != nil {
t.Errorf("ParseStepExpression() = %v, want nil", gotError)
}
} else {
if gotError.Error() != tt.wantError.Error() {
t.Errorf("ParseStepExpression() = \n%v\n, want \n%v", gotError.Error(), tt.wantError.Error())
}
}
})
}
}

func TestLooksLikeResultRef(t *testing.T) {
for _, tt := range []struct {
name string
Expand Down
Loading

0 comments on commit 8bdd145

Please sign in to comment.