Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cel interceptor #285

Merged
merged 1 commit into from
Jan 7, 2020
Merged

Cel interceptor #285

merged 1 commit into from
Jan 7, 2020

Conversation

bigkevmcd
Copy link
Member

Changes

This implements a CEL based interceptor that allows matching on request bodies, and request headers.

Submitter Checklist

These are the criteria that every PR should meet, please check them off as you
review them:

See the contribution guide for more details.

Release Notes

This adds a new CEL interceptor, able to use expressions to determine whether or not to filter out incoming request bodies.

@tekton-robot tekton-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Dec 19, 2019
@tekton-robot
Copy link

Hi @bigkevmcd. Thanks for your PR.

I'm waiting for a tektoncd member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@tekton-robot tekton-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Dec 19, 2019
@bigkevmcd
Copy link
Member Author

bigkevmcd commented Dec 19, 2019

Some thoughts I've had since the initial implementation...

Instead of one expression there could be several.

expressions:
 - "headers.match(...)"
 - "body.pull_request.head.repo.fork == false"

Where each of the expressions would need to return true, this would improve readability (over very long expressions).

Also, there's no way currently to add a custom response header, this could be implemented similar to the values: key.

This is related to #247

@bigkevmcd
Copy link
Member Author

Also, in order to avoid the invisible coupling between the interceptor modifying the request body, and bindings, CEL could be added to the list of expression languages for bindings.

@dibyom
Copy link
Member

dibyom commented Dec 19, 2019

/ok-to-test

@tekton-robot tekton-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Dec 19, 2019
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/interceptors/cel/cel.go Do not exist 80.4%

@bigkevmcd
Copy link
Member Author

/test pull-tekton-triggers-build-tests

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/interceptors/cel/cel.go Do not exist 80.4%

Copy link
Member

@dibyom dibyom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @bigkevmcd I started playing around with your branch the other day and overall it looks great.

The main thing that I'd like to see changed is the modification of the payload (the Value field) -- I think its a super useful feature that we'd probably want to add but I think we should do that in a followup. For the 0.2 release, I think having just the expressions for filtering would be great!

Oh, and would you mind squashing the commits 😄 ?

pkg/apis/triggers/v1alpha1/event_listener_types.go Outdated Show resolved Hide resolved
@@ -67,7 +67,7 @@ func (s *EventListenerSpec) validate(ctx context.Context, el *EventListener) *ap

