Skip to content

Commit

Permalink
TEP-0118: Add validation for matrix pipeline context parameter variab…
Browse files Browse the repository at this point in the history
…les include Matrix.Include.Params

[TEP-0090: Matrix] introduced `Matrix` to the `PipelineTask` specification such that the `PipelineTask` executes a list of `TaskRuns` or `Runs` in parallel with the specified list of inputs for a `Parameter` or with different combinations of the inputs for a set of `Parameters`.

To build on this, Tep-0018 introduced Matrix.Include, which allows passing in a specific combinations of `Parameters` into the `Matrix`.

This commit validates that matrix pipeline context  parameter variables including include params are used appropriately.

Note: This is still in preview mode. Other forms of validation and implementation logic will be added in subsequent commits.
  • Loading branch information
EmmaMunley committed Feb 28, 2023
1 parent 5cc8bf2 commit ee1fcb1
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 0 deletions.
12 changes: 12 additions & 0 deletions pkg/apis/pipeline/v1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,25 @@ func validatePipelineContextVariables(tasks []PipelineTask) *apis.FieldError {
var paramValues []string
for _, task := range tasks {
var matrixParams []Param
var includeParams []Param
if task.IsMatrixed() {
matrixParams = task.Matrix.Params
if task.Matrix.MatrixHasInclude() {
for _, include := range task.Matrix.Include {
includeParams = include.Params
}
}
}
for _, param := range append(task.Params, matrixParams...) {
paramValues = append(paramValues, param.Value.StringVal)
paramValues = append(paramValues, param.Value.ArrayVal...)
}

if task.Matrix.MatrixHasInclude() {
for _, param := range append(task.Params, includeParams...) {
paramValues = append(paramValues, param.Value.StringVal)
}
}
}
errs := validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipelineRun", pipelineRunContextNames).
Also(validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipeline", pipelineContextNames)).
Expand Down
112 changes: 112 additions & 0 deletions pkg/apis/pipeline/v1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2505,6 +2505,55 @@ func TestContextValid(t *testing.T) {
Name: "a-param-mat", Value: ParamValue{ArrayVal: []string{"$(context.pipelineTask.retries)"}},
}}},
}},
}, {
name: "valid string context variable for Pipeline name in include params",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipeline.name)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-mat", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipeline.name)"}}},
}}},
}},
}, {
name: "valid array context variables for Pipeline and PipelineRun names in matrix include",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{ArrayVal: []string{"$(context.pipeline.name)", "and", "$(context.pipelineRun.name)"}},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-mat-1", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipeline.name)"}}},
}, {
Name: "build-2",
Params: []Param{{
Name: "a-param-mat-2", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineRun.name)"}}},
}}},
}},
}, {
name: "valid string context variable for PipelineTask retries in matrix include",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipelineTask.retries)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-mat", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineTask.retries)"}}},
}}},
}},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -2599,6 +2648,69 @@ func TestContextInvalid(t *testing.T) {
Also(apis.ErrGeneric(`non-existent variable in "$(context.pipeline.missing-foo)"`, "value")).
Also(apis.ErrGeneric(`non-existent variable in "$(context.pipelineRun.missing-foo)"`, "value")).
Also(apis.ErrGeneric(`non-existent variable in "$(context.pipelineTask.missing-foo)"`, "value")),
}, {
name: "invalid string context variable for pipeline in include matrix",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipeline)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-foo", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipeline.missing)"}}},
}}},
}},
expectedError: *apis.ErrGeneric("").Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipeline.missing)"`,
Paths: []string{"value"},
}),
}, {
name: "invalid string context variable for pipelineRun",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipelineRun.missing)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-foo", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineRun.missing-foo)"}}},
}}},
}},
expectedError: *apis.ErrGeneric("").Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineRun.missing)"`,
Paths: []string{"value"},
}).Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineRun.missing-foo)"`,
Paths: []string{"value"},
}),
}, {
name: "invalid string context variable for pipelineTask",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipelineTask.missing)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-foo", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineTask.missing-foo)"}}},
}}},
}},
expectedError: *apis.ErrGeneric("").Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineTask.missing)"`,
Paths: []string{"value"},
}).Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineTask.missing-foo)"`,
Paths: []string{"value"},
}),
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
12 changes: 12 additions & 0 deletions pkg/apis/pipeline/v1beta1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,25 @@ func validatePipelineContextVariables(tasks []PipelineTask) *apis.FieldError {
var paramValues []string
for _, task := range tasks {
var matrixParams []Param
var includeParams []Param
if task.IsMatrixed() {
matrixParams = task.Matrix.Params
if task.Matrix.MatrixHasInclude() {
for _, include := range task.Matrix.Include {
includeParams = include.Params
}
}
}
for _, param := range append(task.Params, matrixParams...) {
paramValues = append(paramValues, param.Value.StringVal)
paramValues = append(paramValues, param.Value.ArrayVal...)
}

if task.Matrix.MatrixHasInclude() {
for _, param := range append(task.Params, includeParams...) {
paramValues = append(paramValues, param.Value.StringVal)
}
}
}
errs := validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipelineRun", pipelineRunContextNames).
Also(validatePipelineContextVariablesInParamValues(paramValues, "context\\.pipeline", pipelineContextNames)).
Expand Down
112 changes: 112 additions & 0 deletions pkg/apis/pipeline/v1beta1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2949,6 +2949,55 @@ func TestContextValid(t *testing.T) {
Name: "a-param-mat", Value: ParamValue{ArrayVal: []string{"$(context.pipelineTask.retries)"}},
}}},
}},
}, {
name: "valid string context variable for Pipeline name in include params",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipeline.name)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-mat", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipeline.name)"}}},
}}},
}},
}, {
name: "valid array context variables for Pipeline and PipelineRun names in matrix include",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{ArrayVal: []string{"$(context.pipeline.name)", "and", "$(context.pipelineRun.name)"}},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-mat-1", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipeline.name)"}}},
}, {
Name: "build-2",
Params: []Param{{
Name: "a-param-mat-2", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineRun.name)"}}},
}}},
}},
}, {
name: "valid string context variable for PipelineTask retries in matrix include",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipelineTask.retries)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-mat", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineTask.retries)"}}},
}}},
}},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -3043,6 +3092,69 @@ func TestContextInvalid(t *testing.T) {
Also(apis.ErrGeneric(`non-existent variable in "$(context.pipeline.missing-foo)"`, "value")).
Also(apis.ErrGeneric(`non-existent variable in "$(context.pipelineRun.missing-foo)"`, "value")).
Also(apis.ErrGeneric(`non-existent variable in "$(context.pipelineTask.missing-foo)"`, "value")),
}, {
name: "invalid string context variable for pipeline in include matrix",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipeline)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-foo", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipeline.missing)"}}},
}}},
}},
expectedError: *apis.ErrGeneric("").Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipeline.missing)"`,
Paths: []string{"value"},
}),
}, {
name: "invalid string context variable for pipelineRun",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipelineRun.missing)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-foo", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineRun.missing-foo)"}}},
}}},
}},
expectedError: *apis.ErrGeneric("").Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineRun.missing)"`,
Paths: []string{"value"},
}).Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineRun.missing-foo)"`,
Paths: []string{"value"},
}),
}, {
name: "invalid string context variable for pipelineTask",
tasks: []PipelineTask{{
Name: "bar",
TaskRef: &TaskRef{Name: "bar-task"},
Params: []Param{{
Name: "a-param", Value: ParamValue{StringVal: "$(context.pipelineTask.missing)"},
}},
Matrix: &Matrix{
Include: []MatrixInclude{{
Name: "build-1",
Params: []Param{{
Name: "a-param-foo", Value: ParamValue{Type: ParamTypeString, StringVal: "$(context.pipelineTask.missing-foo)"}}},
}}},
}},
expectedError: *apis.ErrGeneric("").Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineTask.missing)"`,
Paths: []string{"value"},
}).Also(&apis.FieldError{
Message: `non-existent variable in "$(context.pipelineTask.missing-foo)"`,
Paths: []string{"value"},
}),
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit ee1fcb1

Please sign in to comment.