Skip to content

Commit f059cf0

Browse files
pleshakovsjberman
andauthored
Add functional test for product telemetry (#1659)
* Add functional test for product telemetry Problem: Ensure product telemetry feature is tested with a functional test Solution: - Add a functional test. - Because it requires a NGF with a custom built, it needs to run with telemetry label. - Enable telemetry test in the pipeline Testing: Ran successfully: - make test TAG=$(whoami) GINKGO_LABEL=telemetry - make test TAG=$(whoami) # telemetry test didn't run as expected, the functional test succeeded Also, the pipeline runs telemetry tests successfully ClOSES - #1640 Co-authored-by: Saylor Berman <s.berman@f5.com>
1 parent e1d7691 commit f059cf0

File tree

10 files changed

+357
-29
lines changed

10 files changed

+357
-29
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ jobs:
151151
AZURE_STORAGE_KEY: ${{ secrets.AZURE_STORAGE_KEY }}
152152
AZURE_BUCKET_NAME: ${{ secrets.AZURE_BUCKET_NAME }}
153153
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_COMMUNITY }}
154+
TELEMETRY_ENDPOINT: "" # disables sending telemetry
155+
TELEMETRY_ENDPOINT_INSECURE: "false"
154156

155157
- name: Cache Artifacts
156158
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1

.github/workflows/conformance.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ jobs:
8484
with:
8585
version: latest
8686
args: build --snapshot --clean
87+
env:
88+
TELEMETRY_ENDPOINT: "" # disables sending telemetry
89+
TELEMETRY_ENDPOINT_INSECURE: "false"
8790

8891
- name: Build NGF Docker Image
8992
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # v5.2.0

.github/workflows/functional.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ jobs:
7373
with:
7474
version: latest
7575
args: build --snapshot --clean
76+
env:
77+
TELEMETRY_ENDPOINT: otel-collector-opentelemetry-collector.collector.svc.cluster.local:4317
78+
TELEMETRY_ENDPOINT_INSECURE: "true"
7679

7780
- name: Build NGF Docker Image
7881
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
@@ -116,9 +119,9 @@ jobs:
116119
make load-images${{ matrix.nginx-image == 'nginx-plus' && '-with-plus' || ''}} PREFIX=${ngf_prefix} TAG=${ngf_tag}
117120
working-directory: ./tests
118121

119-
- name: Run functional tests
122+
- name: Run functional telemetry tests
120123
run: |
121124
ngf_prefix=ghcr.io/nginxinc/nginx-gateway-fabric
122125
ngf_tag=${{ steps.ngf-meta.outputs.version }}
123-
make test${{ matrix.nginx-image == 'nginx-plus' && '-with-plus' || ''}} PREFIX=${ngf_prefix} TAG=${ngf_tag}
126+
make test${{ matrix.nginx-image == 'nginx-plus' && '-with-plus' || ''}} PREFIX=${ngf_prefix} TAG=${ngf_tag} GINKGO_LABEL=telemetry
124127
working-directory: ./tests

.goreleaser.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ builds:
1515
asmflags:
1616
- all=-trimpath={{.Env.GOPATH}}
1717
ldflags:
18-
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.telemetryReportPeriod=24h -X main.telemetryEndpointInsecure=false
18+
- -s -w
19+
- -X main.version={{.Version}}
20+
- -X main.commit={{.Commit}}
21+
- -X main.date={{.Date}}
22+
- -X main.telemetryReportPeriod=24h
23+
- -X main.telemetryEndpoint={{.Env.TELEMETRY_ENDPOINT}}
24+
- -X main.telemetryEndpointInsecure={{.Env.TELEMETRY_ENDPOINT_INSECURE}}
1925
main: ./cmd/gateway/
2026
binary: gateway
2127

tests/Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ GINKGO_LABEL=
1313
GINKGO_FLAGS=
1414
NGF_VERSION=
1515
CI=false
16+
TELEMETRY_ENDPOINT=
17+
TELEMETRY_ENDPOINT_INSECURE=
1618

1719
ifneq ($(GINKGO_LABEL),)
1820
override GINKGO_FLAGS += -ginkgo.label-filter "$(GINKGO_LABEL)"
@@ -38,11 +40,11 @@ delete-kind-cluster: ## Delete kind cluster
3840

3941
.PHONY: build-images
4042
build-images: ## Build NGF and NGINX images
41-
cd .. && make PREFIX=$(PREFIX) TAG=$(TAG) build-images
43+
cd .. && make PREFIX=$(PREFIX) TAG=$(TAG) TELEMETRY_ENDPOINT=$(TELEMETRY_ENDPOINT) TELEMETRY_ENDPOINT_INSECURE=$(TELEMETRY_ENDPOINT_INSECURE) build-images
4244

