Skip to content

Commit 76b34af

Browse files
authored
GRPCRoute Support (#1835)
* GRPCRoute Support
1 parent 7c3da8d commit 76b34af

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4643
-2765
lines changed

Makefile

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ TELEMETRY_ENDPOINT=# if empty, NGF will report telemetry in its logs at debug le
1616
TELEMETRY_ENDPOINT_INSECURE = false
1717

1818
GW_API_VERSION = 1.0.0
19+
ENABLE_EXPERIMENTAL = false
1920
NODE_VERSION = $(shell cat .nvmrc)
2021

2122
# go build flags - should not be overridden by the user
@@ -192,13 +193,13 @@ install-ngf-local-build-with-plus: build-images-with-plus load-images-with-plus
192193

193194
.PHONY: helm-install-local
194195
helm-install-local: ## Helm install NGF on configured kind cluster with local images. To build, load, and install with helm run make install-ngf-local-build.
195-
./conformance/scripts/install-gateway.sh $(GW_API_VERSION)
196-
helm install dev $(CHART_DIR) --create-namespace --wait --set service.type=NodePort --set nginxGateway.image.repository=$(PREFIX) --set nginxGateway.image.tag=$(TAG) --set nginxGateway.image.pullPolicy=Never --set nginx.image.repository=$(NGINX_PREFIX) --set nginx.image.tag=$(TAG) --set nginx.image.pullPolicy=Never -n nginx-gateway
196+
./conformance/scripts/install-gateway.sh $(GW_API_VERSION) $(ENABLE_EXPERIMENTAL)
197+
helm install dev $(CHART_DIR) --create-namespace --wait --set service.type=NodePort --set nginxGateway.image.repository=$(PREFIX) --set nginxGateway.image.tag=$(TAG) --set nginxGateway.image.pullPolicy=Never --set nginx.image.repository=$(NGINX_PREFIX) --set nginx.image.tag=$(TAG) --set nginx.image.pullPolicy=Never --set nginxGateway.gwAPIExperimentalFeatures.enable=$(ENABLE_EXPERIMENTAL) -n nginx-gateway
197198

198199
.PHONY: helm-install-local-with-plus
199200
helm-install-local-with-plus: ## Helm install NGF with NGINX Plus on configured kind cluster with local images. To build, load, and install with helm run make install-ngf-local-build-with-plus.
200-
./conformance/scripts/install-gateway.sh $(GW_API_VERSION)
201-
helm install dev $(CHART_DIR) --create-namespace --wait --set service.type=NodePort --set nginxGateway.image.repository=$(PREFIX) --set nginxGateway.image.tag=$(TAG) --set nginxGateway.image.pullPolicy=Never --set nginx.image.repository=$(NGINX_PLUS_PREFIX) --set nginx.image.tag=$(TAG) --set nginx.image.pullPolicy=Never --set nginx.plus=true -n nginx-gateway
201+
./conformance/scripts/install-gateway.sh $(GW_API_VERSION) $(ENABLE_EXPERIMENTAL)
202+
helm install dev $(CHART_DIR) --create-namespace --wait --set service.type=NodePort --set nginxGateway.image.repository=$(PREFIX) --set nginxGateway.image.tag=$(TAG) --set nginxGateway.image.pullPolicy=Never --set nginx.image.repository=$(NGINX_PLUS_PREFIX) --set nginx.image.tag=$(TAG) --set nginx.image.pullPolicy=Never --set nginx.plus=true --set nginxGateway.gwAPIExperimentalFeatures.enable=$(ENABLE_EXPERIMENTAL) -n nginx-gateway
202203

203204
# Debug Targets
204205
.PHONY: debug-build

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
NGINX Gateway Fabric is an open-source project that provides an implementation of
1313
the [Gateway API](https://gateway-api.sigs.k8s.io/) using [NGINX](https://nginx.org/) as the data plane. The goal of
14-
this project is to implement the core Gateway APIs -- `Gateway`, `GatewayClass`, `HTTPRoute`, `TCPRoute`, `TLSRoute`,
14+
this project is to implement the core Gateway APIs -- `Gateway`, `GatewayClass`, `HTTPRoute`, `GRPCRoute`, `TCPRoute`, `TLSRoute`,
1515
and `UDPRoute` -- to configure an HTTP or TCP/UDP load balancer, reverse-proxy, or API gateway for applications running
1616
on Kubernetes. NGINX Gateway Fabric supports a subset of the Gateway API.
1717

build/Dockerfile.nginx

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ RUN apk add --no-cache libcap \
1313

1414
COPY ${NJS_DIR}/httpmatches.js /usr/lib/nginx/modules/njs/httpmatches.js
1515
COPY ${NGINX_CONF_DIR}/nginx.conf /etc/nginx/nginx.conf
16+
COPY ${NGINX_CONF_DIR}/grpc-error-locations.conf /etc/nginx/grpc-error-locations.conf
17+
COPY ${NGINX_CONF_DIR}/grpc-error-pages.conf /etc/nginx/grpc-error-pages.conf
1618

1719
RUN chown -R 101:1001 /etc/nginx /var/cache/nginx /var/lib/nginx
1820

build/Dockerfile.nginxplus

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \
2929

3030
COPY ${NJS_DIR}/httpmatches.js /usr/lib/nginx/modules/njs/httpmatches.js
3131
COPY ${NGINX_CONF_DIR}/nginx-plus.conf /etc/nginx/nginx.conf
32+
COPY ${NGINX_CONF_DIR}/grpc-error-locations.conf /etc/nginx/grpc-error-locations.conf
33+
COPY ${NGINX_CONF_DIR}/grpc-error-pages.conf /etc/nginx/grpc-error-pages.conf
3234

3335
RUN chown -R 101:1001 /etc/nginx /var/cache/nginx /var/lib/nginx
3436

charts/nginx-gateway-fabric/templates/rbac.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ rules:
9292
- referencegrants
9393
{{- if .Values.nginxGateway.gwAPIExperimentalFeatures.enable }}
9494
- backendtlspolicies
95+
- grpcroutes
9596
{{- end }}
9697
verbs:
9798
- list
@@ -104,6 +105,7 @@ rules:
104105
- gatewayclasses/status
105106
{{- if .Values.nginxGateway.gwAPIExperimentalFeatures.enable }}
106107
- backendtlspolicies/status
108+
- grpcroutes/status
107109
{{- end }}
108110
verbs:
109111
- update

conformance/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ PROVISIONER_MANIFEST=provisioner/provisioner.yaml
1717
ENABLE_EXPERIMENTAL ?= false
1818
.DEFAULT_GOAL := help
1919

20+
ifeq ($(ENABLE_EXPERIMENTAL),true)
21+
SUPPORTED_FEATURES +=,GRPCExactMethodMatching,GRPCRouteListenerHostnameMatching,GRPCRouteHeaderMatching
22+
endif
23+
2024
.PHONY: help
2125
help: Makefile ## Display this help
2226
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "; printf "Usage:\n\n make \033[36m<target>\033[0m\n\nTargets:\n\n"}; {printf " \033[36m%-30s\033[0m %s\n", $$1, $$2}'

deploy/manifests/nginx-gateway-experimental.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ rules:
7777
- httproutes
7878
- referencegrants
7979
- backendtlspolicies
80+
- grpcroutes
8081
verbs:
8182
- list
8283
- watch
@@ -87,6 +88,7 @@ rules:
8788
- gateways/status
8889
- gatewayclasses/status
8990
- backendtlspolicies/status
91+
- grpcroutes/status
9092
verbs:
9193
- update
9294
- apiGroups:

deploy/manifests/nginx-plus-gateway-experimental.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ rules:
8383
- httproutes
8484
- referencegrants
8585
- backendtlspolicies
86+
- grpcroutes
8687
verbs:
8788
- list
8889
- watch
@@ -93,6 +94,7 @@ rules:
9394
- gateways/status
9495
- gatewayclasses/status
9596
- backendtlspolicies/status
97+
- grpcroutes/status
9698
verbs:
9799
- update
98100
- apiGroups:

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
3535
github.com/cespare/xxhash/v2 v2.2.0 // indirect
3636
github.com/davecgh/go-spew v1.1.1 // indirect
37-
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
37+
github.com/emicklei/go-restful/v3 v3.11.2 // indirect
3838
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
3939
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
4040
github.com/fatih/color v1.16.0 // indirect

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
1111
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1212
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1313
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14-
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
15-
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
14+
github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU=
15+
github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
1616
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
1717
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
1818
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=

internal/framework/gatewayclass/validate.go

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var gatewayCRDs = map[string]apiVersion{
2222
"httproutes.gateway.networking.k8s.io": {},
2323
"referencegrants.gateway.networking.k8s.io": {},
2424
"backendtlspolicies.gateway.networking.k8s.io": {},
25+
"grpcroutes.gateway.networking.k8s.io": {},
2526
}
2627

2728
type apiVersion struct {

internal/mode/static/handler.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,13 @@ func (h *eventHandlerImpl) updateStatuses(ctx context.Context, logger logr.Logge
245245
if h.cfg.updateGatewayClassStatus {
246246
gcReqs = status.PrepareGatewayClassRequests(graph.GatewayClass, graph.IgnoredGatewayClasses, transitionTime)
247247
}
248-
routeReqs := status.PrepareRouteRequests(graph.Routes, transitionTime, h.latestReloadResult, h.cfg.gatewayCtlrName)
248+
routeReqs := status.PrepareRouteRequests(
249+
graph.Routes,
250+
transitionTime,
251+
h.latestReloadResult,
252+
h.cfg.gatewayCtlrName,
253+
)
254+
249255
polReqs := status.PrepareBackendTLSPolicyRequests(graph.BackendTLSPolicies, transitionTime, h.cfg.gatewayCtlrName)
250256

251257
reqs := make([]frameworkStatus.UpdateRequest, 0, len(gcReqs)+len(routeReqs)+len(polReqs))

internal/mode/static/manager.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ func registerControllers(
424424
}
425425

426426
if cfg.ExperimentalFeatures {
427-
backendTLSObjs := []ctlrCfg{
427+
gwExpFeatures := []ctlrCfg{
428428
{
429429
objectType: &gatewayv1alpha2.BackendTLSPolicy{},
430430
options: []controller.Option{
@@ -436,8 +436,14 @@ func registerControllers(
436436
// https://github.com/nginxinc/nginx-gateway-fabric/issues/1545
437437
objectType: &apiv1.ConfigMap{},
438438
},
439+
{
440+
objectType: &gatewayv1alpha2.GRPCRoute{},
441+
options: []controller.Option{
442+
controller.WithK8sPredicate(k8spredicate.GenerationChangedPredicate{}),
443+
},
444+
},
439445
}
440-
controllerRegCfgs = append(controllerRegCfgs, backendTLSObjs...)
446+
controllerRegCfgs = append(controllerRegCfgs, gwExpFeatures...)
441447
}
442448

443449
if cfg.ConfigName != "" {
@@ -604,7 +610,12 @@ func prepareFirstEventBatchPreparerArgs(
604610
}
605611

606612
if enableExperimentalFeatures {
607-
objectLists = append(objectLists, &gatewayv1alpha2.BackendTLSPolicyList{}, &apiv1.ConfigMapList{})
613+
objectLists = append(
614+
objectLists,
615+
&gatewayv1alpha2.BackendTLSPolicyList{},
616+
&apiv1.ConfigMapList{},
617+
&gatewayv1alpha2.GRPCRouteList{},
618+
)
608619
}
609620

610621
if gwNsName == nil {

internal/mode/static/manager_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func TestPrepareFirstEventBatchPreparerArgs(t *testing.T) {
9999
&ngfAPI.NginxProxyList{},
100100
partialObjectMetadataList,
101101
&gatewayv1alpha2.BackendTLSPolicyList{},
102+
&gatewayv1alpha2.GRPCRouteList{},
102103
},
103104
experimentalEnabled: true,
104105
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
location @grpc_deadline_exceeded {
2+
default_type application/grpc;
3+
add_header content-type application/grpc;
4+
add_header grpc-status 4;
5+
add_header grpc-message 'deadline exceeded';
6+
return 204;
7+
}
8+
9+
location @grpc_permission_denied {
10+
default_type application/grpc;
11+
add_header content-type application/grpc;
12+
add_header grpc-status 7;
13+
add_header grpc-message 'permission denied';
14+
return 204;
15+
}
16+
17+
location @grpc_resource_exhausted {
18+
default_type application/grpc;
19+
add_header content-type application/grpc;
20+
add_header grpc-status 8;
21+
add_header grpc-message 'resource exhausted';
22+
return 204;
23+
}
24+
25+
location @grpc_unimplemented {
26+
default_type application/grpc;
27+
add_header content-type application/grpc;
28+
add_header grpc-status 12;
29+
add_header grpc-message unimplemented;
30+
return 204;
31+
}
32+
33+
location @grpc_internal {
34+
default_type application/grpc;
35+
add_header content-type application/grpc;
36+
add_header grpc-status 13;
37+
add_header grpc-message 'internal error';
38+
return 204;
39+
}
40+
41+
location @grpc_unavailable {
42+
default_type application/grpc;
43+
add_header content-type application/grpc;
44+
add_header grpc-status 14;
45+
add_header grpc-message unavailable;
46+
return 204;
47+
}
48+
49+
location @grpc_unauthenticated {
50+
default_type application/grpc;
51+
add_header content-type application/grpc;
52+
add_header grpc-status 16;
53+
add_header grpc-message unauthenticated;
54+
return 204;
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error_page 400 = @grpc_internal;
2+
error_page 401 = @grpc_unauthenticated;
3+
error_page 403 = @grpc_permission_denied;
4+
error_page 404 = @grpc_unimplemented;
5+
error_page 429 = @grpc_unavailable;
6+
error_page 502 = @grpc_unavailable;
7+
error_page 503 = @grpc_unavailable;
8+
error_page 504 = @grpc_unavailable;
9+
error_page 405 = @grpc_internal;
10+
error_page 408 = @grpc_deadline_exceeded;
11+
error_page 413 = @grpc_resource_exhausted;
12+
error_page 414 = @grpc_resource_exhausted;
13+
error_page 415 = @grpc_internal;
14+
error_page 426 = @grpc_internal;
15+
error_page 495 = @grpc_unauthenticated;
16+
error_page 496 = @grpc_unauthenticated;
17+
error_page 497 = @grpc_internal;
18+
error_page 500 = @grpc_internal;
19+
error_page 501 = @grpc_internal;

internal/mode/static/nginx/conf/nginx-plus.conf

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ http {
2727
sendfile on;
2828
tcp_nopush on;
2929

30+
http2 on;
31+
3032
server {
3133
listen 127.0.0.1:8765;
3234
root /usr/share/nginx/html;

internal/mode/static/nginx/conf/nginx.conf

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ http {
2727
sendfile on;
2828
tcp_nopush on;
2929

30+
http2 on;
31+
3032
server {
3133
listen unix:/var/run/nginx/nginx-status.sock;
3234
access_log off;

internal/mode/static/nginx/config/http/config.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Server struct {
77
Locations []Location
88
IsDefaultHTTP bool
99
IsDefaultSSL bool
10+
GRPC bool
1011
Port int32
1112
}
1213

@@ -19,9 +20,10 @@ type Location struct {
1920
ProxySSLVerify *ProxySSLVerify
2021
Return *Return
2122
Rewrites []string
23+
GRPC bool
2224
}
2325

24-
// Header defines a HTTP header to be passed to the proxied server.
26+
// Header defines an HTTP header to be passed to the proxied server.
2527
type Header struct {
2628
Name string
2729
Value string

0 commit comments

Comments
 (0)