-
Notifications
You must be signed in to change notification settings - Fork 225
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
TEP-0022: Triggers - Immutable Input Events #218
Conversation
/cc @dibyom @bigkevmcd |
This TEP proposes a change to Trigger interceptor handling to ensure that incoming input events are not mutated through the triggering process. TEP Design for tektoncd/triggers#778
602d714
to
bca6b70
Compare
What does the new // Interceptor is the interface that all interceptors implement.
//
// Implementations can return extensions, which will be made available to subsequent calls in `inputs.<interceptor name>`.
type Interceptor interface {
ExecuteTrigger(input map[string]interface{}) (extensions map[string]interface{}, err error)
} |
teps/0022-trigger-immutable-input.md
Outdated
Webhook interceptors may no longer modify headers as part of the interceptor | ||
chain. | ||
|
||
If no response body is returned by the interceptor, no `interceptors.<name>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might complicate bindings, if you have several interceptors, and the values go in as...
{
"interceptors": {
"github-push-repo-a-interceptor": {
"short_sha": "bca6b70"
},
"github-push-repo-b-interceptor": {
"short_sha": "3e29f82b"
}
}
}
Getting a binding that can build and tag with the short_sha
in a generic form will be tricky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, what if this is a flat list i.e we do not key by interceptor name (we could call this field extensions
or something?)
The main drawback I see is if two interceptors both produce the same field. In that case, maybe we follow a simple rule -- the last interceptor in the chain can overwrite the previous one? (Another alternative might be to just throw an error in this case)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scope for interceptors to do this is fairly small, there's no "global" interceptor, so if it's happening, it's happening within a fairly small stanza in your YAML.
To me, this suggests a low likelihood of having to debug a mysterious value, and with "last-one-wins", it should be fairly easy to debug (it's not like we're subject to the vagaries of hash ordering).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM.
teps/0022-trigger-immutable-input.md
Outdated
field) we will include the entire input event in a reserved body field named | ||
`input`. This field will not be mutated throughout trigger processing, so that | ||
inteceptors and trigger bindings can rely on assumptions on the incoming event | ||
type. Incoming event data will be stored within `input` as `body` and `headers` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will users have to now use input.body or input.header for bindings/interceptors? If so, can we consider just keeping the body and headers as is today but make them immutable while adding a new modifiable field (interceptors
) as described below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at it again, maybe the interface becomes:
interface Interceptor {
ExecuteTrigger(headers http.Header, body map[string]interface{}) (extensions map[string]interface{}, err error)
}
The Webhook interceptor would just do a POST, transferring the headers, encoding the body, and being expected to return an optional JSON body (which would become extensions) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'll want to encapsulate this into Request/Response types, following what is proposed in the pluggable interceptor proposal by @dibyom. This will make it easier to add additional fields into the request/response objects as needed (i.e. the additional trigger fields proposed by Dibyo):
interface Interceptor {
ExecuteTrigger(req InterceptorRequest) (InterceptorResponse, error)
}
type InterceptorRequest struct {
Headers http.Headers
Body json.RawMessage
Interceptors map[string]interface{}
}
type InterceptorResponse struct {
Body json.RawMessage
}
teps/0022-trigger-immutable-input.md
Outdated
Webhook interceptors may no longer modify headers as part of the interceptor | ||
chain. | ||
|
||
If no response body is returned by the interceptor, no `interceptors.<name>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, what if this is a flat list i.e we do not key by interceptor name (we could call this field extensions
or something?)
The main drawback I see is if two interceptors both produce the same field. In that case, maybe we follow a simple rule -- the last interceptor in the chain can overwrite the previous one? (Another alternative might be to just throw an error in this case)
teps/0022-trigger-immutable-input.md
Outdated
Webhook interceptors may no longer modify headers as part of the interceptor | ||
chain. | ||
|
||
If no response body is returned by the interceptor, no `interceptors.<name>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM.
teps/0022-trigger-immutable-input.md
Outdated
payloads. | ||
|
||
Any response received from interceptors will be included in following paylods | ||
under a new `interceptors` field, keyed by interceptor name for uniqueness. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any thoughts on naming?
- interceptors
- extensions
- plugins
- ???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very slight preference towards extensions
but open to all of them!
teps/0022-trigger-immutable-input.md
Outdated
field) we will include the entire input event in a reserved body field named | ||
`input`. This field will not be mutated throughout trigger processing, so that | ||
inteceptors and trigger bindings can rely on assumptions on the incoming event | ||
type. Incoming event data will be stored within `input` as `body` and `headers` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'll want to encapsulate this into Request/Response types, following what is proposed in the pluggable interceptor proposal by @dibyom. This will make it easier to add additional fields into the request/response objects as needed (i.e. the additional trigger fields proposed by Dibyo):
interface Interceptor {
ExecuteTrigger(req InterceptorRequest) (InterceptorResponse, error)
}
type InterceptorRequest struct {
Headers http.Headers
Body json.RawMessage
Interceptors map[string]interface{}
}
type InterceptorResponse struct {
Body json.RawMessage
}
This primarily removes the proposed `input` field in favor of direct `headers` and `body` field to reduce the impact this would have on existing users. Other minor changes: - Make feature flag an annotation. - Flesh out details around interface changes (highly influenced by Pluggable Interceptors proposal). - Add more details around CEL Interceptor changes.
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: dibyom 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 |
/kind tep |
teps/0022-trigger-immutable-input.md
Outdated
<<[UNRESOLVED wlynch ]>> | ||
TODO based on feedback | ||
<<[/UNRESOLVED]>> | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it's worth generating some of these as part of the proposal, even if the alternatives you come up with aren't very appealing; if that's the case then it increase confidence in the proposal at least :D
some ideas: one alternative is always to NOT do the thing, i.e. what does it look like to try to meet the same goals but without the explicit feature? or we could make this something you opt into (kinda already the case with the feature flag, but maybe at a different level, e.g. configuring mutability in the binding?)? or maybe some kind of interceptor you add into your interceptors list that ensures the event hasn't been mutated?
(none of these are great but again i think it's worth generating and recording)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh and you do have one below! "TODO based on feedback" made me think there wouldn't be any
anyway here are a few more :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
teps/0022-trigger-immutable-input.md
Outdated
<<[UNRESOLVED wlynch ]>> | ||
TODO based on feedback | ||
<<[/UNRESOLVED]>> | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one potential drawback that comes to mind: can't as easily use interceptors to filter out sensitive data? e.g. if you could imagine wanting to pass the entire body of an event to a resulting Run
i suppose you can still do this, but you need to do it by duplicating all the data into a new field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooh, great point! Added (though I don't think we want to support this).
alternatives. Adds drawbacks / alternatives raised in review: - Payload redaction - What if we keep immutable behavior?
Discussed merging at proposed in the API working group today - seems like it's likely we can go to "implementable" quickly after that, don't seem to be any outstanding issues atm /lgtm |
This TEP proposes a change to Trigger interceptor handling to ensure
that incoming input events are not mutated through the triggering
process.
TEP Design for tektoncd/triggers#778