Skip to content

Commit

Permalink
[TEP-0075] Validate object task result variable
Browse files Browse the repository at this point in the history
Prior to this commit, when providing param value with task result
variable, it only allowed using the task result as whole in the format
of `tasks.<taskName>.results.<resultName>` since result can
be only of type string previously.

As we are adding support for object type result, we need to support
using the result variable of object type in the format of
`tasks.<taskName>.results.<objectResultName>.<individualAttribute>`.

In this commit, we consider the object case in the validation webhook.
  • Loading branch information
chuangw6 committed May 16, 2022
1 parent 2cff700 commit e83b670
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 12 deletions.
9 changes: 8 additions & 1 deletion pkg/apis/pipeline/v1beta1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions pkg/apis/pipeline/v1beta1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,10 @@ func TestValidatePipelineResults_Success(t *testing.T) {
Name: "my-pipeline-result",
Description: "this is my pipeline result",
Value: "$(tasks.a-task.results.output)",
}, {
Name: "my-pipeline-object-result",
Description: "this is my pipeline result",
Value: "$(tasks.a-task.results.giturl.commit)",
}}
if err := validatePipelineResults(results); err != nil {
t.Errorf("Pipeline.validatePipelineResults() returned error for valid pipeline: %s: %v", desc, err)
Expand All @@ -1208,10 +1212,10 @@ func TestValidatePipelineResults_Failure(t *testing.T) {
results := []PipelineResult{{
Name: "my-pipeline-result",
Description: "this is my pipeline result",
Value: "$(tasks.a-task.results.output.output)",
Value: "$(tasks.a-task.results.output.key1.invalid)",
}}
expectedError := apis.FieldError{
Message: `invalid value: expected all of the expressions [tasks.a-task.results.output.output] to be result expressions but only [] were`,
Message: `invalid value: expected all of the expressions [tasks.a-task.results.output.key1.invalid] to be result expressions but only [] were`,
Paths: []string{"results[0].value"},
}
err := validatePipelineResults(results)
Expand Down
23 changes: 17 additions & 6 deletions pkg/apis/pipeline/v1beta1/resultref.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ import (
type ResultRef struct {
PipelineTask string `json:"pipelineTask"`
Result string `json:"result"`
Property string `json:"property"`
}

const (
resultExpressionFormat = "tasks.<taskName>.results.<resultName>"
resultExpressionFormat = "tasks.<taskName>.results.<resultName>"
objectResultExpressionFormat = "tasks.<taskName>.results.<objectResultName>.<individualAttribute>"
// ResultTaskPart Constant used to define the "tasks" part of a pipeline result reference
ResultTaskPart = "tasks"
// ResultResultPart Constant used to define the "results" part of a pipeline result reference
Expand All @@ -49,14 +51,15 @@ var resultNameFormatRegex = regexp.MustCompile(ResultNameFormat)
func NewResultRefs(expressions []string) []*ResultRef {
var resultRefs []*ResultRef
for _, expression := range expressions {
pipelineTask, result, err := parseExpression(expression)
pipelineTask, result, property, err := parseExpression(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,
// since although it's not a result ref, it might be some other kind of reference
if err == nil {
resultRefs = append(resultRefs, &ResultRef{
PipelineTask: pipelineTask,
Result: result,
Property: property,
})
}
}
Expand Down Expand Up @@ -94,6 +97,10 @@ func GetVarSubstitutionExpressionsForParam(param Param) ([]string, bool) {
case ParamTypeString:
// string type
allExpressions = append(allExpressions, validateString(param.Value.StringVal)...)
case ParamTypeObject:
for _, value := range param.Value.ObjectVal {
allExpressions = append(allExpressions, validateString(value)...)
}
default:
return nil, false
}
Expand Down Expand Up @@ -122,12 +129,16 @@ func stripVarSubExpression(expression string) string {
return strings.TrimSuffix(strings.TrimPrefix(expression, "$("), ")")
}

func parseExpression(substitutionExpression string) (string, string, error) {
func parseExpression(substitutionExpression string) (string, string, string, error) {
subExpressions := strings.Split(substitutionExpression, ".")
if len(subExpressions) != 4 || subExpressions[0] != ResultTaskPart || subExpressions[2] != ResultResultPart {
return "", "", fmt.Errorf("Must be of the form %q", resultExpressionFormat)
if len(subExpressions) < 4 || len(subExpressions) > 5 || subExpressions[0] != ResultTaskPart || subExpressions[2] != ResultResultPart {
return "", "", "", fmt.Errorf("Must be one of the form 1). %q; 2). %q", resultExpressionFormat, objectResultExpressionFormat)
}

if len(subExpressions) == 4 {
return subExpressions[1], subExpressions[3], "", nil
}
return subExpressions[1], subExpressions[3], nil
return subExpressions[1], subExpressions[3], subExpressions[4], nil
}

// PipelineTaskResultRefs walks all the places a result reference can be used
Expand Down
42 changes: 40 additions & 2 deletions pkg/apis/pipeline/v1beta1/resultref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ func TestNewResultReference(t *testing.T) {
PipelineTask: "sumTask",
Result: "sumResult",
}},
}, {
name: "Test valid expression with object result",
param: v1beta1.Param{
Name: "param",
Value: *v1beta1.NewArrayOrString("$(tasks.sumTask.results.sumResult.key1)"),
},
want: []*v1beta1.ResultRef{{
PipelineTask: "sumTask",
Result: "sumResult",
Property: "key1",
}},
}, {
name: "substitution within string",
param: v1beta1.Param{
Expand All @@ -55,27 +66,35 @@ func TestNewResultReference(t *testing.T) {
name: "multiple substitution",
param: v1beta1.Param{
Name: "param",
Value: *v1beta1.NewArrayOrString("$(tasks.sumTask1.results.sumResult) and another $(tasks.sumTask2.results.sumResult)"),
Value: *v1beta1.NewArrayOrString("$(tasks.sumTask1.results.sumResult) and another $(tasks.sumTask2.results.sumResult), last one $(tasks.sumTask3.results.sumResult.key1)"),
},
want: []*v1beta1.ResultRef{{
PipelineTask: "sumTask1",
Result: "sumResult",
}, {
PipelineTask: "sumTask2",
Result: "sumResult",
}, {
PipelineTask: "sumTask3",
Result: "sumResult",
Property: "key1",
}},
}, {
name: "multiple substitution with param",
param: v1beta1.Param{
Name: "param",
Value: *v1beta1.NewArrayOrString("$(params.param) $(tasks.sumTask1.results.sumResult) and another $(tasks.sumTask2.results.sumResult)"),
Value: *v1beta1.NewArrayOrString("$(params.param) $(tasks.sumTask1.results.sumResult) and another $(tasks.sumTask2.results.sumResult), last one $(tasks.sumTask3.results.sumResult.key1)"),
},
want: []*v1beta1.ResultRef{{
PipelineTask: "sumTask1",
Result: "sumResult",
}, {
PipelineTask: "sumTask2",
Result: "sumResult",
}, {
PipelineTask: "sumTask3",
Result: "sumResult",
Property: "key1",
}},
}, {
name: "first separator typo",
Expand Down Expand Up @@ -188,6 +207,25 @@ func TestHasResultReference(t *testing.T) {
PipelineTask: "sumTask2",
Result: "sumResult2",
}},
}, {
name: "Test valid expression in object",
param: v1beta1.Param{
Name: "param",
Value: *v1beta1.NewObject(map[string]string{
"key1": "$(tasks.sumTask1.results.sumResult1)",
"key2": "$(tasks.sumTask2.results.sumResult2) and another one $(tasks.sumTask3.results.sumResult3)",
}),
},
wantRef: []*v1beta1.ResultRef{{
PipelineTask: "sumTask1",
Result: "sumResult1",
}, {
PipelineTask: "sumTask2",
Result: "sumResult2",
}, {
PipelineTask: "sumTask3",
Result: "sumResult3",
}},
}} {
t.Run(tt.name, func(t *testing.T) {
expressions, ok := v1beta1.GetVarSubstitutionExpressionsForParam(tt.param)
Expand Down
7 changes: 6 additions & 1 deletion pkg/apis/pipeline/v1beta1/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1711,13 +1711,18 @@
"type": "object",
"required": [
"pipelineTask",
"result"
"result",
"property"
],
"properties": {
"pipelineTask": {
"type": "string",
"default": ""
},
"property": {
"type": "string",
"default": ""
},
"result": {
"type": "string",
"default": ""
Expand Down

0 comments on commit e83b670

Please sign in to comment.