Skip to content

Commit 21df3e5

Browse files
committed
introducing matrix.strategy
Signed-off-by: Priti Desai <[email protected]>
1 parent 5066b40 commit 21df3e5

15 files changed

+651
-3
lines changed

docs/pipeline-api.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,19 @@ IncludeParamsList
15421542
<p>Include is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix.</p>
15431543
</td>
15441544
</tr>
1545+
<tr>
1546+
<td>
1547+
<code>strategy</code><br/>
1548+
<em>
1549+
string
1550+
</em>
1551+
</td>
1552+
<td>
1553+
<em>(Optional)</em>
1554+
<p>Strategy is a JSON payload with a list of combinations
1555+
Strategy is an extension of Include to support dynamic combinations</p>
1556+
</td>
1557+
</tr>
15451558
</tbody>
15461559
</table>
15471560
<h3 id="tekton.dev/v1.OnErrorType">OnErrorType
@@ -4654,6 +4667,9 @@ More info: <a href="https://kubernetes.io/docs/tasks/configure-pod-container/sec
46544667
</p>
46554668
<div>
46564669
<p>TaskBreakpoints defines the breakpoint config for a particular Task</p>
4670+
<h3 id="tekton.dev/v1.Strategy">Strategy
4671+
</h3>
4672+
<div>
46574673
</div>
46584674
<table>
46594675
<thead>
@@ -4674,6 +4690,12 @@ string
46744690
<em>(Optional)</em>
46754691
<p>if enabled, pause TaskRun on failure of a step
46764692
failed step will not exit</p>
4693+
<code>include</code><br/>
4694+
<em>
4695+
[]string
4696+
</em>
4697+
</td>
4698+
<td>
46774699
</td>
46784700
</tr>
46794701
</tbody>
@@ -9520,6 +9542,19 @@ IncludeParamsList
95209542
<p>Include is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix.</p>
95219543
</td>
95229544
</tr>
9545+
<tr>
9546+
<td>
9547+
<code>strategy</code><br/>
9548+
<em>
9549+
string
9550+
</em>
9551+
</td>
9552+
<td>
9553+
<em>(Optional)</em>
9554+
<p>Strategy is a JSON payload with a list of combinations
9555+
Strategy is an extension of Include to support dynamic combinations</p>
9556+
</td>
9557+
</tr>
95239558
</tbody>
95249559
</table>
95259560
<h3 id="tekton.dev/v1beta1.OnErrorType">OnErrorType
@@ -13174,6 +13209,9 @@ Default is false.</p>
1317413209
</p>
1317513210
<div>
1317613211
<p>TaskBreakpoints defines the breakpoint config for a particular Task</p>
13212+
<h3 id="tekton.dev/v1beta1.Strategy">Strategy
13213+
</h3>
13214+
<div>
1317713215
</div>
1317813216
<table>
1317913217
<thead>
@@ -13194,6 +13232,12 @@ string
1319413232
<em>(Optional)</em>
1319513233
<p>if enabled, pause TaskRun on failure of a step
1319613234
failed step will not exit</p>
13235+
<code>include</code><br/>
13236+
<em>
13237+
[]string
13238+
</em>
13239+
</td>
13240+
<td>
1319713241
</td>
1319813242
</tr>
1319913243
</tbody>

pkg/apis/pipeline/v1/matrix_types.go

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package v1
1515

1616
import (
1717
"context"
18+
"encoding/json"
1819
"fmt"
1920
"sort"
2021

@@ -38,6 +39,12 @@ type Matrix struct {
3839
// +optional
3940
// +listType=atomic
4041
Include IncludeParamsList `json:"include,omitempty"`
42+
43+
// Strategy is a JSON payload with a list of combinations
44+
// Strategy is an extension of Include to support dynamic combinations
45+
// +optional
46+
// +listType=atomic
47+
Strategy string `json:"strategy,omitempty"`
4148
}
4249

4350
// IncludeParamsList is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix.
@@ -54,14 +61,21 @@ type IncludeParams struct {
5461
Params Params `json:"params,omitempty"`
5562
}
5663

64+
type Strategy struct {
65+
Include []map[string]string `json:"include,omitempty"`
66+
}
67+
5768
// Combination is a map, mainly defined to hold a single combination from a Matrix with key as param.Name and value as param.Value
5869
type Combination map[string]string
5970

6071
// Combinations is a Combination list
6172
type Combinations []Combination
6273

63-
// FanOut returns an list of params that represent combinations
74+
// FanOut returns a list of params that represent combinations
6475
func (m *Matrix) FanOut() []Params {
76+
if m.HasStrategy() {
77+
return m.getStrategy().toParams()
78+
}
6579
var combinations, includeCombinations Combinations
6680
includeCombinations = m.getIncludeCombinations()
6781
if m.HasInclude() && !m.HasParams() {
@@ -177,6 +191,23 @@ func (m *Matrix) getIncludeCombinations() Combinations {
177191
return combinations
178192
}
179193

194+
// getStrategy generates combinations based on Matrix Strategy section
195+
// Matrix Strategy allows "include" as a JSON payload
196+
func (m *Matrix) getStrategy() Combinations {
197+
var combinations Combinations
198+
var s Strategy
199+
if err := json.Unmarshal([]byte(m.Strategy), &s); err == nil {
200+
for _, i := range s.Include {
201+
newCombination := make(Combination)
202+
for k, v := range i {
203+
newCombination[k] = v
204+
}
205+
combinations = append(combinations, newCombination)
206+
}
207+
}
208+
return combinations
209+
}
210+
180211
// distribute generates a new Combination of Parameters by adding a new Parameter to an existing list of Combinations.
181212
func (cs Combinations) distribute(param Param) Combinations {
182213
var expandedCombinations Combinations
@@ -225,6 +256,9 @@ func (m *Matrix) CountCombinations() int {
225256
// Add any additional Combinations generated from Matrix Include Parameters
226257
count += m.countNewCombinationsFromInclude()
227258

259+
// Iterate over Matrix Strategy to count all combinations specified
260+
count += m.countCombinationsFromStrategy()
261+
228262
return count
229263
}
230264

@@ -267,6 +301,11 @@ func (m *Matrix) countNewCombinationsFromInclude() int {
267301
return count
268302
}
269303

304+
// countCombinationsFromStrategy returns the count of Combinations specified in the strategy
305+
func (m *Matrix) countCombinationsFromStrategy() int {
306+
return len(m.getStrategy())
307+
}
308+
270309
// HasInclude returns true if the Matrix has Include Parameters
271310
func (m *Matrix) HasInclude() bool {
272311
return m != nil && m.Include != nil && len(m.Include) > 0
@@ -277,6 +316,10 @@ func (m *Matrix) HasParams() bool {
277316
return m != nil && m.Params != nil && len(m.Params) > 0
278317
}
279318

319+
func (m *Matrix) HasStrategy() bool {
320+
return m != nil && len(m.Strategy) > 0
321+
}
322+
280323
// GetAllParams returns a list of all Matrix Parameters
281324
func (m *Matrix) GetAllParams() Params {
282325
var params Params
@@ -288,6 +331,12 @@ func (m *Matrix) GetAllParams() Params {
288331
params = append(params, include.Params...)
289332
}
290333
}
334+
if m.HasStrategy() {
335+
ps := m.getStrategy().toParams()
336+
for _, p := range ps {
337+
params = append(params, p...)
338+
}
339+
}
291340
return params
292341
}
293342

@@ -336,6 +385,18 @@ func (m *Matrix) validatePipelineParametersVariablesInMatrixParameters(prefix st
336385
}
337386
}
338387
}
388+
if m.HasStrategy() {
389+
var s Strategy
390+
if err := json.Unmarshal([]byte(m.Strategy), &s); err != nil {
391+
for _, i := range s.Include {
392+
for k, v := range i {
393+
// Matrix Strategy Params must be of type string
394+
errs = errs.Also(validateStringVariable(v, prefix, paramNames, arrayParamNames, objectParamNameKeys).ViaField(k).ViaField("matrix.strategy", k))
395+
396+
}
397+
}
398+
}
399+
}
339400
return errs
340401
}
341402

@@ -348,3 +409,21 @@ func (m *Matrix) validateParameterInOneOfMatrixOrParams(params []Param) (errs *a
348409
}
349410
return errs
350411
}
412+
413+
// validateStrategy validates syntax of Matrix Strategy section
414+
// Matrix Strategy allows "include" as a JSON payload with a list of combinations
415+
func (m *Matrix) validateStrategy() (errs *apis.FieldError) {
416+
e := "matrix.strategy section does not have a valid JSON payload, " +
417+
"matrix.strategy section only allows valid JSON payload in the form of " +
418+
"{include: []map[string]string} e.g. {include: [{k1: v1, k2: v2}, {k1: v3, k2: v4}, {k3: v1}]"
419+
var s Strategy
420+
if m.HasStrategy() {
421+
if !json.Valid([]byte(m.Strategy)) {
422+
return apis.ErrGeneric(e)
423+
}
424+
if err := json.Unmarshal([]byte(m.Strategy), &s); err != nil {
425+
return apis.ErrGeneric(e + ": " + err.Error())
426+
}
427+
}
428+
return errs
429+
}

