Skip to content

Commit a816513

Browse files
bjee19sjberman
andauthored
CP/DP Update non-functional tests (#3305)
Update non-functional tests for the control plane data plane split. Problem: The non-functional tests do not work for the control plane data plane split changes. Solution: Update non-functional tests. Testing: Scale, Reconfiguration, Performance, and Longevity tests work. Upgrade test doesn't work, however that is sort of planned since the CP/DP split is a breaking change of NGF and thus you can't easily upgrade with zero downtime. --------- Co-authored-by: Saylor Berman <[email protected]>
1 parent 773fea0 commit a816513

27 files changed

+486
-574
lines changed

.github/workflows/nfr.yml

+7
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ jobs:
9292
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY }}
9393
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
9494

95+
- name: Login to GAR
96+
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
97+
with:
98+
registry: us-docker.pkg.dev
99+
username: oauth2accesstoken
100+
password: ${{ steps.auth.outputs.access_token }}
101+
95102
- name: Set up Cloud SDK
96103
uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4
97104
with:

charts/nginx-gateway-fabric/values.yaml

+5-5
Original file line numberDiff line numberDiff line change
@@ -372,19 +372,19 @@ nginx:
372372
# -- The termination grace period of the NGINX data plane pod.
373373
# terminationGracePeriodSeconds: 30
374374

375-
# -- Tolerations for the NGINX Gateway Fabric control plane pod.
375+
# -- Tolerations for the NGINX data plane pod.
376376
# tolerations: []
377377

378-
# -- The nodeSelector of the NGINX Gateway Fabric control plane pod.
378+
# -- The nodeSelector of the NGINX data plane pod.
379379
# nodeSelector: {}
380380

381-
# -- The affinity of the NGINX Gateway Fabric control plane pod.
381+
# -- The affinity of the NGINX data plane pod.
382382
# affinity: {}
383383

384-
# -- The topology spread constraints for the NGINX Gateway Fabric control plane pod.
384+
# -- The topology spread constraints for the NGINX data plane pod.
385385
# topologySpreadConstraints: []
386386

387-
# -- extraVolumes for the NGINX Gateway Fabric control plane pod. Use in conjunction with
387+
# -- extraVolumes for the NGINX data plane pod. Use in conjunction with
388388
# nginx.container.extraVolumeMounts mount additional volumes to the container.
389389
# extraVolumes: []
390390

