Skip to content

Commit

Permalink
Introduce Value in TaskResults
Browse files Browse the repository at this point in the history
This PR introduces the field `Value` in `TaskResults`.
This field is necessary to capture the results produced by `StepActions`
if the Task needs to resolve name conflicts.

This is part of issue #7259.
Following this PR, we will add support for extracting StepAction results
via termination message and sidecar logs.
  • Loading branch information
chitrangpatel committed Nov 10, 2023
1 parent 63e1a26 commit bd859fd
Show file tree
Hide file tree
Showing 19 changed files with 508 additions and 22 deletions.
32 changes: 30 additions & 2 deletions docs/pipeline-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1741,7 +1741,7 @@ Used to distinguish between a single string and an array of strings.</p>
<h3 id="tekton.dev/v1.ParamValue">ParamValue
</h3>
<p>
(<em>Appears on:</em><a href="#tekton.dev/v1.Param">Param</a>, <a href="#tekton.dev/v1.ParamSpec">ParamSpec</a>, <a href="#tekton.dev/v1.PipelineResult">PipelineResult</a>, <a href="#tekton.dev/v1.PipelineRunResult">PipelineRunResult</a>, <a href="#tekton.dev/v1.TaskRunResult">TaskRunResult</a>)
(<em>Appears on:</em><a href="#tekton.dev/v1.Param">Param</a>, <a href="#tekton.dev/v1.ParamSpec">ParamSpec</a>, <a href="#tekton.dev/v1.PipelineResult">PipelineResult</a>, <a href="#tekton.dev/v1.PipelineRunResult">PipelineRunResult</a>, <a href="#tekton.dev/v1.TaskResult">TaskResult</a>, <a href="#tekton.dev/v1.TaskRunResult">TaskRunResult</a>)
</p>
<div>
<p>ResultValue is a type alias of ParamValue</p>
Expand Down Expand Up @@ -4931,6 +4931,20 @@ string
<p>Description is a human-readable description of the result</p>
</td>
</tr>
<tr>
<td>
<code>value</code><br/>
<em>
<a href="#tekton.dev/v1.ParamValue">
ParamValue
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Value the expression used to retrieve the value of the result from an underlying Step.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="tekton.dev/v1.TaskRunDebug">TaskRunDebug
Expand Down Expand Up @@ -10232,7 +10246,7 @@ Used to distinguish between a single string and an array of strings.</p>
<h3 id="tekton.dev/v1beta1.ParamValue">ParamValue
</h3>
<p>
(<em>Appears on:</em><a href="#tekton.dev/v1beta1.Param">Param</a>, <a href="#tekton.dev/v1beta1.ParamSpec">ParamSpec</a>, <a href="#tekton.dev/v1beta1.PipelineResult">PipelineResult</a>, <a href="#tekton.dev/v1beta1.PipelineRunResult">PipelineRunResult</a>, <a href="#tekton.dev/v1beta1.TaskRunResult">TaskRunResult</a>)
(<em>Appears on:</em><a href="#tekton.dev/v1beta1.Param">Param</a>, <a href="#tekton.dev/v1beta1.ParamSpec">ParamSpec</a>, <a href="#tekton.dev/v1beta1.PipelineResult">PipelineResult</a>, <a href="#tekton.dev/v1beta1.PipelineRunResult">PipelineRunResult</a>, <a href="#tekton.dev/v1beta1.TaskResult">TaskResult</a>, <a href="#tekton.dev/v1beta1.TaskRunResult">TaskRunResult</a>)
</p>
<div>
<p>ResultValue is a type alias of ParamValue</p>
Expand Down Expand Up @@ -14135,6 +14149,20 @@ string
<p>Description is a human-readable description of the result</p>
</td>
</tr>
<tr>
<td>
<code>value</code><br/>
<em>
<a href="#tekton.dev/v1beta1.ParamValue">
ParamValue
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Value the expression used to retrieve the value of the result from an underlying Step.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="tekton.dev/v1beta1.TaskRunConditionType">TaskRunConditionType
Expand Down
8 changes: 7 additions & 1 deletion pkg/apis/pipeline/v1/openapi_generated.go

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

13 changes: 11 additions & 2 deletions pkg/apis/pipeline/v1/result_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,22 @@ limitations under the License.

package v1

import "context"
import (
"context"

"github.com/tektoncd/pipeline/pkg/apis/config"
)

