-
Notifications
You must be signed in to change notification settings - Fork 498
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
Validating HTTPRouteFilter type consistency #946
Validating HTTPRouteFilter type consistency #946
Conversation
Hi @crmejia. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the 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. |
/ok-to-test |
/retest-required |
|
||
switch filter.Type { | ||
case gatewayv1a2.HTTPRouteFilterExtensionRef: | ||
if filter.ExtensionRef == nil || filter.RequestHeaderModifier != nil || filter.RequestMirror != nil || filter.RequestRedirect != nil { |
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.
These if logics might not so feasible, if a new filter type is introduced, we have to change a few places, and it's easy to forget.
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 see what you mean. It's hard to mantain. I've researched alternatives:
- I started by looking at ways to implement discriminated unions in Go and they usually point to using interfaces(see Alternatives to sum types in Go, StackOverflow). Correct me If I'm wrong but I don't think it's right to add interfaces to the API just to solve this problem.
- Something that occurred to me is to compare the json tags with the type but I think is "hacky".
Do you have any ideas or tips for the correct implementation? I have a hunch that there is a more elegant way to solve this and I just didn't look in the right place.
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.
If we can relax the constraints, say unused filter config can still be reserved there, than the switch statement can be simple.
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 best I could come up with was this:
func validateUnionField(discriminator string, name string, value interface{}) {
var isNil bool
switch value := value.(type) {
case *gatewayv1a2.HTTPRequestHeaderFilter:
isNil = (value == nil)
case *gatewayv1a2.HTTPRequestMirrorFilter:
isNil = (value == nil)
case *gatewayv1a2.HTTPRequestRedirectFilter:
isNil = (value == nil)
case *gatewayv1a2.LocalObjectReference:
isNil = (value == nil)
default:
// Puke an error so someone updates this code ...
}
if discriminator == name {
if isNil {
// Filter spec is not set, but it should be.
}
} else {
if !isNil {
// Filter spec is set, but it should not be.
}
}
}
validateUnionField(filter.Type, gatewayv1a2.HTTPRouteFilterExtensionRef, filter.ExtensionRef)
validateUnionField(filter.Type, gatewayv1a2.HTTPRouteFilterRequestHeaderModifier, filter.RequestHeaderModifier)
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've relaxed the constraints to make the switch statement simpler. Also, I added a default case to make sure new types are added to the validation.
I think that func validateUnionField
is not simpler and still has the issue of referencing the types so new types need to be added.
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.
Thanks for the work on this @crmejia!
/retest |
/retest |
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.
Thanks for your work on this @crmejia!
errs = append(errs, validateHTTPRouteUniqueFilters(rule.Filters, path.Child("rules").Index(i))...) | ||
errs = append(errs, validateHTTPRouteFilterTypeMatchesValue(rule.Filters, path.Child("rules").Index(i))...) |
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 is already a great simplification, but can you take it one step further and just have a generic validateHTTPRouteFilters
func instead that covers both of these? Would save us from looping through the same list of filters 2x.
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 kept them separate to have a clear boundary for each validation but I can see the inefficiency. I'll simplify 👍🏾
name: "valid HTTPRouteFilterRequestHeaderModifier type filter with matching field", | ||
routeFilter: []gatewayv1a2.HTTPRouteFilter{{ | ||
Type: gatewayv1a2.HTTPRouteFilterRequestHeaderModifier, | ||
RequestHeaderModifier: &gatewayv1a2.HTTPRequestHeaderFilter{}, |
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 wonder if we need to also make sure that the value of these fields is non empty? Even a simple check like filter.RequestRedirect == nil || (gatewayv1a2.HTTPRequestHeaderFilter{}) == *filter.RequestRedirect)
might be helpful to ensure completely empty structs were also invalid.
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. I had to use reflect.DeepEqual
in some cases.
/retest |
Once the deep equal issue is done, this LGTM, pending Rob's concurrence. /lgtm |
After some discussion with Rob regarding validation we decided to check for
|
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.
Thanks @crmejia! Just a couple tiny nits but otherwise LGTM.
if filter.Type == "" { | ||
errs = append(errs, field.Required(path, "filter.Type cannot be empty")) | ||
} |
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 is actually already covered by CRD validation, no need to also have it here: https://github.com/kubernetes-sigs/gateway-api/blob/master/apis/v1alpha2/httproute_types.go#L497-L498
Thanks @crmejia! Will leave final LGTM for someone else, but this looks great. /approve |
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.
/lgtm
\o/
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: crmejia, jpeach, robscott 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 |
/remove-hold |
* Validating HTTPRouteFilter type consistency * fixing Makefile regression * relaxing validation constraints to simplify switch statement * adding default type validation * refactoring validateHTTPRouteSpec * fixing paths * adding HTTPRouteFilter URLRewrite validation * validating route filters are not empty * simplying validation with validateHTTPRouteFilters * removing reflect.DeepEqual as is not necessary * validate route filters are not nil * small fixes * fixing empty filter test
What type of PR is this?
/kind feature
What this PR does / why we need it:
This PR adds validates that a HTTPRouterFilter Type matches it's value.
Which issue(s) this PR fixes:
Fixes #942
Does this PR introduce a user-facing change?: