feat(api-gateway): add header match invert filter - #5593
Open
bharath-k1999 wants to merge 5 commits into
Open
Conversation
Go Test Coverage: 61.4%Patch coverage: 30.4% (38/125 changed lines covered) See the workflow run for the full per-package breakdown and downloadable HTML report. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5593 +/- ##
==========================================
- Coverage 58.62% 58.54% -0.09%
==========================================
Files 298 299 +1
Lines 36767 36867 +100
==========================================
+ Hits 21554 21583 +29
- Misses 13268 13341 +73
+ Partials 1945 1943 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
Invert(negation) support to API Gateway HTTP header matching, bringing it to paritywith Ingress Gateway which already supports
Invert boolonServiceRouteHTTPMatchHeader.Because the native Kubernetes Gateway API
HTTPHeaderMatchstruct has no invert/negate field,this is implemented via a new Consul-specific CRD —
RouteHeaderMatchInvertFilter— referencedas an
ExtensionReffilter on anHTTPRouterule. The CRD lists the header names whose matchcondition should be negated when the rule is translated into a Consul
http-routeconfig entry.Motivating patterns
Present = true, Invert = true"v2"Exact = "v2", Invert = truex-canary: "true", Invert = true→ send to new version when canary header absentx-legacy-client: present, Invert = true→ serve new API to non-legacy clientsChanges
consul core —
api/config_entry_routes.go+agent/structs/config_entry_routes.goInvert booltoHTTPHeaderMatchin both the HTTP API type and the internal struct.consul core —
proto/private/pbconfigentry/bool Invert = 4to theHTTPHeaderMatchprotobuf message.config_entry.gen.gomog converters andconfig_entry.pb.gogenerated file(
rawDescstring, field descriptor,GetInvert()accessor).consul core —
agent/consul/discoverychain/gateway_httproute.goInvertin all fiveHTTPRouteMatchToServiceRouteHTTPMatchmatch-type cases(exact, prefix, suffix, regex, present).
consul-k8s —
control-plane/api/v1alpha1/routeheadermatchinvertfilter_types.go(new)RouteHeaderMatchInvertFilterwithspec.headerNames []string.init().consul-k8s —
control-plane/api/v1alpha1/zz_generated.deepcopy.goRouteHeaderMatchInvertFilter,RouteHeaderMatchInvertFilterList,RouteHeaderMatchInvertFilterSpec,RouteHeaderMatchInvertFilterStatus.consul-k8s —
control-plane/api-gateway/controllers/index.goHTTPRoute_RouteHeaderMatchInvertFilterIndexconstant and index registration entry.consul-k8s —
control-plane/api-gateway/controllers/gateway_controller.goWatchesentry forRouteHeaderMatchInvertFilterobjects.transformRouteHeaderMatchInvertFilterenqueue function.RouteHeaderMatchInvertFilterKindcase in the external filter resolver switch.consul-k8s —
control-plane/api-gateway/common/translation.gotranslateHTTPHeaderMatchnow delegates totranslateHTTPHeaderMatchWithInvert.translateHTTPHeaderMatchWithInvertsetsInvert = truewhen the header name (lowercased)appears in an
invertedHeadersset.invertedHeaderNamesForRulescans a rule's filters for aRouteHeaderMatchInvertFilterExtensionRefand returns the lowercase set of header names.translateHTTPRouteRulecallsinvertedHeaderNamesForRuleand passes the set throughwhen translating each header match.
consul-k8s —
control-plane/api-gateway/common/diff.gohttpHeaderMatchesEqualwas missingInvertfrom its comparison — without this,day-2 updates to a
RouteHeaderMatchInvertFilterwould not trigger a Consul config entry write.Tests added
control-plane/api-gateway/common/header_match_invert_diff_test.gocontrol-plane/api-gateway/common/header_match_invert_translation_test.goacceptance/tests/api-gateway/api_gateway_header_match_invert_test.goTestAPIGateway_HeaderMatchInvert_Lifecycle— 3-phase lifecycle acceptance testacceptance/tests/fixtures/cases/api-gateways/header-match-invert/Local Testing
Unit tests
Expected output (all pass):
Manual end-to-end (plain HTTP gateway, no cluster required)
1. Apply the
RouteHeaderMatchInvertFilterCRDApply the new CRD to your cluster:
cd hashicorp/consul-k8s kubectl apply -f control-plane/config/crd/bases/consul.hashicorp.com_routeheadermatchinvertfilters.yaml2. Deploy all fixture resources
This deploys in one shot:
static-serverbackend (connect-injected)GatewayClassConfig→GatewayClass(header-invert-class)Gateway(header-invert-gw) — plain HTTP on port 8080RouteHeaderMatchInvertFilter× 2 (invert-filter-v1,invert-filter-v2)HTTPRoute(invert-route) — Day-1 state withExtensionRef → invert-filter-v13. Verify the Consul config entry (Day 1)
consul config read -kind http-route -name invert-routeExpected —
Rules[0].Matches[0].Headers[0]should show:{ "Name": "x-canary", "Value": "true", "Match": "exact", "Invert": true }4. Simulate Day 2 — swap filter to
invert-filter-v2Re-read the Consul config entry and verify:
consul config read -kind http-route -name invert-routeExpected:
{ "Name": "x-version", "Value": "true", "Match": "exact", "Invert": true }5. Simulate Day 3 — remove the ExtensionRef entirely
Expected —
Invertreverts tofalse:{ "Name": "x-version", "Value": "true", "Match": "exact", "Invert": false }6. Verify traffic routing with curl
Assuming the gateway is port-forwarded to
localhost:8080:Run the acceptance test
The acceptance test requires a real Kubernetes cluster with Consul deployed via Helm.
The test exercises the full 3-phase lifecycle:
invert-filter-v1(x-canary)Headers[0].Name == "x-canary",Invert == trueinvert-filter-v2(x-version)Headers[0].Name == "x-version",Invert == trueInvert == falseapi-gateway+http-routeconfig entries absent from Consul