Skip to content

Commit 7fa2eab

Browse files
committed
Support multiple routes per path (nginx#126)
* Support multiple routes per path * Adds new boolean field "any" to internal httpMatch object. An httpMatch with any=true represents a path-only match. Any request sent to the path will satisfy this match. * Changes algorithm for sorting routes to match the rules defined by the spec. * Adds local development instructions for njs module as well as test coverage output. * Updates advanced-routing example to demonstrate new capabilities.
1 parent 38d9d1e commit 7fa2eab

File tree

20 files changed

+3583
-560
lines changed

20 files changed

+3583
-560
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080
uses: rutajdash/[email protected]
8181
with:
8282
config_path: ${{ github.workspace }}/internal/nginx/modules/.prettierrc
83-
file_pattern: ${{ github.workspace }}/internal/nginx/modules/*.js
83+
file_pattern: ${{ github.workspace }}/internal/nginx/modules/**/*.js
8484
prettier_version: 2.6.2
8585
- name: Prettier Output
8686
if: ${{ failure() }}
@@ -119,9 +119,9 @@ jobs:
119119
- name: Setup Node.js Environment
120120
uses: actions/setup-node@v3
121121
with:
122-
node_version: 18
122+
node-version: 18
123123
- run: npm install mocha@^8.2 esm chai
124-
- run: npx mocha -r esm ${{ github.workspace }}/internal/nginx/modules/httpmatches_test.js
124+
- run: npx mocha -r esm ${{ github.workspace }}/internal/nginx/modules/test/httpmatches.test.js
125125

126126
binary:
127127
name: Build Binary

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ cover.html
2525

2626
# Binary and Artifacts
2727
build/.out
28+
29+
# Node modules
30+
internal/nginx/modules/node_modules
31+
32+
# JS test coverage
33+
internal/nginx/modules/coverage

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ fmt: ## Run go fmt against code.
6767

6868
.PHONY: njs-fmt
6969
njs-fmt: ## Run prettier against the njs httpmatches module.
70-
docker run --rm \
71-
-v $(PWD)/internal/nginx/modules/:/njs-modules/ \
70+
docker run --rm -w /modules \
71+
-v $(PWD)/internal/nginx/modules/:/modules/ \
7272
node:18 \
73-
npx [email protected] --write njs-modules/ --config=njs-modules/.prettierrc
73+
/bin/bash -c "npm install && npm run format"
7474

7575
.PHONY: vet
7676
vet: ## Run go vet against code.
@@ -86,10 +86,10 @@ unit-test: ## Run unit tests for the go code
8686
go tool cover -html=cover.out -o cover.html
8787

8888
njs-unit-test: ## Run unit tests for the njs httpmatches module.
89-
docker run --rm -w /src \
90-
-v $(PWD)/internal/nginx/modules/:/src/njs-modules/ \
89+
docker run --rm -w /modules \
90+
-v $(PWD)/internal/nginx/modules:/modules/ \
9191
node:18 \
92-
/bin/bash -c "npm install mocha@^8.2 esm chai && npx mocha -r esm njs-modules/httpmatches_test.js"
92+
/bin/bash -c "npm install && npm test && npm run clean"
9393

9494
.PHONY: dev-all
9595
dev-all: deps fmt njs-fmt vet lint unit-test njs-unit-test

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ You can deploy NGINX Kubernetes Gateway on an existing Kubernetes 1.16+ cluster.
6969
1. Create the njs-modules configmap:
7070
7171
```
72-
kubectl create configmap njs-modules --from-file=internal/nginx/modules/httpmatches.js -n nginx-gateway
72+
kubectl create configmap njs-modules --from-file=internal/nginx/modules/src/httpmatches.js -n nginx-gateway
7373
```
7474
7575
1. Deploy the NGINX Kubernetes Gateway:

examples/advanced-routing/README.md

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
In this example we will deploy NGINX Kubernetes Gateway and configure advanced routing rules for a simple cafe application.
44
We will use `HTTPRoute` resources to route traffic to the cafe application based on a combination of the request method, headers, and query parameters.
55

6+
The cafe application consists of four services: `coffee-v1-svc`, `coffee-v2-svc`, `tea-svc`, and `tea-post-svc`. In the next section we will create the following routing rules for the cafe application:
7+
- For the path `/coffee` route requests with the header `version` set to `v2` or with the query param `TEST` set to `v2` to `coffee-v2-svc`, and all other requests to `coffee-v1-svc`.
8+
- For the path `/tea` route POST requests to `tea-post-svc`, and all other requests, such as `GET` requests, to `tea-svc`.
9+
610
## Running the Example
711

812
## 1. Deploy NGINX Kubernetes Gateway
@@ -33,9 +37,11 @@ We will use `HTTPRoute` resources to route traffic to the cafe application based
3337

3438
```
3539
kubectl -n default get pods
36-
NAME READY STATUS RESTARTS AGE
37-
coffee-6f4b79b975-2sb28 1/1 Running 0 12s
38-
tea-6fb46d899f-fm7zr 1/1 Running 0 12s
40+
NAME READY STATUS RESTARTS AGE
41+
coffee-v1-75869cf7ff-vlfpq 1/1 Running 0 17m
42+
coffee-v2-67499ff985-2k6cc 1/1 Running 0 17m
43+
tea-6fb46d899f-hjzwr 1/1 Running 0 17m
44+
tea-post-648dfcdd6c-2rlqb 1/1 Running 0 17m
3945
```
4046

4147
## 3. Configure Routing
@@ -48,51 +54,68 @@ We will use `HTTPRoute` resources to route traffic to the cafe application based
4854

4955
## 4. Test the Application
5056

51-
We will use `curl` to send requests to the `coffee` and `tea` services.
57+
We will use `curl` to send requests to the `/coffee` and `/tea` endpoints of the cafe application.
5258

5359
### 4.1 Access coffee
5460

55-
Send a `POST` request to the path `/coffee` with the headers `X-Demo-Header:Demo-X1` and `version:v1`:
61+
Send a request with the header `version:v2` and confirm that the response comes from `coffee-v2-svc`:
5662

57-
```
58-
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -X POST -H "X-Demo-Header:Demo-X1" -H "version:v1"
59-
Server address: 10.12.0.18:80
60-
Server name: coffee-7586895968-r26zn
63+
```bash
64+
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "version:v2"
65+
Server address: 10.116.2.67:8080
66+
Server name: coffee-v2-67499ff985-gw6vt
67+
...
6168
```
6269

63-
Header keys are case-insensitive, so we can also access coffee with the following request:
70+
Send a request with the query parameter `TEST=v2` and confirm that the response comes from `coffee-v2-svc`:
6471

65-
```
66-
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -X POST -H "X-DEMO-HEADER:Demo-X1" -H "Version:v1"
67-
Server address: 10.12.0.18:80
68-
Server name: coffee-7586895968-r26zn
72+
```bash
73+
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee?TEST=v2
74+
Server address: 10.116.2.67:8080
75+
Server name: coffee-v2-67499ff985-gw6vt
76+
...
6977
```
7078

71-
Only `POST` requests to the path `/coffee` with the headers `X-Demo-Header:Demo-X1` and `version:v1` will be able to access coffee.
72-
For example, try sending the following `GET` request:
73-
```
74-
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "X-Demo-Header:Demo-X1" -H "version:v1"
75-
```
79+
Send a request without the header or the query parameter and confirm the response comes from `coffee-v1-svc`:
7680

77-
NGINX Kubernetes Gateway returns a 405 since the request method does not match the method defined in the routing rule for `/coffee`.
81+
```bash
82+
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee
83+
Server address: 10.116.2.70:8080
84+
Server name: coffee-v1-75869cf7ff-vlfpq
85+
...
86+
```
7887

7988
### 4.2 Access tea
8089

81-
Send a request to the path `/tea` with the query parameter `Great=Example`:
90+
Send a POST request and confirm that the response comes from `tea-post-svc`:
8291

92+
```bash
93+
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea -X POST
94+
Server address: 10.116.2.72:8080
95+
Server name: tea-post-648dfcdd6c-2rlqb
96+
...
8397
```
84-
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea?Great=Example
85-
Server address: 10.12.0.19:80
86-
Server name: tea-7cd44fcb4d-xfw2x
87-
```
88-
89-
Query parameters are case-sensitive, so the case must match what you specify in the `HTTPRoute` resource.
9098

91-
Only requests to the path `/tea` with the query parameter `Great=Example` will be able to access tea.
92-
For example, try sending the following request:
99+
Send a GET request and confirm that the response comes from `tea-svc`:
93100

94-
```
101+
```bash
95102
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea
103+
Server address: 10.116.3.30:8080
104+
Server name: tea-6fb46d899f-hjzwr
105+
...
96106
```
97107

98-
NGINX Kubernetes Gateway returns a 404 since the request does not satisfy the routing rule configured for `/tea`.
108+
The `/tea` endpoint has routing rules configured for GET and POST requests. If you send a request with a different method, NGINX Kubernetes Gateway will return a 404.
109+
110+
Send a PUT request and confirm the 404 Not Found response:
111+
112+
```bash
113+
curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea -X PUT
114+
<html>
115+
<head><title>404 Not Found</title></head>
116+
<body>
117+
<center><h1>404 Not Found</h1></center>
118+
<hr><center>nginx/1.21.3</center>
119+
</body>
120+
</html>
121+
```
Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
apiVersion: gateway.networking.k8s.io/v1alpha2
22
kind: HTTPRoute
3-
metadata:
4-
name: cafe
5-
spec:
6-
parentRefs:
7-
- name: gateway
8-
namespace: nginx-gateway
9-
sectionName: http
10-
hostnames:
11-
- "cafe.example.com"
12-
rules:
13-
- backendRefs:
14-
- name: main
15-
port: 80
16-
---
17-
apiVersion: gateway.networking.k8s.io/v1alpha2
18-
kind: HTTPRoute
193
metadata:
204
name: coffee
215
spec:
@@ -30,14 +14,24 @@ spec:
3014
- path:
3115
type: PathPrefix
3216
value: /coffee
33-
method: POST
17+
backendRefs:
18+
- name: coffee-v1-svc
19+
port: 80
20+
- matches:
21+
- path:
22+
type: PathPrefix
23+
value: /coffee
3424
headers:
35-
- name: X-Demo-Header # header names are case-insensitive
36-
value: Demo-X1 # header values are case-sensitive
3725
- name: version
38-
value: v1
26+
value: v2
27+
- path:
28+
type: PathPrefix
29+
value: /coffee
30+
queryParams:
31+
- name: TEST
32+
value: v2
3933
backendRefs:
40-
- name: coffee
34+
- name: coffee-v2-svc
4135
port: 80
4236
---
4337
apiVersion: gateway.networking.k8s.io/v1alpha2
@@ -56,9 +50,15 @@ spec:
5650
- path:
5751
type: PathPrefix
5852
value: /tea
59-
queryParams:
60-
- name: Great # query params and values are case-sensitive
61-
value: Example
53+
method: POST
54+
backendRefs:
55+
- name: tea-post-svc
56+
port: 80
57+
- matches:
58+
- path:
59+
type: PathPrefix
60+
value: /tea
61+
method: GET
6262
backendRefs:
63-
- name: tea
63+
- name: tea-svc
6464
port: 80

examples/advanced-routing/cafe.yaml

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,101 @@
11
apiVersion: apps/v1
22
kind: Deployment
33
metadata:
4-
name: coffee
4+
name: coffee-v1
55
spec:
66
replicas: 1
77
selector:
88
matchLabels:
9-
app: coffee
9+
app: coffee-v1
1010
template:
1111
metadata:
1212
labels:
13-
app: coffee
13+
app: coffee-v1
1414
spec:
1515
containers:
16-
- name: coffee
16+
- name: coffee-v1
1717
image: nginxdemos/nginx-hello:plain-text
1818
ports:
1919
- containerPort: 8080
2020
---
2121
apiVersion: v1
2222
kind: Service
2323
metadata:
24-
name: coffee
24+
name: coffee-v1-svc
2525
spec:
2626
ports:
2727
- port: 80
2828
targetPort: 8080
2929
protocol: TCP
3030
name: http
3131
selector:
32-
app: coffee
32+
app: coffee-v1
33+
---
34+
apiVersion: apps/v1
35+
kind: Deployment
36+
metadata:
37+
name: coffee-v2
38+
spec:
39+
replicas: 1
40+
selector:
41+
matchLabels:
42+
app: coffee-v2
43+
template:
44+
metadata:
45+
labels:
46+
app: coffee-v2
47+
spec:
48+
containers:
49+
- name: coffee-v2
50+
image: nginxdemos/nginx-hello:plain-text
51+
ports:
52+
- containerPort: 8080
53+
---
54+
apiVersion: v1
55+
kind: Service
56+
metadata:
57+
name: coffee-v2-svc
58+
spec:
59+
ports:
60+
- port: 80
61+
targetPort: 8080
62+
protocol: TCP
63+
name: http
64+
selector:
65+
app: coffee-v2
66+
---
67+
apiVersion: apps/v1
68+
kind: Deployment
69+
metadata:
70+
name: tea-post
71+
spec:
72+
replicas: 1
73+
selector:
74+
matchLabels:
75+
app: tea-post
76+
template:
77+
metadata:
78+
labels:
79+
app: tea-post
80+
spec:
81+
containers:
82+
- name: tea-post
83+
image: nginxdemos/nginx-hello:plain-text
84+
ports:
85+
- containerPort: 8080
86+
---
87+
apiVersion: v1
88+
kind: Service
89+
metadata:
90+
name: tea-post-svc
91+
spec:
92+
ports:
93+
- port: 80
94+
targetPort: 8080
95+
protocol: TCP
96+
name: http
97+
selector:
98+
app: tea-post
3399
---
34100
apiVersion: apps/v1
35101
kind: Deployment
@@ -54,7 +120,7 @@ spec:
54120
apiVersion: v1
55121
kind: Service
56122
metadata:
57-
name: tea
123+
name: tea-svc
58124
spec:
59125
ports:
60126
- port: 80

0 commit comments

Comments
 (0)