internal/mode/static/handler_test.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,6 @@ var _ = Describe("eventHandler", func() {
249249
handler.HandleEventBatch(context.Background(), logr.Discard(), batch)
250250

251251
checkUpsertEventExpectations(e)
252-
Expect(fakeProvisioner.RegisterGatewayCallCount()).Should(Equal(0))
253-
Expect(fakeGenerator.GenerateCallCount()).Should(Equal(0))
254252
// status update should still occur for GatewayClasses
255253
Eventually(
256254
func() int {
@@ -305,7 +303,7 @@ var _ = Describe("eventHandler", func() {
305303
Eventually(
306304
func() int {
307305
return fakeStatusUpdater.UpdateGroupCallCount()
308-
}).Should(Equal(1))
306+
}).Should(BeNumerically(">", 1))
309307

310308
_, name, reqs := fakeStatusUpdater.UpdateGroupArgsForCall(0)
311309
Expect(name).To(Equal(groupControlPlane))
@@ -324,7 +322,7 @@ var _ = Describe("eventHandler", func() {
324322
Eventually(
325323
func() int {
326324
return fakeStatusUpdater.UpdateGroupCallCount()
327-
}).Should(Equal(1))
325+
}).Should(BeNumerically(">", 1))
328326

329327
_, name, reqs := fakeStatusUpdater.UpdateGroupArgsForCall(0)
330328
Expect(name).To(Equal(groupControlPlane))
@@ -356,7 +354,7 @@ var _ = Describe("eventHandler", func() {
356354
Eventually(
357355
func() int {
358356
return fakeStatusUpdater.UpdateGroupCallCount()
359-
}).Should(Equal(1))
357+
}).Should(BeNumerically(">", 1))
360358

361359
_, name, reqs := fakeStatusUpdater.UpdateGroupArgsForCall(0)
362360
Expect(name).To(Equal(groupControlPlane))

tests/Makefile

+4-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ build-test-runner-image: ## Build conformance test runner image
4444

4545
.PHONY: build-crossplane-image
4646
build-crossplane-image: ## Build the crossplane image
47-
docker build --build-arg NGINX_CONF_DIR=$(NGINX_CONF_DIR) -t nginx-crossplane:latest -f framework/crossplane/Dockerfile ..
47+
docker build --platform $(GOOS)/$(GOARCH) --build-arg NGINX_CONF_DIR=$(NGINX_CONF_DIR) -t nginx-crossplane:latest -f framework/crossplane/Dockerfile ..
4848

4949
.PHONY: run-conformance-tests
5050
run-conformance-tests: ## Run conformance tests
@@ -105,7 +105,9 @@ sync-files-to-vm: ## Syncs your local NGF files with the NGF repo on the VM
105105
./scripts/sync-files-to-vm.sh
106106

107107
.PHONY: nfr-test
108-
nfr-test: check-for-plus-usage-endpoint ## Run the NFR tests on a GCP VM
108+
nfr-test: GOARCH=amd64
109+
nfr-test: check-for-plus-usage-endpoint build-crossplane-image ## Run the NFR tests on a GCP VM
110+
./scripts/push-crossplane-image.sh
109111
CI=$(CI) ./scripts/run-tests-gcp-vm.sh
110112

111113
.PHONY: start-longevity-test

tests/framework/crossplane.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type ExpectedNginxField struct {
3838
ValueSubstringAllowed bool
3939
}
4040

41+
const crossplaneImageName = "nginx-crossplane:latest"
42+
4143
// ValidateNginxFieldExists accepts the nginx config and the configuration for the expected field,
4244
// and returns whether or not that field exists where it should.
4345
func ValidateNginxFieldExists(conf *Payload, expFieldCfg ExpectedNginxField) error {
@@ -144,11 +146,17 @@ func injectCrossplaneContainer(
144146
k8sClient kubernetes.Interface,
145147
timeout time.Duration,
146148
ngfPodName,
147-
namespace string,
149+
namespace,
150+
crossplaneImageRepo string,
148151
) error {
149152
ctx, cancel := context.WithTimeout(context.Background(), timeout)
150153
defer cancel()
151154

155+
image := crossplaneImageName
156+
if crossplaneImageRepo != "" {
157+
image = crossplaneImageRepo + "/" + image
158+
}
159+
152160
pod := &core.Pod{
153161
ObjectMeta: metav1.ObjectMeta{
154162
Name: ngfPodName,
@@ -160,8 +168,8 @@ func injectCrossplaneContainer(
160168
TargetContainerName: "nginx",
161169
EphemeralContainerCommon: core.EphemeralContainerCommon{
162170
Name: "crossplane",
163-
Image: "nginx-crossplane:latest",
164-
ImagePullPolicy: "Never",
171+
Image: image,
172+
ImagePullPolicy: "IfNotPresent",
165173
Stdin: true,
166174
VolumeMounts: []core.VolumeMount{
167175
{

tests/framework/info.go

+25
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"runtime/debug"
66

7+
. "github.com/onsi/ginkgo/v2"
78
core "k8s.io/api/core/v1"
89
"sigs.k8s.io/controller-runtime/pkg/client"
910
)
@@ -81,3 +82,27 @@ func GetBuildInfo() (commitHash string, commitTime string, dirtyBuild string) {
8182

8283
return
8384
}
85+
86+
// AddNginxLogsAndEventsToReport adds nginx logs and events from the namespace to the report if the spec failed.
87+
func AddNginxLogsAndEventsToReport(rm ResourceManager, namespace string) {
88+
if CurrentSpecReport().Failed() {
89+
var returnLogs string
90+
91+
nginxPodNames, _ := GetReadyNginxPodNames(rm.K8sClient, namespace, rm.TimeoutConfig.GetStatusTimeout)
92+
93+
for _, nginxPodName := range nginxPodNames {
94+
returnLogs += fmt.Sprintf("Logs for Nginx Pod %s:\n", nginxPodName)
95+
nginxLogs, _ := rm.GetPodLogs(
96+
namespace,
97+
nginxPodName,
98+
&core.PodLogOptions{Container: "nginx"},
99+
)
100+
101+
returnLogs += fmt.Sprintf(" %s\n", nginxLogs)
102+
}
103+
AddReportEntry("Nginx Logs", returnLogs, ReportEntryVisibilityNever)
104+
105+
events := GetEvents(rm, namespace)
106+
AddReportEntry("Test Events", events, ReportEntryVisibilityNever)
107+
}
108+
}

tests/framework/prometheus.go

-113
Original file line numberDiff line numberDiff line change
@@ -302,119 +302,6 @@ type Bucket struct {
302302
Val int
303303
}
304304

305-
// GetReloadCount gets the total number of nginx reloads.
306-
func GetReloadCount(promInstance PrometheusInstance, ngfPodName string) (float64, error) {
307-
return getFirstValueOfVector(
308-
fmt.Sprintf(
309-
`nginx_gateway_fabric_nginx_reloads_total{pod="%[1]s"}`,
310-
ngfPodName,
311-
),
312-
promInstance,
313-
)
314-
}
315-
316-
// GetReloadCountWithStartTime gets the total number of nginx reloads from a start time to the current time.
317-
func GetReloadCountWithStartTime(
318-
promInstance PrometheusInstance,
319-
ngfPodName string,
320-
startTime time.Time,
321-
) (float64, error) {
322-
return getFirstValueOfVector(
323-
fmt.Sprintf(
324-
`nginx_gateway_fabric_nginx_reloads_total{pod="%[1]s"}`+
325-
` - `+
326-
`nginx_gateway_fabric_nginx_reloads_total{pod="%[1]s"} @ %d`,
327-
ngfPodName,
328-
startTime.Unix(),
329-
),
330-
promInstance,
331-
)
332-
}
333-
334-
// GetReloadErrsCountWithStartTime gets the total number of nginx reload errors from a start time to the current time.
335-
func GetReloadErrsCountWithStartTime(
336-
promInstance PrometheusInstance,
337-
ngfPodName string,
338-
startTime time.Time,
339-
) (float64, error) {
340-
return getFirstValueOfVector(
341-
fmt.Sprintf(
342-
`nginx_gateway_fabric_nginx_reload_errors_total{pod="%[1]s"}`+
343-
` - `+
344-
`nginx_gateway_fabric_nginx_reload_errors_total{pod="%[1]s"} @ %d`,
345-
ngfPodName,
346-
startTime.Unix(),
347-
),
348-
promInstance,
349-
)
350-
}
351-
352-
// GetReloadAvgTime gets the average time in milliseconds for nginx to reload.
353-
func GetReloadAvgTime(promInstance PrometheusInstance, ngfPodName string) (float64, error) {
354-
return getFirstValueOfVector(
355-
fmt.Sprintf(
356-
`nginx_gateway_fabric_nginx_reloads_milliseconds_sum{pod="%[1]s"}`+
357-
` / `+
358-
`nginx_gateway_fabric_nginx_reloads_total{pod="%[1]s"}`,
359-
ngfPodName,
360-
),
361-
promInstance,
362-
)
363-
}
364-
365-
// GetReloadAvgTimeWithStartTime gets the average time in milliseconds for nginx to reload using a start time
366-
// to the current time to calculate.
367-
func GetReloadAvgTimeWithStartTime(
368-
promInstance PrometheusInstance,
369-
ngfPodName string,
370-
startTime time.Time,
371-
) (float64, error) {
372-
return getFirstValueOfVector(
373-
fmt.Sprintf(
374-
`(nginx_gateway_fabric_nginx_reloads_milliseconds_sum{pod="%[1]s"}`+
375-
` - `+
376-
`nginx_gateway_fabric_nginx_reloads_milliseconds_sum{pod="%[1]s"} @ %[2]d)`+
377-
` / `+
378-
`(nginx_gateway_fabric_nginx_reloads_total{pod="%[1]s"}`+
379-
` - `+
380-
`nginx_gateway_fabric_nginx_reloads_total{pod="%[1]s"} @ %[2]d)`,
381-
ngfPodName,
382-
startTime.Unix(),
383-
),
384-
promInstance,
385-
)
386-
}
387-
388-
// GetReloadBuckets gets the Buckets in millisecond intervals for nginx reloads.
389-
func GetReloadBuckets(promInstance PrometheusInstance, ngfPodName string) ([]Bucket, error) {
390-
return getBuckets(
391-
fmt.Sprintf(
392-
`nginx_gateway_fabric_nginx_reloads_milliseconds_bucket{pod="%[1]s"}`,
393-
ngfPodName,
394-
),
395-
promInstance,
396-
)
397-
}
398-
399-
// GetReloadBucketsWithStartTime gets the Buckets in millisecond intervals for nginx reloads from a start time
400-
// to the current time.
401-
func GetReloadBucketsWithStartTime(
402-
promInstance PrometheusInstance,
403-
ngfPodName string,
404-
startTime time.Time,
405-
) ([]Bucket, error) {
406-
return getBuckets(
407-
fmt.Sprintf(
408-
`nginx_gateway_fabric_nginx_reloads_milliseconds_bucket{pod="%[1]s"}`+
409-
` - `+
410-
`nginx_gateway_fabric_nginx_reloads_milliseconds_bucket{pod="%[1]s"} @ %d`,
411-
ngfPodName,
412-
startTime.Unix(),
413-
),
414-
promInstance,
415-
)
416-
}
417-
418305
// GetEventsCount gets the NGF event batch processing count.
419306
func GetEventsCount(promInstance PrometheusInstance, ngfPodName string) (float64, error) {
420307
return getFirstValueOfVector(

tests/framework/resourcemanager.go

+43-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import (
4646
"k8s.io/client-go/util/retry"
4747
"sigs.k8s.io/controller-runtime/pkg/client"
4848
v1 "sigs.k8s.io/gateway-api/apis/v1"
49+
50+
ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/apis/v1alpha2"
4951
)
5052

5153
// ResourceManager handles creating/updating/deleting Kubernetes resources.
@@ -647,6 +649,44 @@ func (rm *ResourceManager) GetNGFDeployment(namespace, releaseName string) (*app
647649
return &deployment, nil
648650
}
649651

652+
func (rm *ResourceManager) getGatewayClassNginxProxy(
653+
namespace,
654+
releaseName string,
655+
) (*ngfAPIv1alpha2.NginxProxy, error) {
656+
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.GetTimeout)
657+
defer cancel()
658+
659+
var proxy ngfAPIv1alpha2.NginxProxy
660+
proxyName := releaseName + "-proxy-config"
661+
662+
if err := rm.K8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: proxyName}, &proxy); err != nil {
663+
return nil, err
664+
}
665+
666+
return &proxy, nil
667+
}
668+
669+
// ScaleNginxDeployment scales the Nginx Deployment to the specified number of replicas.
670+
func (rm *ResourceManager) ScaleNginxDeployment(namespace, releaseName string, replicas int32) error {
671+
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.UpdateTimeout)
672+
defer cancel()
673+
674+
// If there is another NginxProxy which "overrides" the gateway class one, then this won't work and
675+
// may need refactoring.
676+
proxy, err := rm.getGatewayClassNginxProxy(namespace, releaseName)
677+
if err != nil {
678+
return fmt.Errorf("error getting NginxProxy: %w", err)
679+
}
680+
681+
proxy.Spec.Kubernetes.Deployment.Replicas = &replicas
682+
683+
if err = rm.K8sClient.Update(ctx, proxy); err != nil {
684+
return fmt.Errorf("error updating NginxProxy: %w", err)
685+
}
686+
687+
return nil
688+
}
689+
650690
// GetEvents returns all Events in the specified namespace.
651691
func (rm *ResourceManager) GetEvents(namespace string) (*core.EventList, error) {
652692
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.GetTimeout)
@@ -843,12 +883,14 @@ func (rm *ResourceManager) WaitForGatewayObservedGeneration(
843883
}
844884

845885
// GetNginxConfig uses crossplane to get the nginx configuration and convert it to JSON.
846-
func (rm *ResourceManager) GetNginxConfig(nginxPodName, namespace string) (*Payload, error) {
886+
// If the crossplane image is loaded locally on the node, crossplaneImageRepo can be empty.
887+
func (rm *ResourceManager) GetNginxConfig(nginxPodName, namespace, crossplaneImageRepo string) (*Payload, error) {
847888
if err := injectCrossplaneContainer(
848889
rm.ClientGoClient,
849890
rm.TimeoutConfig.UpdateTimeout,
850891
nginxPodName,
851892
namespace,
893+
crossplaneImageRepo,
852894
); err != nil {
853895
return nil, err
854896
}

tests/framework/timeout.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func DefaultTimeoutConfig() TimeoutConfig {
4343
CreateTimeout: 60 * time.Second,
4444
UpdateTimeout: 60 * time.Second,
4545
DeleteTimeout: 10 * time.Second,
46-
DeleteNamespaceTimeout: 60 * time.Second,
46+
DeleteNamespaceTimeout: 90 * time.Second,
4747
GetTimeout: 10 * time.Second,
4848
ManifestFetchTimeout: 10 * time.Second,
4949
RequestTimeout: 10 * time.Second,
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
3+
set -eo pipefail
4+
5+
source scripts/vars.env
6+
7+
docker tag nginx-crossplane:latest us-docker.pkg.dev/$GKE_PROJECT/nginx-gateway-fabric/nginx-crossplane:latest
8+
docker push us-docker.pkg.dev/$GKE_PROJECT/nginx-gateway-fabric/nginx-crossplane:latest

tests/suite/advanced_routing_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var _ = Describe("AdvancedRouting", Ordered, Label("functional", "routing"), fun
4848
})
4949

5050
AfterAll(func() {
51+
framework.AddNginxLogsAndEventsToReport(resourceManager, namespace)
5152
cleanUpPortForward()
5253

5354
Expect(resourceManager.DeleteFromFiles(files, namespace)).To(Succeed())

0 commit comments

Comments
 (0)