func (i *EventInterceptor) validate(ctx context.Context, namespace string) *apis.FieldError {
// Validate at least one
if i.Webhook == nil && i.Github == nil && i.Gitlab == nil {
if i.Webhook == nil && i.Github == nil && i.Gitlab == nil && i.CEL == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also validate that if CEL has the expression field set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add validation for the renamed filter expression.

func (w *Interceptor) ExecuteTrigger(payload []byte, request *http.Request, _ *triggersv1.EventListenerTrigger, _ string) ([]byte, error) {
env, err := makeCelEnv()
if err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add some extra context when returning errors in this file?
e.g. fmt.Errof("error creating cel environment: %w, err)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
name: "simple header check with non matching header",
CEL: &triggersv1.CELInterceptor{
Expression: "headers['X-Test'][0] == 'unknown'",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The headers part here is slightly different from headers in TriggerBindings.
In Bindings, the keyword is header not headers -- though headers does sound like the more correct term.
Also, you do not have to use array accessors e.g. $(headers.foo) not $(headers.foo[0])
Its probably worth being consistent here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, you would prefer it to emulate JSON path expressions in CEL?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if CEL is the right expression language to use, maybe we could augment the JSON path expressions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, the syntax header['X-Test'][0] is one possible option.

The header value is a https://golang.org/pkg/net/http/#Header value, but, it's translated in CEL into a map, so, there's a custom match overload, which uses .Get() on the value, this is doing more than a simple key-value lookup.

I'm not sure how easy it is to emulate JSON path expressions in CEL without a significant amount of work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "emulating", I was just referring to making it easy to access single valued headers i.e header['X-Test'][0] vs header['X-Test']. From reading @wlynch 's comment it seems like that might already be the case...
We definitely should not try to do anything more complicated than that 😅

@dibyom
Copy link
Member

dibyom commented Dec 19, 2019

Instead of one expression there could be several.

Another more verbose option could be the multiple interceptor support that @wlynch is working on

Also, in order to avoid the invisible coupling between the interceptor modifying the request body, and bindings, CEL could be added to the list of expression languages for bindings

This could be an interesting add on for the future...(JSONPath also supports filtering, maybe that could be a interceptor too 😛 )

@bigkevmcd
Copy link
Member Author

Instead of one expression there could be several.

Another more verbose option could be the multiple interceptor support that @wlynch is working on

This would be slightly different, in that it would be more about readability to avoid having to write long expressions like filter1 && filter2 && filter3 && filter4

@bigkevmcd bigkevmcd force-pushed the cel-interceptor branch 2 times, most recently from 6229aed to 33bc5b8 Compare December 19, 2019 17:13
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5

@bigkevmcd
Copy link
Member Author

The main driver for this is to get rid of an external interceptor that is doing matching, and extracting the SHA and trimming it (amongst other things).

The "values" behaviour emulates this behaviour, so, I'd like to see some discussion about what alternatives could look like.

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5

Copy link
Member

@wlynch wlynch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure to add a reference to the issue (#49) in the PR body / commit message.

Otherwise looking good! :D

docs/eventlisteners.md Outdated Show resolved Hide resolved
@@ -67,7 +67,7 @@ func (s *EventListenerSpec) validate(ctx context.Context, el *EventListener) *ap

func (i *EventInterceptor) validate(ctx context.Context, namespace string) *apis.FieldError {
// Validate at least one
if i.Webhook == nil && i.Github == nil && i.Gitlab == nil {
if i.Webhook == nil && i.Github == nil && i.Gitlab == nil && i.CEL == nil {
return apis.ErrMissingField("interceptor")
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add logic for numSet below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add a test to make sure CEL and another Interceptor can't be set at the same time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this still needs to be added

pkg/interceptors/cel/cel.go Show resolved Hide resolved
return cel.Functions(
&functions.Overload{
Operator: "match",
Function: matchHeader},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this function is to simplify accessing headers with only 1 value, correct? Could we just use the built-in in function instead?

e.g. in('foo', header['myheader']). See https://github.com/google/cel-spec/blob/master/doc/langdef.md for more details.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not quite what it does, the value of header is https://golang.org/pkg/net/http/#Header and the match delegates to the Get method, which does a bit more than accessing the header for single-value headers.

func evaluate(expr string, env cel.Env, data map[string]interface{}) (ref.Val, error) {
parsed, issues := env.Parse(expr)
if issues != nil && issues.Err() != nil {
return nil, issues.Err()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know what will be returned in this error message? e.g. will it be clear what exactly is failing? We may want to consider wrapping this error to make it clear what went wrong.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message from CEL is pretty good, the test I added only really tests a subset of the message.

failed to evaluate expression 'header['X-Test': ERROR: <input>:1:8: Syntax error: token recognition error at: ''X-Test'
 | header['X-Test
 | .......^
ERROR: <input>:1:15: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
 | header['X-Test
 | ..............^

pkg/interceptors/cel/cel_test.go Show resolved Hide resolved
pkg/interceptors/cel/cel_test.go Show resolved Hide resolved
pkg/interceptors/cel/cel_test.go Show resolved Hide resolved
pkg/sink/sink.go Show resolved Hide resolved
@dibyom
Copy link
Member

dibyom commented Dec 19, 2019

Can't comment on the actual comment for some reason Now I magically can! 😕

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 93.9% 0.3

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 93.9% 0.3

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
test/builder/eventlistener.go 93.6% 93.9% 0.3

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 93.9% 0.3

Copy link
Member

@dibyom dibyom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @bigkevmcd , sorry about the long review time. This is looking almost good to go - just needs a rebase, and a couple of minor issues.

One thing is that this is going to conflict with #272 depending on which gets in first

[CEL](https://github.com/google/cel-go) expression language.

Supported features include case-insensitive matching on request headers, and the
ability to add new keys (which can be extracted in a TemplateBinding) to the response
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we decided to remove the part about adding new keys for now

@@ -67,7 +67,7 @@ func (s *EventListenerSpec) validate(ctx context.Context, el *EventListener) *ap

func (i *EventInterceptor) validate(ctx context.Context, namespace string) *apis.FieldError {
// Validate at least one
if i.Webhook == nil && i.Github == nil && i.Gitlab == nil {
if i.Webhook == nil && i.Github == nil && i.Gitlab == nil && i.CEL == nil {
return apis.ErrMissingField("interceptor")
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this still needs to be added

@tekton-robot tekton-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jan 6, 2020
@dibyom
Copy link
Member

dibyom commented Jan 6, 2020

We should make sure to add a reference to the issue (#49) in the PR body / commit message.

Looks like we need to add this as well!

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 92.2% -1.5

CEL interceptors parse expressions to filter requests based on JSON bodies and request headers, using the
[CEL](https://github.com/google/cel-go) expression language.

Supported features include case-insensitive matching on request headers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 😄 Thanks!

@tekton-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dibyom, wlynch

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 92.2% -1.5

This adds a cel interceptor, that uses a a CEL expression to filter request bodies.

This implements issue tektoncd#49.
@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 92.2% -1.5

@tekton-robot
Copy link

The following is the coverage report on pkg/.
Say /test pull-tekton-triggers-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/triggers/v1alpha1/event_listener_validation.go 91.5% 92.0% 0.5
test/builder/eventlistener.go 93.6% 92.2% -1.5

@wlynch
Copy link
Member

wlynch commented Jan 7, 2020

/lgtm

@tekton-robot tekton-robot added the lgtm Indicates that a PR is ready to be merged. label Jan 7, 2020
@tekton-robot tekton-robot merged commit 9ace5c1 into tektoncd:master Jan 7, 2020
@dibyom dibyom mentioned this pull request Feb 3, 2020
3 tasks
@bigkevmcd bigkevmcd deleted the cel-interceptor branch March 10, 2020 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants