Skip to content

Commit 7281d3c

Browse files
authored
Add functional tests for ClientSettingsPolicies (#2013)
Problem: No automated functional tests for ClientSettingsPolicies. Solution: Add functional tests for ClientSettingsPolicies. Test Cases: - NGF writes the correct status on policies - Valid policies are accepted - Conflicting policies are marked as conflicted - Invalid policies are not accepted - NGF propagates the body.maxSize setting in the NGINX config. This is the only field in ClientSettingsPolicy that we can test e2e by sending traffic.
1 parent f908851 commit 7281d3c

21 files changed

+885
-46
lines changed

.github/workflows/functional.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,10 @@ jobs:
119119
ngf_tag=${{ steps.ngf-meta.outputs.version }}
120120
make test${{ inputs.image == 'plus' && '-with-plus' || ''}} PREFIX=${ngf_prefix} TAG=${ngf_tag} GINKGO_LABEL=telemetry
121121
working-directory: ./tests
122+
123+
- name: Run functional tests
124+
run: |
125+
ngf_prefix=ghcr.io/nginxinc/nginx-gateway-fabric
126+
ngf_tag=${{ steps.ngf-meta.outputs.version }}
127+
make test${{ inputs.image == 'plus' && '-with-plus' || ''}} PREFIX=${ngf_prefix} TAG=${ngf_tag}
128+
working-directory: ./tests

tests/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ Directory structure is as follows:
255255

256256
> Note: Existing NFR tests will be migrated into this testing `suite` and results stored in the `results` directory.
257257
258+
### Logging in tests
259+
260+
To log in the tests, use the `GinkgoWriter` interface described here: https://onsi.github.io/ginkgo/#logging-output.
261+
258262
### Step 1 - Run the tests
259263

260264
#### 1a - Run the functional tests locally

tests/conformance/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ FROM golang:1.22
33

44
WORKDIR /go/src/github.com/nginxinc/nginx-gateway-fabric/tests/conformance
55

6+
COPY ../. /go/src/github.com/nginxinc/nginx-gateway-fabric/
67
COPY --link go.mod /go/src/github.com/nginxinc/nginx-gateway-fabric/tests/
78
COPY --link go.sum /go/src/github.com/nginxinc/nginx-gateway-fabric/tests/
89
RUN go mod download

tests/framework/ngf.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ type InstallationConfig struct {
3232
}
3333

3434
// InstallGatewayAPI installs the specified version of the Gateway API resources.
35-
func InstallGatewayAPI(
36-
k8sClient client.Client,
37-
apiVersion,
38-
k8sVersion string,
39-
) ([]byte, error) {
35+
func InstallGatewayAPI(apiVersion string) ([]byte, error) {
4036
apiPath := fmt.Sprintf("%s/v%s/standard-install.yaml", gwInstallBasePath, apiVersion)
4137

4238
if output, err := exec.Command("kubectl", "apply", "-f", apiPath).CombinedOutput(); err != nil {
@@ -163,7 +159,9 @@ func setImageArgs(cfg InstallationConfig) []string {
163159
if cfg.ServiceType != "" {
164160
args = append(args, formatValueSet("service.type", cfg.ServiceType)...)
165161
if cfg.ServiceType == "LoadBalancer" && cfg.IsGKEInternalLB {
166-
args = append(args, formatValueSet(`service.annotations.networking\.gke\.io\/load-balancer-type`, "Internal")...)
162+
args = append(
163+
args,
164+
formatValueSet(`service.annotations.networking\.gke\.io\/load-balancer-type`, "Internal")...)
167165
}
168166
}
169167

tests/framework/request.go

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"crypto/tls"
77
"fmt"
8+
"io"
89
"net"
910
"net/http"
1011
"strings"
@@ -15,6 +16,29 @@ import (
1516
// It resolves to the specified address instead of using DNS.
1617
// The status and body of the response is returned, or an error.
1718
func Get(url, address string, timeout time.Duration) (int, string, error) {
19+
resp, err := makeRequest(http.MethodGet, url, address, nil, timeout)
20+
if err != nil {
21+
return 0, "", err
22+
}
23+
24+
defer resp.Body.Close()
25+
26+
body := new(bytes.Buffer)
27+
_, err = body.ReadFrom(resp.Body)
28+
if err != nil {
29+
return resp.StatusCode, "", err
30+
}
31+
32+
return resp.StatusCode, body.String(), nil
33+
}
34+
35+
// Post sends a POST request to the specified url with the body as the payload.
36+
// It resolves to the specified address instead of using DNS.
37+
func Post(url, address string, body io.Reader, timeout time.Duration) (*http.Response, error) {
38+
return makeRequest(http.MethodPost, url, address, body, timeout)
39+
}
40+
41+
func makeRequest(method, url, address string, body io.Reader, timeout time.Duration) (*http.Response, error) {
1842
dialer := &net.Dialer{}
1943

2044
http.DefaultTransport.(*http.Transport).DialContext = func(
@@ -30,9 +54,9 @@ func Get(url, address string, timeout time.Duration) (int, string, error) {
3054
ctx, cancel := context.WithTimeout(context.Background(), timeout)
3155
defer cancel()
3256

33-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
57+
req, err := http.NewRequestWithContext(ctx, method, url, body)
3458
if err != nil {
35-
return 0, "", err
59+
return nil, err
3660
}
3761

3862
var resp *http.Response
@@ -48,15 +72,8 @@ func Get(url, address string, timeout time.Duration) (int, string, error) {
4872
}
4973

5074
if err != nil {
51-
return 0, "", err
52-
}
53-
defer resp.Body.Close()
54-
55-
body := new(bytes.Buffer)
56-
_, err = body.ReadFrom(resp.Body)
57-
if err != nil {
58-
return resp.StatusCode, "", err
75+
return nil, err
5976
}
6077

61-
return resp.StatusCode, body.String(), nil
78+
return resp, nil
6279
}

tests/framework/resourcemanager.go

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,11 @@ func (rm *ResourceManager) WaitForAppsToBeReadyWithCtx(ctx context.Context, name
313313
return err
314314
}
315315

316-
if err := rm.waitForRoutesToBeReady(ctx, namespace); err != nil {
316+
if err := rm.waitForHTTPRoutesToBeReady(ctx, namespace); err != nil {
317+
return err
318+
}
319+
320+
if err := rm.waitForGRPCRoutesToBeReady(ctx, namespace); err != nil {
317321
return err
318322
}
319323

@@ -371,7 +375,7 @@ func (rm *ResourceManager) waitForGatewaysToBeReady(ctx context.Context, namespa
371375
)
372376
}
373377

374-
func (rm *ResourceManager) waitForRoutesToBeReady(ctx context.Context, namespace string) error {
378+
func (rm *ResourceManager) waitForHTTPRoutesToBeReady(ctx context.Context, namespace string) error {
375379
return wait.PollUntilContextCancel(
376380
ctx,
377381
500*time.Millisecond,
@@ -385,13 +389,29 @@ func (rm *ResourceManager) waitForRoutesToBeReady(ctx context.Context, namespace
385389
var numParents, readyCount int
386390
for _, route := range routeList.Items {
387391
numParents += len(route.Spec.ParentRefs)
388-
for _, parent := range route.Status.Parents {
389-
for _, cond := range parent.Conditions {
390-
if cond.Type == string(v1.RouteConditionAccepted) && cond.Status == metav1.ConditionTrue {
391-
readyCount++
392-
}
393-
}
394-
}
392+
readyCount += countNumberOfReadyParents(route.Status.Parents)
393+
}
394+
395+
return numParents == readyCount, nil
396+
},
397+
)
398+
}
399+
400+
func (rm *ResourceManager) waitForGRPCRoutesToBeReady(ctx context.Context, namespace string) error {
401+
return wait.PollUntilContextCancel(
402+
ctx,
403+
500*time.Millisecond,
404+
true, /* poll immediately */
405+
func(ctx context.Context) (bool, error) {
406+
var routeList v1.GRPCRouteList
407+
if err := rm.K8sClient.List(ctx, &routeList, client.InNamespace(namespace)); err != nil {
408+
return false, err
409+
}
410+
411+
var numParents, readyCount int
412+
for _, route := range routeList.Items {
413+
numParents += len(route.Spec.ParentRefs)
414+
readyCount += countNumberOfReadyParents(route.Status.Parents)
395415
}
396416

397417
return numParents == readyCount, nil
@@ -649,3 +669,17 @@ func GetReadyNGFPodNames(
649669

650670
return nil, errors.New("unable to find NGF Pod(s)")
651671
}
672+
673+
func countNumberOfReadyParents(parents []v1.RouteParentStatus) int {
674+
readyCount := 0
675+
676+
for _, parent := range parents {
677+
for _, cond := range parent.Conditions {
678+
if cond.Type == string(v1.RouteConditionAccepted) && cond.Status == metav1.ConditionTrue {
679+
readyCount++
680+
}
681+
}
682+
}
683+
684+
return readyCount
685+
}

tests/framework/timeout.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ type TimeoutConfig struct {
2929

3030
// GetLeaderLeaseTimeout represents the maximum time for NGF to retrieve the leader lease.
3131
GetLeaderLeaseTimeout time.Duration
32+
33+
// GetStatusTimeout represents the maximum time for NGF to update the status of a resource.
34+
GetStatusTimeout time.Duration
3235
}
3336

3437
// DefaultTimeoutConfig populates a TimeoutConfig with the default values.
@@ -43,5 +46,6 @@ func DefaultTimeoutConfig() TimeoutConfig {
4346
RequestTimeout: 10 * time.Second,
4447
ContainerRestartTimeout: 10 * time.Second,
4548
GetLeaderLeaseTimeout: 60 * time.Second,
49+
GetStatusTimeout: 60 * time.Second,
4650
}
4751
}

tests/go.mod

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/nginxinc/nginx-gateway-fabric/tests
33
go 1.22.2
44

55
require (
6+
github.com/nginxinc/nginx-gateway-fabric v0.0.0
67
github.com/onsi/ginkgo/v2 v2.19.0
78
github.com/onsi/gomega v1.33.1
89
github.com/prometheus/client_golang v1.19.1
@@ -24,7 +25,7 @@ require (
2425
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
2526
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
2627
github.com/fsnotify/fsnotify v1.7.0 // indirect
27-
github.com/go-logr/logr v1.4.1 // indirect
28+
github.com/go-logr/logr v1.4.2 // indirect
2829
github.com/go-openapi/jsonpointer v0.21.0 // indirect
2930
github.com/go-openapi/jsonreference v0.21.0 // indirect
3031
github.com/go-openapi/swag v0.23.0 // indirect
@@ -59,17 +60,17 @@ require (
5960
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
6061
golang.org/x/mod v0.17.0 // indirect
6162
golang.org/x/net v0.25.0 // indirect
62-
golang.org/x/oauth2 v0.19.0 // indirect
63+
golang.org/x/oauth2 v0.20.0 // indirect
6364
golang.org/x/sync v0.7.0 // indirect
6465
golang.org/x/sys v0.20.0 // indirect
6566
golang.org/x/term v0.20.0 // indirect
6667
golang.org/x/text v0.15.0 // indirect
6768
golang.org/x/time v0.5.0 // indirect
6869
golang.org/x/tools v0.21.0 // indirect
6970
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
70-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
71-
google.golang.org/grpc v1.63.2 // indirect
72-
google.golang.org/protobuf v1.33.0 // indirect
71+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
72+
google.golang.org/grpc v1.64.0 // indirect
73+
google.golang.org/protobuf v1.34.1 // indirect
7374
gopkg.in/inf.v0 v0.9.1 // indirect
7475
gopkg.in/yaml.v2 v2.4.0 // indirect
7576
gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -79,3 +80,5 @@ require (
7980
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
8081
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
8182
)
83+
84+
replace github.com/nginxinc/nginx-gateway-fabric => ../

tests/go.sum

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0
1919
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
2020
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
2121
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
22-
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
23-
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
22+
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
23+
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
2424
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
2525
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
2626
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
@@ -122,8 +122,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
122122
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
123123
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
124124
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
125-
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
126-
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
125+
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
126+
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
127127
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
128128
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
129129
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -140,8 +140,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
140140
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
141141
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
142142
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
143-
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
144-
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
143+
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
144+
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
145145
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
146146
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
147147
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -176,12 +176,12 @@ gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuB
176176
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca h1:PupagGYwj8+I4ubCxcmcBRk3VlUWtTg5huQpZR9flmE=
177177
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
178178
gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
179-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
180-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
181-
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
182-
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
183-
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
184-
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
179+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
180+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
181+
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
182+
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
183+
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
184+
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
185185
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
186186
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
187187
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

0 commit comments

Comments
 (0)