pkg/apis/pipeline/v1/matrix_types_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,40 @@ func TestMatrix_FanOut(t *testing.T) {
582582
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "I-do-not-exist"},
583583
},
584584
}},
585+
}, {
586+
name: "Fan out explicit combinations using strategy",
587+
matrix: v1.Matrix{
588+
Strategy: "{\"include\": [" +
589+
"{\"DOCKERFILE\": \"path/to/Dockerfile1\", \"IMAGE\": \"image-1\"}," +
590+
"{\"DOCKERFILE\": \"path/to/Dockerfile2\", \"IMAGE\": \"image-2\"}," +
591+
"{\"DOCKERFILE\": \"path/to/Dockerfile3\", \"IMAGE\": \"image-3\"}" +
592+
"]}",
593+
},
594+
want: []v1.Params{{
595+
{
596+
Name: "DOCKERFILE",
597+
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "path/to/Dockerfile1"},
598+
}, {
599+
Name: "IMAGE",
600+
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "image-1"},
601+
},
602+
}, {
603+
{
604+
Name: "DOCKERFILE",
605+
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "path/to/Dockerfile2"},
606+
}, {
607+
Name: "IMAGE",
608+
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "image-2"},
609+
},
610+
}, {
611+
{
612+
Name: "DOCKERFILE",
613+
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "path/to/Dockerfile3"},
614+
}, {
615+
Name: "IMAGE",
616+
Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "image-3"},
617+
},
618+
}},
585619
}}
586620
for _, tt := range tests {
587621
t.Run(tt.name, func(t *testing.T) {

pkg/apis/pipeline/v1/openapi_generated.go

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/pipeline/v1/pipeline_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func (et *EmbeddedTask) IsCustomTask() bool {
263263

264264
// IsMatrixed return whether pipeline task is matrixed
265265
func (pt *PipelineTask) IsMatrixed() bool {
266-
return pt.Matrix.HasParams() || pt.Matrix.HasInclude()
266+
return pt.Matrix.HasParams() || pt.Matrix.HasInclude() || pt.Matrix.HasStrategy()
267267
}
268268

269269
// TaskSpecMetadata returns the metadata of the PipelineTask's EmbeddedTask spec.

pkg/apis/pipeline/v1/pipeline_validation.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ func (pt *PipelineTask) validateMatrix(ctx context.Context) (errs *apis.FieldErr
244244
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "matrix", config.BetaAPIFields))
245245
errs = errs.Also(pt.Matrix.validateCombinationsCount(ctx))
246246
errs = errs.Also(pt.Matrix.validateUniqueParams())
247+
errs = errs.Also(pt.Matrix.validateStrategy())
247248
}
248249
errs = errs.Also(pt.Matrix.validateParameterInOneOfMatrixOrParams(pt.Params))
249250
return errs

0 commit comments

Comments
 (0)