Skip to content

Commit

Permalink
feat(kuma-cp) add GatewayRoute route generation (#2782)
Browse files Browse the repository at this point in the history
Add machinery to generate Envoy routes from GatewayRoute resources. This
is split into a generator that translates GatewayRoute resources into a
generic route table structure, and a generator that translates the generic
structure into Envoy resources. The generic route table structure provides
an API-independent way to configure Envoy.

This change only generates routes (not cluster or endpoints), but the full
semantics of GatewayRoute are supported, including path prefix matches,
multiple match criteria and correct route match ordering.

Signed-off-by: James Peach <james.peach@konghq.com>
  • Loading branch information
jpeach authored Sep 27, 2021
1 parent 13df4cf commit 511eeb9
Show file tree
Hide file tree
Showing 29 changed files with 2,597 additions and 82 deletions.
76 changes: 40 additions & 36 deletions api/mesh/v1alpha1/gateway_route.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions api/mesh/v1alpha1/gateway_route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,16 @@ message GatewayRoute {

// Filters are request processing steps that are applied to
// matched requests.
//
// If the redirect filter is specified, it must be the only
// filter given.
repeated Filter filters = 2;

// Backends is the set of services to which the gateway will
// forward requests.
repeated Backend backends = 3
[ (doc.required) = true, (validate.rules).repeated .min_items = 1 ];
// forward requests. If a redirect filter is specified, no
// backends are allowed. Otherwise, at least one backend
// must be given.
repeated Backend backends = 3 [ (doc.required) = false ];
};

// Hostnames lists the server names for which this route is valid. The
Expand Down
30 changes: 26 additions & 4 deletions pkg/core/resources/apis/mesh/gateway_route_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,46 @@ func validateGatewayRouteHTTPRule(
path validators.PathBuilder,
conf *mesh_proto.GatewayRoute_HttpRoute_Rule,
) validators.ValidationError {
var hasRedirect bool

if len(conf.GetMatches()) < 1 {
return validators.MakeRequiredFieldErr(path.Field("matches"))
}

if len(conf.GetBackends()) < 1 {
return validators.MakeRequiredFieldErr(path.Field("backends"))
}

var err validators.ValidationError

for i, m := range conf.GetMatches() {
err.Add(validateGatewayRouteHTTPMatch(path.Field("matches").Index(i), m))
}

for i, f := range conf.GetFilters() {
if f.GetRedirect() != nil {
hasRedirect = true
}

err.Add(validateGatewayRouteHTTPFilter(path.Field("filters").Index(i), f))
}

// It doesn't make sense to redirect and also mirror or rewrite request headers.
if hasRedirect && len(conf.GetFilters()) != 1 {
err.AddViolationAt(path.Field("filters"), "redirects cannot be used with other filters")

// Return since the redirect filter error makes the backend length check ambiguous.
return err
}

switch len(conf.GetBackends()) {
case 0:
// Redirection doesn't forward, so there must not be any backend.
if !hasRedirect {
err.AddViolationAt(path.Field("backends"), "cannot be empty")
}
default:
if hasRedirect {
err.AddViolationAt(path.Field("backends"), "must be empty when using redirect filters")
}
}

for i, b := range conf.GetBackends() {
err.Add(validateGatewayRouteBackend(path.Field("backends").Index(i), b))
}
Expand Down
122 changes: 97 additions & 25 deletions pkg/core/resources/apis/mesh/gateway_route_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func (g GatewayRouteGenerator) New() model.Resource {

var _ = Describe("GatewayRoute", func() {
DescribeValidCases(GatewayRouteGenerator(NewGatewayRouteResource),

Entry("HTTP route", `
type: GatewayRoute
name: route
Expand Down Expand Up @@ -53,19 +54,18 @@ conf:
value: kong
filters:
- request_header:
set:
- name: x-foo
value: foo
add:
- name: x-added
value: added
remove:
- x-deleted
- redirect:
scheme: http
hostname: foo.example.com
port: 80
status_code: 307
set:
- name: x-foo
value: foo
- name: x-foo
value: foo
add:
- name: x-added
value: added
- name: x-added
value: added
remove:
- x-deleted
- mirror:
percentage: 1.12
backend:
Expand All @@ -78,6 +78,41 @@ conf:
- weight: 5
destination:
kuma.io/service: target-2
`),
Entry("HTTP redirect", `
type: GatewayRoute
name: route
mesh: default
selectors:
- match:
kuma.io/service: gateway
conf:
http:
hostnames:
- foo.example.com
rules:
- matches:
- method: GET
path:
match: EXACT
value: /
headers:
- match: EXACT
name: x-foo
value: "my great foo"
- match: REGEX
name: x-count
value: "[0-9]+"
query_parameters:
- match: EXACT
name: customer
value: kong
filters:
- redirect:
scheme: http
hostname: foo.example.com
port: 80
status_code: 307
`),
)

Expand Down Expand Up @@ -496,10 +531,6 @@ conf:
- redirect:
hostname: example.com
status_code: 301
backends:
- weight: 5
destination:
kuma.io/service: target-2
`),
ErrorCase("redirect filter with empty hostname", validators.Violation{
Field: "conf.http.rules[0].filters[0].redirect.hostname",
Expand All @@ -521,10 +552,6 @@ conf:
- redirect:
scheme: https
status_code: 301
backends:
- weight: 5
destination:
kuma.io/service: target-2
`),
ErrorCase("redirect filter with invalid port", validators.Violation{
Field: "conf.http.rules[0].filters[0].redirect.port",
Expand All @@ -548,10 +575,6 @@ conf:
hostname: example.com
port: 128555
status_code: 301
backends:
- weight: 5
destination:
kuma.io/service: target-2
`),
ErrorCase("redirect filter with invalid status", validators.Violation{
Field: "conf.http.rules[0].filters[0].redirect.status_code",
Expand All @@ -574,10 +597,59 @@ conf:
scheme: https
hostname: example.com
status_code: 500
`),
ErrorCase("redirect filter with backend routes", validators.Violation{
Field: "conf.http.rules[0].backends",
Message: "must be empty when using redirect filters",
}, `
type: GatewayRoute
name: route
mesh: default
selectors:
- match:
kuma.io/service: gateway
conf:
http:
rules:
- matches:
- path:
value: /
filters:
- redirect:
scheme: https
hostname: example.com
status_code: 300
backends:
- weight: 5
destination:
kuma.io/service: target-2
`),
ErrorCase("redirect prevents other filters", validators.Violation{
Field: "conf.http.rules[0].filters",
Message: "redirects cannot be used with other filters",
}, `
type: GatewayRoute
name: route
mesh: default
selectors:
- match:
kuma.io/service: gateway
conf:
http:
rules:
- matches:
- path:
value: /
filters:
- redirect:
scheme: https
hostname: example.com
status_code: 300
- mirror:
backends:
- weight: 5
destination:
kuma.io/service: target-2
`),
)
})
Loading

0 comments on commit 511eeb9

Please sign in to comment.