// SetDefaults set the default type for TaskResult
func (tr *TaskResult) SetDefaults(context.Context) {
func (tr *TaskResult) SetDefaults(ctx context.Context) {
if tr == nil {
return
}
if tr.Value == nil && config.FromContextOrDefaults(ctx).FeatureFlags.EnableStepActions {
tr.Value = &ParamValue{
Type: ParamTypeString,
}
}
if tr.Type == "" {
if tr.Properties != nil {
// Set type to object if `properties` is given
Expand Down
26 changes: 22 additions & 4 deletions pkg/apis/pipeline/v1/result_defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/tektoncd/pipeline/pkg/apis/config"
v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"github.com/tektoncd/pipeline/test/diff"
)

func TestTaskResult_SetDefaults(t *testing.T) {
tests := []struct {
name string
before *v1.TaskResult
after *v1.TaskResult
name string
before *v1.TaskResult
after *v1.TaskResult
enableStepActions bool
}{{
name: "empty taskresult",
before: nil,
Expand Down Expand Up @@ -82,10 +84,26 @@ func TestTaskResult_SetDefaults(t *testing.T) {
Type: v1.ResultsTypeObject,
Properties: map[string]v1.PropertySpec{"key1": {v1.ParamTypeString}},
},
}, {
name: "value is not provided by step actions enabled",
before: &v1.TaskResult{
Name: "resultname",
Type: v1.ResultsTypeString,
},
after: &v1.TaskResult{
Name: "resultname",
Type: v1.ResultsTypeString,
Value: &v1.ParamValue{Type: v1.ParamTypeString},
},
enableStepActions: true,
}}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
ctx := config.ToContext(context.Background(), &config.Config{
FeatureFlags: &config.FeatureFlags{
EnableStepActions: tc.enableStepActions,
},
})
tc.before.SetDefaults(ctx)
if d := cmp.Diff(tc.after, tc.before); d != "" {
t.Error(diff.PrintWantGot(d))
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/pipeline/v1/result_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type TaskResult struct {
// Description is a human-readable description of the result
// +optional
Description string `json:"description,omitempty"`

// Value the expression used to retrieve the value of the result from an underlying Step.
// +optional
Value *ResultValue `json:"value,omitempty"`
}

// TaskRunResult used to describe the results of a task
Expand Down
51 changes: 47 additions & 4 deletions pkg/apis/pipeline/v1/result_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ package v1
import (
"context"
"fmt"
"regexp"

"github.com/tektoncd/pipeline/pkg/apis/config"
"knative.dev/pkg/apis"
)

Expand All @@ -31,17 +33,14 @@ func (tr TaskResult) Validate(ctx context.Context) (errs *apis.FieldError) {
errs := validateObjectResult(tr)
return errs
case tr.Type == ResultsTypeArray:
return nil
// Resources created before the result. Type was introduced may not have Type set
// and should be considered valid
case tr.Type == "":
return nil
// By default, the result type is string
case tr.Type != ResultsTypeString:
return apis.ErrInvalidValue(tr.Type, "type", "type must be string")
}

return nil
return tr.validateValue(ctx)
}

// validateObjectResult validates the object result and check if the Properties is missing
Expand All @@ -66,3 +65,47 @@ func validateObjectResult(tr TaskResult) (errs *apis.FieldError) {
}
return nil
}

// validateValue validates the value of the TaskResult.
// It requires that enable-step-actions is true, the value is of type string
// and format $(steps.<stepName>.results.<resultName>)
func (tr TaskResult) validateValue(ctx context.Context) (errs *apis.FieldError) {
if tr.Value != nil {
if !config.FromContextOrDefaults(ctx).FeatureFlags.EnableStepActions {
return apis.ErrGeneric("feature flag %s should be set to true to fetch Results from Steps using StepActions.", config.EnableStepActions)
}
if tr.Value.Type != ParamTypeString {
return &apis.FieldError{
Message: fmt.Sprintf(
"Invalid Type. Wanted string but got: \"%v\"", tr.Value.Type),
Paths: []string{
fmt.Sprintf("%s.type", tr.Name),
},
}
}
if tr.Value.StringVal != "" {
_, _, err := ExtractStepResultName(tr.Value.StringVal)
if err != nil {
return &apis.FieldError{
Message: fmt.Sprintf("Could not extract step and result name from TaskResult. Expect value to look like $(steps.<stepName>.results.<resultName>) but got %v. Got error: %v", tr.Value.StringVal, err),
Paths: []string{
fmt.Sprintf("%s.value", tr.Name),
},
}
}
}
}
return nil
}

// ExtractStepResultName extracts the step name and result name from a string matching
// formtat $(steps.<stepName>.results.<resultName>).
// If a match is not found, an error is retured.
func ExtractStepResultName(value string) (string, string, error) {
re := regexp.MustCompile(`\$\(steps\.(.*?)\.results\.(.*?)\)`)
rs := re.FindStringSubmatch(value)
if len(rs) != 3 {
return "", "", fmt.Errorf("Did not find two matches.")
}
return rs[1], rs[2], nil
}
Loading

0 comments on commit bd859fd

Please sign in to comment.