4345
.PHONY: build-images-with-plus
4446
build-images-with-plus: ## Build NGF and NGINX Plus images
45-
cd .. && make PREFIX=$(PREFIX) TAG=$(TAG) build-images-with-plus
47+
cd .. && make PREFIX=$(PREFIX) TAG=$(TAG) TELEMETRY_ENDPOINT=$(TELEMETRY_ENDPOINT) TELEMETRY_ENDPOINT_INSECURE=$(TELEMETRY_ENDPOINT_INSECURE) build-images-with-plus
4648

4749
.PHONY: load-images
4850
load-images: ## Load NGF and NGINX images on configured kind cluster

tests/README.md

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,23 @@ test-with-plus Runs the functional tests for NGF with NGINX Plus
6868

6969
**Note:** The following variables are configurable when running the below `make` commands:
7070

71-
| Variable | Default | Description |
72-
| ------------------- | ------------------------------- | -------------------------------------------------------------- |
73-
| TAG | edge | tag for the locally built NGF images |
74-
| PREFIX | nginx-gateway-fabric | prefix for the locally built NGF image |
75-
| NGINX_PREFIX | nginx-gateway-fabric/nginx | prefix for the locally built NGINX image |
76-
| NGINX_PLUS_PREFIX | nginx-gateway-fabric/nginx-plus | prefix for the locally built NGINX Plus image |
77-
| PLUS_ENABLED | false | Flag to indicate if NGINX Plus should be enabled |
78-
| PULL_POLICY | Never | NGF image pull policy |
79-
| GW_API_VERSION | 1.0.0 | version of Gateway API resources to install |
80-
| K8S_VERSION | latest | version of k8s that the tests are run on |
81-
| GW_SERVICE_TYPE | NodePort | type of Service that should be created |
82-
| GW_SVC_GKE_INTERNAL | false | specifies if the LoadBalancer should be a GKE internal service |
83-
| GINKGO_LABEL | "" | name of the ginkgo label that will filter the tests to run |
84-
| GINKGO_FLAGS | "" | other ginkgo flags to pass to the go test command |
71+
| Variable | Default | Description |
72+
|------------------------------|---------------------------------|---------------------------------------------------------------------|
73+
| TAG | edge | tag for the locally built NGF images |
74+
| PREFIX | nginx-gateway-fabric | prefix for the locally built NGF image |
75+
| NGINX_PREFIX | nginx-gateway-fabric/nginx | prefix for the locally built NGINX image |
76+
| NGINX_PLUS_PREFIX | nginx-gateway-fabric/nginx-plus | prefix for the locally built NGINX Plus image |
77+
| PLUS_ENABLED | false | Flag to indicate if NGINX Plus should be enabled |
78+
| PULL_POLICY | Never | NGF image pull policy |
79+
| GW_API_VERSION | 1.0.0 | version of Gateway API resources to install |
80+
| K8S_VERSION | latest | version of k8s that the tests are run on |
81+
| GW_SERVICE_TYPE | NodePort | type of Service that should be created |
82+
| GW_SVC_GKE_INTERNAL | false | specifies if the LoadBalancer should be a GKE internal service |
83+
| GINKGO_LABEL | "" | name of the ginkgo label that will filter the tests to run |
84+
| GINKGO_FLAGS | "" | other ginkgo flags to pass to the go test command |
85+
| TELEMETRY_ENDPOINT | Set in the main Makefile | The endpoint to which telemetry reports are sent |
86+
| TELEMETRY_ENDPOINT_INSECURE= | Set in the main Makefile | Controls whether TLS should be used when sending telemetry reports. |
87+
8588

8689
## Step 1 - Create a Kubernetes cluster
8790

