Skip to content

Commit

Permalink
TEP-0076 (array results and indexing into array)promoted to stable
Browse files Browse the repository at this point in the history
Pipelines 0.45 promoted array results and indexing into array param
(proposed in TEP-0076) to beta. This commit is promoting these two
features to stable such that these features can be used by the task
authors and pipeline authors in a cluster when enable-api-fields is
either set to alpha, beta or stable.

Closes #6816

Signed-off-by: Yongxuan Zhang [email protected]
  • Loading branch information
Yongxuanzhang committed Jan 17, 2024
1 parent fe47c9b commit 30661b9
Show file tree
Hide file tree
Showing 20 changed files with 8 additions and 550 deletions.
1 change: 0 additions & 1 deletion docs/additional-configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,6 @@ Features currently in "beta" are:

| Feature | Proposal | Alpha Release | Beta Release | Individual Flag |
|:------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------------------------------------------------------------------|:------------------------------|
| [Array Results and Array Indexing](pipelineruns.md#specifying-parameters) | [TEP-0076](https://github.com/tektoncd/community/blob/main/teps/0076-array-result-types.md) | [v0.38.0](https://github.com/tektoncd/pipeline/releases/tag/v0.38.0) | [v0.45.0](https://github.com/tektoncd/pipeline/releases/tag/v0.45.0) | |
| [Remote Tasks](./taskruns.md#remote-tasks) and [Remote Pipelines](./pipelineruns.md#remote-pipelines) | [TEP-0060](https://github.com/tektoncd/community/blob/main/teps/0060-remote-resolution.md) | | [v0.41.0](https://github.com/tektoncd/pipeline/releases/tag/v0.41.0) | |
| [`Provenance` field in Status](pipeline-api.md#provenance) | [issue#5550](https://github.com/tektoncd/pipeline/issues/5550) | [v0.41.0](https://github.com/tektoncd/pipeline/releases/tag/v0.41.0) | [v0.48.0](https://github.com/tektoncd/pipeline/releases/tag/v0.48.0) | `enable-provenance-in-status` |
| [Isolated `Step` & `Sidecar` `Workspaces`](./workspaces.md#isolated-workspaces) | [TEP-0029](https://github.com/tektoncd/community/blob/main/teps/0029-step-workspaces.md) | [v0.24.0](https://github.com/tektoncd/pipeline/releases/tag/v0.24.0) | [v0.50.0](https://github.com/tektoncd/pipeline/releases/tag/v0.50.0) | |
Expand Down
3 changes: 1 addition & 2 deletions docs/pipeline-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2022,8 +2022,7 @@ PipelineRun has provided an invalid binding.</p>
misses some keys required for the object param declared in Pipeline spec.</p>
</td>
</tr><tr><td><p>&#34;ParamArrayIndexingInvalid&#34;</p></td>
<td><p>ReasonParamArrayIndexingInvalid indicates that the use of param array indexing is not under correct api fields feature gate
or the array is out of bound.</p>
<td><p>ReasonParamArrayIndexingInvalid indicates that the use of param array indexing is out of bound.</p>
</td>
</tr><tr><td><p>&#34;ParameterMissing&#34;</p></td>
<td><p>ReasonParameterMissing indicates that the reason for the failure status is that the
Expand Down
4 changes: 2 additions & 2 deletions docs/pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -1375,8 +1375,7 @@ results:

For an end-to-end example, see [`Results` in a `PipelineRun`](../examples/v1/pipelineruns/pipelinerun-results.yaml).

Array result is a beta feature,
see [`Array and Object Results` in a `PipelineRun`](../examples/v1/pipelineruns/beta/pipeline-emitting-results.yaml).
In the example below, the `Pipeline` collects array and object results from `Tasks`.

```yaml
results:
Expand All @@ -1398,6 +1397,7 @@ see [`Array and Object Results` in a `PipelineRun`](../examples/v1/pipelineruns/
value: $(tasks.task2.results.object-results.foo)
```

For an end-to-end example see [`Array and Object Results` in a `PipelineRun`](../examples/v1/pipelineruns/pipeline-emitting-results.yaml).

A `Pipeline Result` is not emitted if any of the following are true:
- A `PipelineTask` referenced by the `Pipeline Result` failed. The `PipelineRun` will also
Expand Down
7 changes: 3 additions & 4 deletions docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,13 +588,12 @@ spec:
type: string
```

Refer to the [TaskRun example](../examples/v1/taskruns/beta/object-param-result.yaml) and the [PipelineRun example](../examples/v1/pipelineruns/beta/pipeline-object-param-and-result.yaml) in which `object` parameters are demonstrated.
Refer to the [TaskRun example](../examples/v1/taskruns/object-param-result.yaml) and the [PipelineRun example](../examples/v1/pipelineruns/pipeline-object-param-and-result.yaml) in which `object` parameters are demonstrated.

> NOTE:
> - `object` param is a `beta` feature and gated by the `alpha` or `beta` feature flag.
> - `object` param must specify the `properties` section to define the schema i.e. what keys are available for this object param. See how to define `properties` section in the following example and the [TEP-0075](https://github.com/tektoncd/community/blob/main/teps/0075-object-param-and-result-types.md#defaulting-to-string-types-for-values).
> - When providing value for an `object` param, one may provide values for just a subset of keys in spec's `default`, and provide values for the rest of keys at runtime ([example](../examples/v1/taskruns/beta/object-param-result.yaml)).
> - When using object in variable replacement, users can only access its individual key ("child" member) of the object by its name i.e. `$(params.gitrepo.url)`. Using an entire object as a value is only allowed when the value is also an object like [this example](https://github.com/tektoncd/pipeline/blob/55665765e4de35b3a4fb541549ae8cdef0996641/examples/v1/pipelineruns/beta/pipeline-object-param-and-result.yaml#L64-L65). See more details about using object param from the [TEP-0075](https://github.com/tektoncd/community/blob/main/teps/0075-object-param-and-result-types.md#using-objects-in-variable-replacement).
> - When providing value for an `object` param, one may provide values for just a subset of keys in spec's `default`, and provide values for the rest of keys at runtime ([example](../examples/v1/taskruns/object-param-result.yaml)).
> - When using object in variable replacement, users can only access its individual key ("child" member) of the object by its name i.e. `$(params.gitrepo.url)`. Using an entire object as a value is only allowed when the value is also an object like [this example](../examples/v1/pipelineruns/pipeline-object-param-and-result.yaml). See more details about using object param from the [TEP-0075](https://github.com/tektoncd/community/blob/main/teps/0075-object-param-and-result-types.md#using-objects-in-variable-replacement).

##### `array` type

Expand Down
18 changes: 0 additions & 18 deletions pkg/apis/pipeline/v1/pipeline_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,22 +94,6 @@ func (ps *PipelineSpec) Validate(ctx context.Context) (errs *apis.FieldError) {
// have "enable-api-fields" set to "alpha" or "beta".
func (ps *PipelineSpec) ValidateBetaFields(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
// Indexing into array parameters
arrayParamIndexingRefs := ps.GetIndexingReferencesToArrayParams()
if len(arrayParamIndexingRefs) != 0 {
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "indexing into array parameters", config.BetaAPIFields))
}
// array and object results
for i, result := range ps.Results {
switch result.Type {
case ResultsTypeObject:
// stable feature
case ResultsTypeArray:
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "array results", config.BetaAPIFields).ViaFieldIndex("results", i))
case ResultsTypeString:
default:
}
}
for i, pt := range ps.Tasks {
errs = errs.Also(pt.validateBetaFields(ctx).ViaFieldIndex("tasks", i))
}
Expand All @@ -132,8 +116,6 @@ func (pt *PipelineTask) validateBetaFields(ctx context.Context) *apis.FieldError
if len(pt.TaskRef.Params) > 0 {
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "taskref.params", config.BetaAPIFields))
}
} else if pt.TaskSpec != nil {
errs = errs.Also(pt.TaskSpec.ValidateBetaFields(ctx))
}
return errs
}
Expand Down
67 changes: 0 additions & 67 deletions pkg/apis/pipeline/v1/pipeline_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4150,38 +4150,6 @@ func TestPipelineWithBetaFields(t *testing.T) {
name string
spec PipelineSpec
}{{
name: "array indexing in Tasks",
spec: PipelineSpec{
Params: []ParamSpec{
{Name: "first-param", Type: ParamTypeArray, Default: NewStructuredValues("default-value", "default-value-again")},
},
Tasks: []PipelineTask{{
Name: "foo",
Params: Params{
{Name: "first-task-first-param", Value: *NewStructuredValues("$(params.first-param[0])")},
},
TaskRef: &TaskRef{Name: "foo"},
}},
},
}, {
name: "array indexing in Finally",
spec: PipelineSpec{
Params: []ParamSpec{
{Name: "first-param", Type: ParamTypeArray, Default: NewStructuredValues("default-value", "default-value-again")},
},
Tasks: []PipelineTask{{
Name: "foo",
TaskRef: &TaskRef{Name: "foo"},
}},
Finally: []PipelineTask{{
Name: "bar",
Params: Params{
{Name: "first-task-first-param", Value: *NewStructuredValues("$(params.first-param[0])")},
},
TaskRef: &TaskRef{Name: "bar"},
}},
},
}, {
name: "pipeline tasks - use of resolver",
spec: PipelineSpec{
Tasks: []PipelineTask{{
Expand Down Expand Up @@ -4221,41 +4189,6 @@ func TestPipelineWithBetaFields(t *testing.T) {
TaskRef: &TaskRef{ResolverRef: ResolverRef{Resolver: "bar", Params: Params{{}}}},
}},
},
}, {
name: "array results",
spec: PipelineSpec{
Tasks: []PipelineTask{{
Name: "valid-pipeline-task",
TaskRef: &TaskRef{Name: "foo-task"},
}},
Results: []PipelineResult{{Name: "my-array-result", Type: ResultsTypeArray, Value: *NewStructuredValues("$(tasks.valid-pipeline-task.results.foo[*])")}},
},
}, {
name: "array results in Tasks",
spec: PipelineSpec{
Tasks: []PipelineTask{{
Name: "valid-pipeline-task",
TaskSpec: &EmbeddedTask{TaskSpec: TaskSpec{
Steps: []Step{{Image: "busybox", Script: "echo hello"}},
Results: []TaskResult{{Name: "my-array-result", Type: ResultsTypeArray}},
}},
}},
},
}, {
name: "array results in Finally",
spec: PipelineSpec{
Tasks: []PipelineTask{{
Name: "valid-pipeline-task",
TaskRef: &TaskRef{Name: "foo-task"},
}},
Finally: []PipelineTask{{
Name: "valid-finally-task",
TaskSpec: &EmbeddedTask{TaskSpec: TaskSpec{
Steps: []Step{{Image: "busybox", Script: "echo hello"}},
Results: []TaskResult{{Name: "my-array-result", Type: ResultsTypeArray}},
}},
}},
},
}}
for _, tt := range tts {
t.Run(tt.name, func(t *testing.T) {
Expand Down
3 changes: 1 addition & 2 deletions pkg/apis/pipeline/v1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,7 @@ const (
// ReasonObjectParameterMissKeys indicates that the object param value provided from PipelineRun spec
// misses some keys required for the object param declared in Pipeline spec.
PipelineRunReasonObjectParameterMissKeys PipelineRunReason = "ObjectParameterMissKeys"
// ReasonParamArrayIndexingInvalid indicates that the use of param array indexing is not under correct api fields feature gate
// or the array is out of bound.
// ReasonParamArrayIndexingInvalid indicates that the use of param array indexing is out of bound.
PipelineRunReasonParamArrayIndexingInvalid PipelineRunReason = "ParamArrayIndexingInvalid"
// ReasonCouldntGetTask indicates that the reason for the failure status is that the
// associated Pipeline's Tasks couldn't all be retrieved
Expand Down
67 changes: 0 additions & 67 deletions pkg/apis/pipeline/v1/pipelinerun_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1415,38 +1415,6 @@ func TestPipelineRunSpecBetaFeatures(t *testing.T) {
name string
spec v1.PipelineSpec
}{{
name: "array indexing in Tasks",
spec: v1.PipelineSpec{
Params: []v1.ParamSpec{
{Name: "first-param", Type: v1.ParamTypeArray, Default: v1.NewStructuredValues("default-value", "default-value-again")},
},
Tasks: []v1.PipelineTask{{
Name: "foo",
Params: v1.Params{
{Name: "first-task-first-param", Value: *v1.NewStructuredValues("$(params.first-param[0])")},
},
TaskRef: &v1.TaskRef{Name: "foo"},
}},
},
}, {
name: "array indexing in Finally",
spec: v1.PipelineSpec{
Params: []v1.ParamSpec{
{Name: "first-param", Type: v1.ParamTypeArray, Default: v1.NewStructuredValues("default-value", "default-value-again")},
},
Tasks: []v1.PipelineTask{{
Name: "foo",
TaskRef: &v1.TaskRef{Name: "foo"},
}},
Finally: []v1.PipelineTask{{
Name: "bar",
Params: v1.Params{
{Name: "first-task-first-param", Value: *v1.NewStructuredValues("$(params.first-param[0])")},
},
TaskRef: &v1.TaskRef{Name: "bar"},
}},
},
}, {
name: "pipeline tasks - use of resolver",
spec: v1.PipelineSpec{
Tasks: []v1.PipelineTask{{
Expand Down Expand Up @@ -1486,41 +1454,6 @@ func TestPipelineRunSpecBetaFeatures(t *testing.T) {
TaskRef: &v1.TaskRef{ResolverRef: v1.ResolverRef{Resolver: "bar", Params: v1.Params{{}}}},
}},
},
}, {
name: "array results",
spec: v1.PipelineSpec{
Tasks: []v1.PipelineTask{{
Name: "valid-pipeline-task",
TaskRef: &v1.TaskRef{Name: "foo-task"},
}},
Results: []v1.PipelineResult{{Name: "my-array-result", Type: v1.ResultsTypeArray, Value: *v1.NewStructuredValues("$(tasks.valid-pipeline-task.results.foo[*])")}},
},
}, {
name: "array results in Tasks",
spec: v1.PipelineSpec{
Tasks: []v1.PipelineTask{{
Name: "valid-pipeline-task",
TaskSpec: &v1.EmbeddedTask{TaskSpec: v1.TaskSpec{
Steps: []v1.Step{{Image: "busybox", Script: "echo hello"}},
Results: []v1.TaskResult{{Name: "my-array-result", Type: v1.ResultsTypeArray}},
}},
}},
},
}, {
name: "array results in Finally",
spec: v1.PipelineSpec{
Tasks: []v1.PipelineTask{{
Name: "valid-pipeline-task",
TaskRef: &v1.TaskRef{Name: "foo-task"},
}},
Finally: []v1.PipelineTask{{
Name: "valid-finally-task",
TaskSpec: &v1.EmbeddedTask{TaskSpec: v1.TaskSpec{
Steps: []v1.Step{{Image: "busybox", Script: "echo hello"}},
Results: []v1.TaskResult{{Name: "my-array-result", Type: v1.ResultsTypeArray}},
}},
}},
},
}}
for _, tt := range tts {
t.Run(tt.name, func(t *testing.T) {
Expand Down
24 changes: 0 additions & 24 deletions pkg/apis/pipeline/v1/task_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ func (t *Task) Validate(ctx context.Context) *apis.FieldError {

// Validate implements apis.Validatable
func (ts *TaskSpec) Validate(ctx context.Context) (errs *apis.FieldError) {
errs = errs.Also(ts.ValidateBetaFields(ctx))
if len(ts.Steps) == 0 {
errs = errs.Also(apis.ErrMissingField("steps"))
}
Expand All @@ -98,29 +97,6 @@ func (ts *TaskSpec) Validate(ctx context.Context) (errs *apis.FieldError) {
return errs
}

// ValidateBetaFields returns an error if the Task spec uses beta features but does not
// have "enable-api-fields" set to "alpha" or "beta".
func (ts *TaskSpec) ValidateBetaFields(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
// Indexing into array parameters
arrayIndexParamRefs := ts.GetIndexingReferencesToArrayParams()
if len(arrayIndexParamRefs) != 0 {
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "indexing into array parameters", config.BetaAPIFields))
}
// Array and object results
for i, result := range ts.Results {
switch result.Type {
case ResultsTypeObject:
// stable feature
case ResultsTypeArray:
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "array results", config.BetaAPIFields).ViaFieldIndex("results", i))
case ResultsTypeString:
default:
}
}
return errs
}

// ValidateUsageOfDeclaredParameters validates that all parameters referenced in the Task are declared by the Task.
func ValidateUsageOfDeclaredParameters(ctx context.Context, steps []Step, params ParamSpecs) *apis.FieldError {
var errs *apis.FieldError
Expand Down
45 changes: 0 additions & 45 deletions pkg/apis/pipeline/v1/task_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2002,51 +2002,6 @@ func TestIncompatibleAPIVersions(t *testing.T) {
}
}

func TestTaskBetaFields(t *testing.T) {
tests := []struct {
name string
spec v1.TaskSpec
}{{
name: "array param indexing",
spec: v1.TaskSpec{
Params: []v1.ParamSpec{{Name: "foo", Type: v1.ParamTypeArray}},
Steps: []v1.Step{{
Name: "my-step",
Image: "my-image",
Script: `
#!/usr/bin/env bash
echo $(params.foo[1])`,
}},
},
}, {
name: "array results",
spec: v1.TaskSpec{
Results: []v1.TaskResult{{Name: "array-result", Type: v1.ResultsTypeArray}},
Steps: []v1.Step{{
Name: "my-step",
Image: "my-image",
Script: `
#!/usr/bin/env bash
echo -n "[\"hello\",\"world\"]" | tee $(results.array-result.path)`,
}},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := cfgtesting.EnableStableAPIFields(context.Background())
task := v1.Task{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: tt.spec}
if err := task.Validate(ctx); err == nil {
t.Errorf("no error when using beta field when `enable-api-fields` is stable")
}

ctx = cfgtesting.EnableBetaAPIFields(context.Background())
if err := task.Validate(ctx); err != nil {
t.Errorf("unexpected error when using beta field: %s", err)
}
})
}
}

func TestTaskSpecValidateUsageOfDeclaredParams(t *testing.T) {
tests := []struct {
name string
Expand Down
47 changes: 0 additions & 47 deletions pkg/apis/pipeline/v1/taskrun_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -898,50 +898,3 @@ func TestTaskRunSpec_Validate(t *testing.T) {
})
}
}

func TestTaskRunBetaFields(t *testing.T) {
tests := []struct {
name string
spec v1.TaskSpec
}{{
name: "array param indexing",
spec: v1.TaskSpec{
Params: []v1.ParamSpec{{Name: "foo", Type: v1.ParamTypeArray}},
Steps: []v1.Step{{
Name: "my-step",
Image: "my-image",
Script: `
#!/usr/bin/env bash
echo $(params.foo[1])`,
}},
},
}, {
name: "array results",
spec: v1.TaskSpec{
Results: []v1.TaskResult{{Name: "array-result", Type: v1.ResultsTypeArray}},
Steps: []v1.Step{{
Name: "my-step",
Image: "my-image",
Script: `
#!/usr/bin/env bash
echo -n "[\"hello\",\"world\"]" | tee $(results.array-result.path)`,
}},
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := cfgtesting.EnableStableAPIFields(context.Background())
tr := v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: v1.TaskRunSpec{
TaskSpec: &tt.spec,
}}
if err := tr.Validate(ctx); err == nil {
t.Errorf("no error when using beta field when `enable-api-fields` is stable")
}

ctx = cfgtesting.EnableBetaAPIFields(context.Background())
if err := tr.Validate(ctx); err != nil {
t.Errorf("unexpected error when using beta field: %s", err)
}
})
}
}
Loading

0 comments on commit 30661b9

Please sign in to comment.