Skip to content

Commit 799ea76

Browse files
authored
Integrate telemetry exporter (nginx#1656)
Problem: Integrate the exporter library https://github.com/nginxinc/telemetry-exporter so that it is possible to send product telemetry data to an endpoint. Solution: - Integrate the exporter library. - Update existing telemetry data struct to use the common data struct defined in the exporter library. - Refactor existing telemetry data structs to adhere to the exporter library requirements. - Generate scheme and Attributes for data structs. - Allow configuring telemetry endpoint params via build flags. If the telemetry endpoint is not specified, NGF will log data points to the debug log (existing behavior). - Add root CA certs to NGF image so that NGF can verify the cert of the telemetry service. Testing: - Unit tests - Manual testing of build flags validation. - Manual testing that NGF sends data to an OTel collector. - Manual testing that NGF sends data to a dev F5 telemetry service. CLOSES -- nginx#1377
1 parent 2c8f750 commit 799ea76

25 files changed

+582
-126
lines changed

Diff for: .goreleaser.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ 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
18+
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.telemetryReportPeriod=24h -X main.telemetryEndpointInsecure=false
1919
main: ./cmd/gateway/
2020
binary: gateway
2121

Diff for: .pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ repos:
55
rev: v4.5.0
66
hooks:
77
- id: trailing-whitespace
8-
exclude: (^tests/results/)
8+
exclude: (^tests/results/|\.avdl$|_generated.go$)
99
- id: end-of-file-fixer
1010
- id: check-yaml
1111
args: [--allow-multiple-documents]

Diff for: Makefile

+8-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ NJS_DIR = internal/mode/static/nginx/modules/src
99
NGINX_DOCKER_BUILD_PLUS_ARGS = --secret id=nginx-repo.crt,src=nginx-repo.crt --secret id=nginx-repo.key,src=nginx-repo.key
1010
BUILD_AGENT=local
1111
TELEMETRY_REPORT_PERIOD = 24h # also configured in goreleaser.yml
12+
13+
# FIXME(pleshakov) - TELEMETRY_ENDPOINT will have the default value of F5 telemetry service once we're ready
14+
# to report. https://github.com/nginxinc/nginx-gateway-fabric/issues/1563
15+
# Also, we will need to set it in goreleaser.yml
16+
TELEMETRY_ENDPOINT =# if empty, NGF will report telemetry in its logs at debug level.
17+
18+
TELEMETRY_ENDPOINT_INSECURE = false # also configured in goreleaser.yml
1219
GW_API_VERSION = 1.0.0
1320
INSTALL_WEBHOOK = false
1421
NODE_VERSION = $(shell cat .nvmrc)
1522

1623
# go build flags - should not be overridden by the user
17-
GO_LINKER_FlAGS_VARS = -X main.version=${VERSION} -X main.commit=${GIT_COMMIT} -X main.date=${DATE} -X main.telemetryReportPeriod=${TELEMETRY_REPORT_PERIOD}
24+
GO_LINKER_FlAGS_VARS = -X main.version=${VERSION} -X main.commit=${GIT_COMMIT} -X main.date=${DATE} -X main.telemetryReportPeriod=${TELEMETRY_REPORT_PERIOD} -X main.telemetryEndpoint=${TELEMETRY_ENDPOINT} -X main.telemetryEndpointInsecure=${TELEMETRY_ENDPOINT_INSECURE}
1825
GO_LINKER_FLAGS_OPTIMIZATIONS = -s -w
1926
GO_LINKER_FLAGS = $(GO_LINKER_FLAGS_OPTIMIZATIONS) $(GO_LINKER_FlAGS_VARS)
2027

Diff for: build/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ COPY dist/gateway_linux_$TARGETARCH*/gateway /usr/bin/
2626
RUN setcap 'cap_kill=+ep' /usr/bin/gateway
2727

2828
FROM scratch as common
29+
# CA certs are needed for telemetry report and NGINX Plus usage report features, so that
30+
# NGF can verify the server's certificate.
31+
COPY --from=builder --link /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
2932
USER 102:1001
3033
ARG BUILD_AGENT
3134
ENV BUILD_AGENT=${BUILD_AGENT}

Diff for: cmd/gateway/commands.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"os"
7+
"strconv"
78
"time"
89

910
"github.com/spf13/cobra"
@@ -160,6 +161,17 @@ func createStaticModeCommand() *cobra.Command {
160161
return fmt.Errorf("error parsing telemetry report period: %w", err)
161162
}
162163

164+
if telemetryEndpoint != "" {
165+
if err := validateEndpoint(telemetryEndpoint); err != nil {
166+
return fmt.Errorf("error validating telemetry endpoint: %w", err)
167+
}
168+
}
169+
170+
telemetryEndpointInsecure, err := strconv.ParseBool(telemetryEndpointInsecure)
171+
if err != nil {
172+
return fmt.Errorf("error parsing telemetry endpoint insecure: %w", err)
173+
}
174+
163175
var gwNsName *types.NamespacedName
164176
if cmd.Flags().Changed(gatewayFlag) {
165177
gwNsName = &gateway.value
@@ -211,8 +223,10 @@ func createStaticModeCommand() *cobra.Command {
211223
},
212224
UsageReportConfig: usageReportConfig,
213225
ProductTelemetryConfig: config.ProductTelemetryConfig{
214-
TelemetryReportPeriod: period,
215-
Enabled: !disableProductTelemetry,
226+
ReportPeriod: period,
227+
Enabled: !disableProductTelemetry,
228+
Endpoint: telemetryEndpoint,
229+
EndpointInsecure: telemetryEndpointInsecure,
216230
},
217231
Plus: plus,
218232
Version: version,

Diff for: cmd/gateway/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ var (
1313

1414
// telemetryReportPeriod is the period at which telemetry reports are sent.
1515
telemetryReportPeriod string
16+
// telemetryEndpoint is the endpoint to which telemetry reports are sent.
17+
telemetryEndpoint string
18+
// telemetryEndpointInsecure controls whether TLS should be used when sending telemetry reports.
19+
telemetryEndpointInsecure string
1620
)
1721

1822
func main() {

Diff for: cmd/gateway/validation.go

+30
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net"
77
"net/url"
88
"regexp"
9+
"strconv"
910
"strings"
1011

1112
"k8s.io/apimachinery/pkg/types"
@@ -133,6 +134,35 @@ func validateIP(ip string) error {
133134
return nil
134135
}
135136

137+
// validateEndpoint validates an endpoint, which is <host>:<port> where host is either a hostname or an IP address.
138+
func validateEndpoint(endpoint string) error {
139+
host, port, err := net.SplitHostPort(endpoint)
140+
if err != nil {
141+
return fmt.Errorf("%q must be in the format <host>:<port>: %w", endpoint, err)
142+
}
143+
144+
portVal, err := strconv.ParseInt(port, 10, 16)
145+
if err != nil {
146+
return fmt.Errorf("port must be a valid number: %w", err)
147+
}
148+
149+
if portVal < 1 || portVal > 65535 {
150+
return fmt.Errorf("port outside of valid port range [1 - 65535]: %v", port)
151+
}
152+
153+
if err := validateIP(host); err == nil {
154+
return nil
155+
}
156+
157+
if errs := validation.IsDNS1123Subdomain(host); len(errs) == 0 {
158+
return nil
159+
}
160+
161+
// we don't know if the user intended to use a hostname or an IP address,
162+
// so we return a generic error message
163+
return fmt.Errorf("%q must be in the format <host>:<port>", endpoint)
164+
}
165+
136166
// validatePort makes sure a given port is inside the valid port range for its usage
137167
func validatePort(port int) error {
138168
if port < 1024 || port > 65535 {

Diff for: cmd/gateway/validation_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,73 @@ func TestValidateIP(t *testing.T) {
419419
}
420420
}
421421

422+
func TestValidateEndpoint(t *testing.T) {
423+
tests := []struct {
424+
name string
425+
endp string
426+
expErr bool
427+
}{
428+
{
429+
name: "valid endpoint with hostname",
430+
endp: "localhost:8080",
431+
expErr: false,
432+
},
433+
{
434+
name: "valid endpoint with IPv4",
435+
endp: "1.2.3.4:8080",
436+
expErr: false,
437+
},
438+
{
439+
name: "valid endpoint with IPv6",
440+
endp: "[::1]:8080",
441+
expErr: false,
442+
},
443+
{
444+
name: "invalid port - 1",
445+
endp: "localhost:0",
446+
expErr: true,
447+
},
448+
{
449+
name: "invalid port - 2",
450+
endp: "localhost:65536",
451+
expErr: true,
452+
},
453+
{
454+
name: "missing port with hostname",
455+
endp: "localhost",
456+
expErr: true,
457+
},
458+
{
459+
name: "missing port with IPv4",
460+
endp: "1.2.3.4",
461+
expErr: true,
462+
},
463+
{
464+
name: "missing port with IPv6",
465+
endp: "[::1]",
466+
expErr: true,
467+
},
468+
{
469+
name: "invalid hostname or IP",
470+
endp: "loc@lhost:8080",
471+
expErr: true,
472+
},
473+
}
474+
475+
for _, tc := range tests {
476+
t.Run(tc.name, func(t *testing.T) {
477+
g := NewWithT(t)
478+
479+
err := validateEndpoint(tc.endp)
480+
if !tc.expErr {
481+
g.Expect(err).ToNot(HaveOccurred())
482+
} else {
483+
g.Expect(err).To(HaveOccurred())
484+
}
485+
})
486+
}
487+
}
488+
422489
func TestValidatePort(t *testing.T) {
423490
tests := []struct {
424491
name string

Diff for: docs/developer/implementing-a-feature.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ practices to ensure a successful feature development process.
5959
different reviewer in mind, you can request them as well. Refer to
6060
the [pull request](/docs/developer/pull-request.md) documentation for expectations and guidelines.
6161
14. **Obtain the necessary approvals**: Work with code reviewers to maintain the required number of approvals.
62-
15. **Squash and merge**: Squash your commits locally, or use the GitHub UI to squash and merge. Only one commit per
62+
15. **Ensure the product telemetry works**. If you made any changes to the product telemetry data points, it is
63+
necessary to push the generated scheme (`.avdl`, generated in Step 12) to the scheme registry. After that, manually
64+
verify that the product telemetry data is successfully pushed to the telemetry service by confirming that the data
65+
has been received.
66+
16. **Squash and merge**: Squash your commits locally, or use the GitHub UI to squash and merge. Only one commit per
6367
pull request should be merged. Make sure the first line of the final commit message includes the pull request
6468
number. For example, Fix supported gateway conditions in compatibility doc (#674).
6569
> **Note**:

Diff for: docs/developer/quickstart.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,16 @@ Run the following make command from the project's root directory to lint the Hel
214214
make lint-helm
215215
```
216216
217-
## Run go generate
217+
## Run Code Generation
218218
219219
To ensure all the generated code is up to date, run the following make command from the project's root directory:
220220

221221
```shell
222222
make generate
223223
```
224224

225+
That command also will generate the avro scheme (`.avdl`) for product telemetry data points.
226+
225227
## Update Generated Manifests
226228

227229
To update the generated manifests, run the following make command from the project's root directory:

Diff for: go.mod

+17-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ require (
1212
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1
1313
github.com/nginxinc/nginx-plus-go-client v1.2.0
1414
github.com/nginxinc/nginx-prometheus-exporter v1.1.0
15+
github.com/nginxinc/telemetry-exporter v0.0.0-20240307135433-a5ecce59bddf
1516
github.com/onsi/ginkgo/v2 v2.16.0
1617
github.com/onsi/gomega v1.31.1
1718
github.com/prometheus/client_golang v1.19.0
1819
github.com/prometheus/common v0.50.0
1920
github.com/spf13/cobra v1.8.0
2021
github.com/spf13/pflag v1.0.5
2122
github.com/tsenart/vegeta/v12 v12.11.1
23+
go.opentelemetry.io/otel v1.24.0
24+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
2225
go.uber.org/zap v1.27.0
2326
k8s.io/api v0.29.2
2427
k8s.io/apiextensions-apiserver v0.29.2
@@ -32,6 +35,7 @@ require (
3235

3336
require (
3437
github.com/beorn7/perks v1.0.1 // indirect
38+
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
3539
github.com/cespare/xxhash/v2 v2.2.0 // indirect
3640
github.com/davecgh/go-spew v1.1.1 // indirect
3741
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
@@ -40,6 +44,7 @@ require (
4044
github.com/fatih/color v1.16.0 // indirect
4145
github.com/fsnotify/fsnotify v1.7.0 // indirect
4246
github.com/go-logfmt/logfmt v0.5.1 // indirect
47+
github.com/go-logr/stdr v1.2.2 // indirect
4348
github.com/go-logr/zapr v1.3.0 // indirect
4449
github.com/go-openapi/jsonpointer v0.20.0 // indirect
4550
github.com/go-openapi/jsonreference v0.20.2 // indirect
@@ -52,8 +57,9 @@ require (
5257
github.com/google/gnostic-models v0.6.8 // indirect
5358
github.com/google/gofuzz v1.2.0 // indirect
5459
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
55-
github.com/google/uuid v1.3.1 // indirect
60+
github.com/google/uuid v1.6.0 // indirect
5661
github.com/gorilla/websocket v1.5.0 // indirect
62+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
5763
github.com/imdario/mergo v0.3.16 // indirect
5864
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5965
github.com/influxdata/tdigest v0.0.1 // indirect
@@ -73,19 +79,27 @@ require (
7379
github.com/prometheus/procfs v0.12.0 // indirect
7480
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
7581
github.com/stretchr/testify v1.8.4 // indirect
82+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
83+
go.opentelemetry.io/otel/metric v1.24.0 // indirect
84+
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
85+
go.opentelemetry.io/otel/trace v1.24.0 // indirect
86+
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
7687
go.uber.org/multierr v1.11.0 // indirect
7788
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
78-
golang.org/x/mod v0.14.0 // indirect
89+
golang.org/x/mod v0.16.0 // indirect
7990
golang.org/x/net v0.22.0 // indirect
8091
golang.org/x/oauth2 v0.18.0 // indirect
8192
golang.org/x/sync v0.6.0 // indirect
8293
golang.org/x/sys v0.18.0 // indirect
8394
golang.org/x/term v0.18.0 // indirect
8495
golang.org/x/text v0.14.0 // indirect
8596
golang.org/x/time v0.3.0 // indirect
86-
golang.org/x/tools v0.17.0 // indirect
97+
golang.org/x/tools v0.19.0 // indirect
8798
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
8899
google.golang.org/appengine v1.6.8 // indirect
100+
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
101+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
102+
google.golang.org/grpc v1.61.1 // indirect
89103
google.golang.org/protobuf v1.33.0 // indirect
90104
gopkg.in/inf.v0 v0.9.1 // indirect
91105
gopkg.in/yaml.v2 v2.4.0 // indirect

0 commit comments

Comments
 (0)