@@ -137,6 +140,12 @@ Or, to build NGF with NGINX Plus enabled (NGINX Plus cert and key must exist in
137140
make build-images-with-plus load-images-with-plus TAG=$(whoami)
138141
```
139142

143+
For the telemetry test, which requires a OTel collector, build an image with the following variables set:
144+
145+
```makefile
146+
TELEMETRY_ENDPOINT=otel-collector-opentelemetry-collector.collector.svc.cluster.local:4317 TELEMETRY_ENDPOINT_INSECURE=true
147+
```
148+
140149
## Step 3 - Run the tests
141150

142151
### 3a - Run the functional tests locally
@@ -151,6 +160,15 @@ Or, to run the tests with NGINX Plus enabled:
151160
make test TAG=$(whoami) PLUS_ENABLED=true
152161
```
153162

163+
> The command above doesn't run the telemetry functional test, which requires a dedicated invocation because it uses a
164+
> specially built image (see above) and it needs to deploy NGF differently from the rest of functional tests.
165+
166+
To run the telemetry test:
167+
168+
```makefile
169+
make test TAG=$(whoami) GINKGO_LABEL=telemetry
170+
```
171+
154172
### 3b - Run the tests on a GKE cluster from a GCP VM
155173

156174
This step only applies if you are running the NFR tests, or would like to run the functional tests on a GKE cluster from a GCP based VM.

tests/framework/resourcemanager.go

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,32 @@ import (
3030
"strings"
3131
"time"
3232

33+
apps "k8s.io/api/apps/v1"
3334
core "k8s.io/api/core/v1"
3435
apierrors "k8s.io/apimachinery/pkg/api/errors"
3536
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3637
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3738
"k8s.io/apimachinery/pkg/types"
3839
"k8s.io/apimachinery/pkg/util/wait"
3940
"k8s.io/apimachinery/pkg/util/yaml"
41+
"k8s.io/client-go/kubernetes"
4042
"sigs.k8s.io/controller-runtime/pkg/client"
4143
v1 "sigs.k8s.io/gateway-api/apis/v1"
4244
)
4345

4446
// ResourceManager handles creating/updating/deleting Kubernetes resources.
4547
type ResourceManager struct {
46-
K8sClient client.Client
47-
FS embed.FS
48-
TimeoutConfig TimeoutConfig
48+
K8sClient client.Client
49+
ClientGoClient kubernetes.Interface // used when k8sClient is not enough
50+
FS embed.FS
51+
TimeoutConfig TimeoutConfig
4952
}
5053

5154
// ClusterInfo holds the cluster metadata
5255
type ClusterInfo struct {
53-
K8sVersion string
56+
K8sVersion string
57+
// ID is the UID of kube-system namespace
58+
ID string
5459
MemoryPerNode string
5560
GkeInstanceType string
5661
GkeZone string
@@ -406,9 +411,89 @@ func (rm *ResourceManager) GetClusterInfo() (ClusterInfo, error) {
406411
ci.GkeZone = node.Labels["topology.kubernetes.io/zone"]
407412
}
408413

414+
var ns core.Namespace
415+
key := types.NamespacedName{Name: "kube-system"}
416+
417+
if err := rm.K8sClient.Get(ctx, key, &ns); err != nil {
418+
return *ci, fmt.Errorf("error getting kube-system namespace: %w", err)
419+
}
420+
421+
ci.ID = string(ns.UID)
422+
409423
return *ci, nil
410424
}
411425

426+
// GetPodNames returns the names of all Pods in the specified namespace that match the given labels.
427+
func (rm *ResourceManager) GetPodNames(namespace string, labels client.MatchingLabels) ([]string, error) {
428+
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.GetTimeout)
429+
defer cancel()
430+
431+
var podList core.PodList
432+
if err := rm.K8sClient.List(
433+
ctx,
434+
&podList,
435+
client.InNamespace(namespace),
436+
labels,
437+
); err != nil {
438+
return nil, fmt.Errorf("error getting list of Pods: %w", err)
439+
}
440+
441+
names := make([]string, 0, len(podList.Items))
442+
443+
for _, pod := range podList.Items {
444+
names = append(names, pod.Name)
445+
}
446+
447+
return names, nil
448+
}
449+
450+
// GetPodLogs returns the logs from the specified Pod
451+
func (rm *ResourceManager) GetPodLogs(namespace, name string, opts *core.PodLogOptions) (string, error) {
452+
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.GetTimeout)
453+
defer cancel()
454+
455+
req := rm.ClientGoClient.CoreV1().Pods(namespace).GetLogs(name, opts)
456+
457+
logs, err := req.Stream(ctx)
458+
if err != nil {
459+
return "", fmt.Errorf("error getting logs from Pod: %w", err)
460+
}
461+
defer logs.Close()
462+
463+
buf := new(bytes.Buffer)
464+
if _, err := buf.ReadFrom(logs); err != nil {
465+
return "", fmt.Errorf("error reading logs from Pod: %w", err)
466+
}
467+
468+
return buf.String(), nil
469+
}
470+
471+
// GetNGFDeployment returns the NGF Deployment in the specified namespace with the given release name.
472+
func (rm *ResourceManager) GetNGFDeployment(namespace, releaseName string) (*apps.Deployment, error) {
473+
ctx, cancel := context.WithTimeout(context.Background(), rm.TimeoutConfig.GetTimeout)
474+
defer cancel()
475+
476+
var deployments apps.DeploymentList
477+
478+
if err := rm.K8sClient.List(
479+
ctx,
480+
&deployments,
481+
client.InNamespace(namespace),
482+
client.MatchingLabels{
483+
"app.kubernetes.io/instance": releaseName,
484+
},
485+
); err != nil {
486+
return nil, fmt.Errorf("error getting list of Deployments: %w", err)
487+
}
488+
489+
if len(deployments.Items) != 1 {
490+
return nil, fmt.Errorf("expected 1 NGF Deployment, got %d", len(deployments.Items))
491+
}
492+
493+
deployment := deployments.Items[0]
494+
return &deployment, nil
495+
}
496+
412497
// GetReadyNGFPodNames returns the name(s) of the NGF Pod(s).
413498
func GetReadyNGFPodNames(
414499
k8sClient client.Client,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
mode: deployment
2+
replicaCount: 1
3+
config:
4+
exporters:
5+
debug:
6+
verbosity: detailed
7+
logging: {}
8+
extensions:
9+
health_check: {}
10+
memory_ballast:
11+
size_in_percentage: 40
12+
processors:
13+
batch: {}
14+
memory_limiter:
15+
check_interval: 5s
16+
limit_percentage: 80
17+
spike_limit_percentage: 25
18+
receivers:
19+
otlp:
20+
protocols:
21+
grpc:
22+
endpoint: 0.0.0.0:4317
23+
service:
24+
extensions:
25+
- health_check
26+
pipelines:
27+
traces:
28+
exporters:
29+
- debug
30+
receivers:
31+
- otlp

tests/suite/system_suite_test.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
k8sRuntime "k8s.io/apimachinery/pkg/runtime"
2222
"k8s.io/apimachinery/pkg/types"
2323
"k8s.io/apimachinery/pkg/util/wait"
24+
"k8s.io/client-go/kubernetes"
2425
ctlr "sigs.k8s.io/controller-runtime"
2526
"sigs.k8s.io/controller-runtime/pkg/client"
2627
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -103,11 +104,15 @@ func setup(cfg setupConfig, extraInstallArgs ...string) {
103104
k8sClient, err = client.New(k8sConfig, options)
104105
Expect(err).ToNot(HaveOccurred())
105106

107+
clientGoClient, err := kubernetes.NewForConfig(k8sConfig)
108+
Expect(err).ToNot(HaveOccurred())
109+
106110
timeoutConfig = framework.DefaultTimeoutConfig()
107111
resourceManager = framework.ResourceManager{
108-
K8sClient: k8sClient,
109-
FS: manifests,
110-
TimeoutConfig: timeoutConfig,
112+
K8sClient: k8sClient,
113+
ClientGoClient: clientGoClient,
114+
FS: manifests,
115+
TimeoutConfig: timeoutConfig,
111116
}
112117

113118
clusterInfo, err = resourceManager.GetClusterInfo()
@@ -210,26 +215,33 @@ func teardown(relName string) {
210215
)).To(Succeed())
211216
}
212217

