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

Validating HTTPRouteFilter type consistency #946

Merged

Conversation

crmejia
Copy link
Contributor

@crmejia crmejia commented Nov 23, 2021

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?:

NONE

@k8s-ci-robot k8s-ci-robot added kind/feature Categorizes issue or PR as related to a new feature. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Nov 23, 2021
@k8s-ci-robot
Copy link
Contributor

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 /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.

@k8s-ci-robot k8s-ci-robot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Nov 23, 2021
@crmejia
Copy link
Contributor Author

crmejia commented Nov 23, 2021

Hey @jpeach, this PR looks to address #942

@jpeach
Copy link
Contributor

jpeach commented Nov 23, 2021

/ok-to-test

@k8s-ci-robot k8s-ci-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 Nov 23, 2021
@crmejia
Copy link
Contributor Author

crmejia commented Nov 24, 2021

/retest-required

Makefile Outdated Show resolved Hide resolved

switch filter.Type {
case gatewayv1a2.HTTPRouteFilterExtensionRef:
if filter.ExtensionRef == nil || filter.RequestHeaderModifier != nil || filter.RequestMirror != nil || filter.RequestRedirect != nil {
Copy link
Contributor

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.

Copy link
Contributor Author

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:

  1. 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.
  2. 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.

Copy link
Contributor

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.

Copy link
Contributor

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)
         

Copy link
Contributor Author

@crmejia crmejia Nov 30, 2021

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.

Copy link
Member

@robscott robscott 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 work on this @crmejia!

apis/v1alpha2/validation/httproute.go Outdated Show resolved Hide resolved
apis/v1alpha2/validation/httproute.go Outdated Show resolved Hide resolved
apis/v1alpha2/validation/httproute_test.go Outdated Show resolved Hide resolved
@crmejia
Copy link
Contributor Author

crmejia commented Dec 7, 2021

/retest

@robscott
Copy link
Member

/retest

Copy link
Member

@robscott robscott 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 your work on this @crmejia!

Comment on lines 51 to 52
errs = append(errs, validateHTTPRouteUniqueFilters(rule.Filters, path.Child("rules").Index(i))...)
errs = append(errs, validateHTTPRouteFilterTypeMatchesValue(rule.Filters, path.Child("rules").Index(i))...)
Copy link
Member

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.

Copy link
Contributor Author

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 👍🏾

apis/v1alpha2/validation/httproute.go Outdated Show resolved Hide resolved
name: "valid HTTPRouteFilterRequestHeaderModifier type filter with matching field",
routeFilter: []gatewayv1a2.HTTPRouteFilter{{
Type: gatewayv1a2.HTTPRouteFilterRequestHeaderModifier,
RequestHeaderModifier: &gatewayv1a2.HTTPRequestHeaderFilter{},
Copy link
Member

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.

Copy link
Contributor Author

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.

@crmejia
Copy link
Contributor Author

crmejia commented Dec 12, 2021

/retest

@jpeach
Copy link
Contributor

jpeach commented Dec 12, 2021

Once the deep equal issue is done, this LGTM, pending Rob's concurrence.

/lgtm
/hold

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 12, 2021
@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 12, 2021
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 14, 2021
@crmejia
Copy link
Contributor Author

crmejia commented Dec 16, 2021

After some discussion with Rob regarding validation we decided to check for nil instead of empty and to check these two cases for each filter:

  1. if filter != nil && filter.Type != filter return some error
  2. if filter == nil && filter.Type == filter return some other error
    Please let me know what y'all think.

Copy link
Member

@robscott robscott left a 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.

apis/v1alpha2/validation/httproute.go Outdated Show resolved Hide resolved
Comment on lines 181 to 183
if filter.Type == "" {
errs = append(errs, field.Required(path, "filter.Type cannot be empty"))
}
Copy link
Member

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

@robscott robscott added the tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. label Dec 17, 2021
@robscott
Copy link
Member

Thanks @crmejia! Will leave final LGTM for someone else, but this looks great.

/approve

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 17, 2021
Copy link
Contributor

@jpeach jpeach left a comment

Choose a reason for hiding this comment

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

/lgtm

\o/

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 20, 2021
@k8s-ci-robot
Copy link
Contributor

[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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@jpeach
Copy link
Contributor

jpeach commented Dec 20, 2021

/remove-hold

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 20, 2021
@k8s-ci-robot k8s-ci-robot merged commit ceaec64 into kubernetes-sigs:master Dec 20, 2021
@crmejia crmejia deleted the validate_HTTPRouteFilter_consistency branch December 20, 2021 22:21
robscott pushed a commit to robscott/gateway-api that referenced this pull request Mar 24, 2022
* 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
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. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", 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/L Denotes a PR that changes 100-499 lines, ignoring generated files. tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Validate HTTP filter discriminated union consistency.
5 participants