Skip to content

Commit

Permalink
TEP0154 - Enable concise resolver syntax
Browse files Browse the repository at this point in the history
This PR enables concise resolver syntax interface.
  • Loading branch information
chitrangpatel committed Apr 24, 2024
1 parent b419b2c commit 25ae3f6
Show file tree
Hide file tree
Showing 55 changed files with 1,293 additions and 292 deletions.
2 changes: 2 additions & 0 deletions config/config-feature-flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,5 @@ data:
# "pipelinerun" for Pipelinerun and "taskrun" for Taskrun. Or a combination of
# these.
disable-inline-spec: ""
# Setting this flag to "true" will enable the use of concise resolver syntax
enable-concise-resolver-syntax: "false"
45 changes: 45 additions & 0 deletions docs/how-to-write-a-resolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,37 @@ func (r *resolver) ValidateParams(ctx context.Context, params map[string]string)
You'll also need to add the `"errors"` package to your list of imports at
the top of the file.

## The `ValidateRequest` method

The `ValidateRequest` method checks that the resolution request is valid
and can be parsed by the resolver. Our example resolver expects
format for the `url` to be `demoscheme://<path>` so we'll validate this format.
Additionally, it does not accept any params.
This is currently at an ALPHA stability level and subject to alpha API compatibility policies.
```go
// ValidateRequest ensures that the resolution request is as expected.
func (r *resolver) ValidateRequest(ctx context.Context, req *v1beta1.ResolutionRequestSpec) error {
if len(req.Params) > 0 {
return r.ValidateParams(ctx, req.Params)
}
url := req.URL
u, err := neturl.ParseRequestURI(url)
if err != nil {
return err
}
if u.Scheme != "demoscheme" {
return fmt.Errorf("Invalid Scheme. Want %s, Got %s", "demoscheme", u.Scheme)
}
if u.Path == "" {
return errors.New("Empty path.")
}
return nil
}
```

You'll also need to add the `net/url` as `neturl` and `"errors"` package to your list of imports at
the top of the file.

## The `Resolve` method

We implement the `Resolve` method to do the heavy lifting of fetching
Expand Down Expand Up @@ -293,6 +324,20 @@ func (*myResolvedResource) RefSource() *pipelinev1.RefSource {
}
```

## The `ResolveRequest` method

We implement the `ResolveRequest` method to do the heavy lifting of fetching
the contents of a file and returning them. It takes in the resolution request spec as input.
// This is currently at an ALPHA stability level and subject to alpha API compatibility policies.

```go
// Resolve uses the given params to resolve the requested file or resource.
func (r *resolver) ResolveRequest(ctx context.Context, req *v1beta1.ResolutionRequestSpec) (framework.ResolvedResource, error) {
return &myResolvedResource{}, nil
}
```


## The deployment configuration

Finally, our resolver needs some deployment configuration so that it can
Expand Down
32 changes: 32 additions & 0 deletions docs/pipeline-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,22 @@ resource being requested. For example: repo URL, commit SHA,
path to file, the kind of authentication to leverage, etc.</p>
</td>
</tr>
<tr>
<td>
<code>url</code><br/>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>URL is the runtime url passed to the resolver
to help it figure out how to resolver the resource being
requested.
This is currently at an ALPHA stability level and subject to
alpha API compatibility policies.</p>
</td>
</tr>
</table>
</td>
</tr>
Expand Down Expand Up @@ -358,6 +374,22 @@ resource being requested. For example: repo URL, commit SHA,
path to file, the kind of authentication to leverage, etc.</p>
</td>
</tr>
<tr>
<td>
<code>url</code><br/>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>URL is the runtime url passed to the resolver
to help it figure out how to resolver the resource being
requested.
This is currently at an ALPHA stability level and subject to
alpha API compatibility policies.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="resolution.tekton.dev/v1beta1.ResolutionRequestStatus">ResolutionRequestStatus
Expand Down
23 changes: 23 additions & 0 deletions docs/resolver-template/cmd/demoresolver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ package main
import (
"context"
"errors"
"fmt"
neturl "net/url"

pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1"
Expand Down Expand Up @@ -51,6 +53,22 @@ func (r *resolver) GetSelector(context.Context) map[string]string {
}
}

// ValidateRequest ensures the ResolutionSpec from a request is as expected.
// This is currently at an ALPHA stability level and subject to alpha API compatibility policies.
func (r *resolver) ValidateRequest(ctx context.Context, req *v1beta1.ResolutionRequestSpec) error {
if len(req.Params) > 0 {
return r.ValidateParams(ctx, req.Params)
}
u, err := neturl.ParseRequestURI(req.URL)
if err != nil {
return err
}
if u.Scheme != "demoscheme" {
return fmt.Errorf("Invalid Scheme. Want %s, Got %s", "demoscheme", u.Scheme)
}
return nil
}

// ValidateParams ensures parameters from a request are as expected.
func (r *resolver) ValidateParams(ctx context.Context, params []pipelinev1.Param) error {
if len(params) > 0 {
Expand All @@ -59,6 +77,11 @@ func (r *resolver) ValidateParams(ctx context.Context, params []pipelinev1.Param
return nil
}

// ResolveRequest uses the given resolution request spec to resolve the requested file or resource.
func (r *resolver) ResolveRequest(ctx context.Context, req *v1beta1.ResolutionRequestSpec) (framework.ResolvedResource, error) {
return &myResolvedResource{}, nil
}

// Resolve uses the given params to resolve the requested file or resource.
func (r *resolver) Resolve(ctx context.Context, params []pipelinev1.Param) (framework.ResolvedResource, error) {
return &myResolvedResource{}, nil
Expand Down
142 changes: 141 additions & 1 deletion docs/resolver-template/cmd/demoresolver/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ package main

import (
"encoding/base64"
"errors"
"testing"
"time"

pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1"
ttesting "github.com/tektoncd/pipeline/pkg/reconciler/testing"
resolutioncommon "github.com/tektoncd/pipeline/pkg/resolution/common"
frtesting "github.com/tektoncd/pipeline/pkg/resolution/resolver/framework/testing"
"github.com/tektoncd/pipeline/test"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "knative.dev/pkg/apis/duck/v1"
_ "knative.dev/pkg/system/testing"
)

Expand All @@ -48,7 +51,9 @@ func TestResolver(t *testing.T) {
resolutioncommon.LabelKeyResolverType: "demo",
},
},
Spec: v1beta1.ResolutionRequestSpec{},
Spec: v1beta1.ResolutionRequestSpec{
URL: "demoscheme://foo/bar",
},
}
d := test.Data{
ResolutionRequests: []*v1beta1.ResolutionRequest{request},
Expand All @@ -65,3 +70,138 @@ func TestResolver(t *testing.T) {

frtesting.RunResolverReconcileTest(ctx, t, d, r, request, expectedStatus, expectedErr)
}

func TestResolver_Failure_Wrong_Scheme(t *testing.T) {
ctx, _ := ttesting.SetupFakeContext(t)

r := &resolver{}

request := &v1beta1.ResolutionRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: "resolution.tekton.dev/v1beta1",
Kind: "ResolutionRequest",
},
ObjectMeta: metav1.ObjectMeta{
Name: "rr",
Namespace: "foo",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: map[string]string{
resolutioncommon.LabelKeyResolverType: "demo",
},
},
Spec: v1beta1.ResolutionRequestSpec{
URL: "wrongscheme://foo/bar",
},
}
d := test.Data{
ResolutionRequests: []*v1beta1.ResolutionRequest{request},
}

expectedStatus := &v1beta1.ResolutionRequestStatus{
Status: v1.Status{
Conditions: v1.Conditions{
{
Type: "Succeeded",
Status: "False",
Reason: "ResolutionFailed",
Message: `invalid resource request "foo/rr": Invalid Scheme. Want demoscheme, Got wrongscheme`,
},
},
},
}

// If you want to test scenarios where an error should occur, pass a non-nil error to RunResolverReconcileTest
expectedErr := errors.New(`invalid resource request "foo/rr": Invalid Scheme. Want demoscheme, Got wrongscheme`)
frtesting.RunResolverReconcileTest(ctx, t, d, r, request, expectedStatus, expectedErr)
}

func TestResolver_Failure_InvalidUrl(t *testing.T) {
ctx, _ := ttesting.SetupFakeContext(t)

r := &resolver{}

request := &v1beta1.ResolutionRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: "resolution.tekton.dev/v1beta1",
Kind: "ResolutionRequest",
},
ObjectMeta: metav1.ObjectMeta{
Name: "rr",
Namespace: "foo",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: map[string]string{
resolutioncommon.LabelKeyResolverType: "demo",
},
},
Spec: v1beta1.ResolutionRequestSpec{
URL: "foo/bar",
},
}
d := test.Data{
ResolutionRequests: []*v1beta1.ResolutionRequest{request},
}

expectedStatus := &v1beta1.ResolutionRequestStatus{
Status: v1.Status{
Conditions: v1.Conditions{
{
Type: "Succeeded",
Status: "False",
Reason: "ResolutionFailed",
Message: `invalid resource request "foo/rr": parse "foo/bar": invalid URI for request`,
},
},
},
}

// If you want to test scenarios where an error should occur, pass a non-nil error to RunResolverReconcileTest
expectedErr := errors.New(`invalid resource request "foo/rr": parse "foo/bar": invalid URI for request`)
frtesting.RunResolverReconcileTest(ctx, t, d, r, request, expectedStatus, expectedErr)
}

func TestResolver_Failure_InvalidParams(t *testing.T) {
ctx, _ := ttesting.SetupFakeContext(t)

r := &resolver{}

request := &v1beta1.ResolutionRequest{
TypeMeta: metav1.TypeMeta{
APIVersion: "resolution.tekton.dev/v1beta1",
Kind: "ResolutionRequest",
},
ObjectMeta: metav1.ObjectMeta{
Name: "rr",
Namespace: "foo",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: map[string]string{
resolutioncommon.LabelKeyResolverType: "demo",
},
},
Spec: v1beta1.ResolutionRequestSpec{
Params: []pipelinev1.Param{{
Name: "foo",
Value: *pipelinev1.NewStructuredValues("bar"),
}},
},
}
d := test.Data{
ResolutionRequests: []*v1beta1.ResolutionRequest{request},
}

expectedStatus := &v1beta1.ResolutionRequestStatus{
Status: v1.Status{
Conditions: v1.Conditions{
{
Type: "Succeeded",
Status: "False",
Reason: "ResolutionFailed",
Message: `invalid resource request "foo/rr": no params allowed`,
},
},
},
}

// If you want to test scenarios where an error should occur, pass a non-nil error to RunResolverReconcileTest
expectedErr := errors.New(`invalid resource request "foo/rr": no params allowed`)
frtesting.RunResolverReconcileTest(ctx, t, d, r, request, expectedStatus, expectedErr)
}
Loading

0 comments on commit 25ae3f6

Please sign in to comment.