213-
var _ = BeforeSuite(func() {
218+
func getDefaultSetupCfg() setupConfig {
214219
_, file, _, _ := runtime.Caller(0)
215220
fileDir := path.Join(path.Dir(file), "../")
216221
basepath := filepath.Dir(fileDir)
217222
localChartPath = filepath.Join(basepath, "deploy/helm-chart")
218223

219-
cfg := setupConfig{
224+
return setupConfig{
220225
releaseName: releaseName,
221226
chartPath: localChartPath,
222227
gwAPIVersion: *gatewayAPIVersion,
223228
deploy: true,
224229
}
230+
}
231+
232+
var _ = BeforeSuite(func() {
233+
cfg := getDefaultSetupCfg()
225234

226235
labelFilter := GinkgoLabelFilter()
227236
cfg.nfr = isNFR(labelFilter)
228237

229238
// Skip deployment if:
230239
// - running upgrade test (this test will deploy its own version)
231240
// - running longevity teardown (deployment will already exist)
232-
if strings.Contains(labelFilter, "upgrade") || strings.Contains(labelFilter, "longevity-teardown") {
241+
// - running telemetry test (NGF will be deployed as part of the test)
242+
if strings.Contains(labelFilter, "upgrade") ||
243+
strings.Contains(labelFilter, "longevity-teardown") ||
244+
strings.Contains(labelFilter, "telemetry") {
233245
cfg.deploy = false
234246
}
235247

0 commit comments

Comments
 (0)