Skip to content

Commit 8bd27a3

Browse files
authored
NGINX Plus: support enforce_initial_report field (#3899)
NGINX Plus: support enforce_initial_report field in CLI and HELM
1 parent 84a517f commit 8bd27a3

File tree

13 files changed

+190
-66
lines changed

13 files changed

+190
-66
lines changed

charts/nginx-gateway-fabric/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
207207
| `certGenerator.ttlSecondsAfterFinished` | How long to wait after the cert generator job has finished before it is removed by the job controller. | int | `30` |
208208
| `clusterDomain` | The DNS cluster domain of your Kubernetes cluster. | string | `"cluster.local"` |
209209
| `gateways` | A list of Gateway objects. View https://gateway-api.sigs.k8s.io/reference/spec/#gateway for full Gateway reference. | list | `[]` |
210-
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"autoscaling":{"enable":false},"config":{},"container":{"hostPorts":[],"lifecycle":{},"readinessProbe":{},"resources":{},"volumeMounts":[]},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","nginxOneConsole":{"dataplaneKeySecretName":"","endpointHost":"agent.connect.nginx.com","endpointPort":443,"skipVerify":false},"patches":[],"plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"patches":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","resolver":"","secretName":"nplus-license","skipVerify":false}}` |
210+
| `nginx` | The nginx section contains the configuration for all NGINX data plane deployments installed by the NGINX Gateway Fabric control plane. | object | `{"autoscaling":{"enable":false},"config":{},"container":{"hostPorts":[],"lifecycle":{},"readinessProbe":{},"resources":{},"volumeMounts":[]},"debug":false,"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric/nginx","tag":"edge"},"imagePullSecret":"","imagePullSecrets":[],"kind":"deployment","nginxOneConsole":{"dataplaneKeySecretName":"","endpointHost":"agent.connect.nginx.com","endpointPort":443,"skipVerify":false},"patches":[],"plus":false,"pod":{},"replicas":1,"service":{"externalTrafficPolicy":"Local","loadBalancerClass":"","loadBalancerIP":"","loadBalancerSourceRanges":[],"nodePorts":[],"patches":[],"type":"LoadBalancer"},"usage":{"caSecretName":"","clientSSLSecretName":"","endpoint":"","enforceInitialReport":true,"resolver":"","secretName":"nplus-license","skipVerify":false}}` |
211211
| `nginx.autoscaling` | Autoscaling configuration for the NGINX data plane. | object | `{"enable":false}` |
212212
| `nginx.autoscaling.enable` | Enable or disable Horizontal Pod Autoscaler for the NGINX data plane. | bool | `false` |
213213
| `nginx.config` | The configuration for the data plane that is contained in the NginxProxy resource. This is applied globally to all Gateways managed by this instance of NGINX Gateway Fabric. | object | `{}` |
@@ -241,6 +241,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
241241
| `nginx.usage.caSecretName` | The name of the Secret containing the NGINX Instance Manager CA certificate. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | string | `""` |
242242
| `nginx.usage.clientSSLSecretName` | The name of the Secret containing the client certificate and key for authenticating with NGINX Instance Manager. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | string | `""` |
243243
| `nginx.usage.endpoint` | The endpoint of the NGINX Plus usage reporting server. Default: product.connect.nginx.com | string | `""` |
244+
| `nginx.usage.enforceInitialReport` | Enable enforcement of the initial NGINX Plus licensing report. If set to false, the initial report is not enforced. | bool | `true` |
244245
| `nginx.usage.resolver` | The nameserver used to resolve the NGINX Plus usage reporting endpoint. Used with NGINX Instance Manager. | string | `""` |
245246
| `nginx.usage.secretName` | The name of the Secret containing the JWT for NGINX Plus usage reporting. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | string | `"nplus-license"` |
246247
| `nginx.usage.skipVerify` | Disable client verification of the NGINX Plus usage reporting server certificate. | bool | `false` |

charts/nginx-gateway-fabric/templates/deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ spec:
7272
{{- if .Values.nginx.usage.clientSSLSecretName }}
7373
- --usage-report-client-ssl-secret={{ .Values.nginx.usage.clientSSLSecretName }}
7474
{{- end }}
75+
{{- if hasKey .Values.nginx.usage "enforceInitialReport" }}
76+
- --usage-report-enforce-initial-report={{ .Values.nginx.usage.enforceInitialReport }}
77+
{{- end }}
7578
{{- end }}
7679
{{- if .Values.nginxGateway.metrics.enable }}
7780
- --metrics-port={{ .Values.nginxGateway.metrics.port }}

charts/nginx-gateway-fabric/values.schema.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,13 @@
692692
"title": "endpoint",
693693
"type": "string"
694694
},
695+
"enforceInitialReport": {
696+
"default": true,
697+
"description": "Enable enforcement of the initial NGINX Plus licensing report. If set to false, the initial report is not enforced.",
698+
"required": [],
699+
"title": "enforceInitialReport",
700+
"type": "boolean"
701+
},
695702
"resolver": {
696703
"default": "",
697704
"description": "The nameserver used to resolve the NGINX Plus usage reporting endpoint. Used with NGINX Instance Manager.",

charts/nginx-gateway-fabric/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@ nginx:
337337
# Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway).
338338
clientSSLSecretName: ""
339339

340+
# -- Enable enforcement of the initial NGINX Plus licensing report. If set to false, the initial report is not enforced.
341+
enforceInitialReport: true
342+
340343
# @schema
341344
# type: object
342345
# properties:

cmd/gateway/commands.go

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ const (
4242
nginxOneTelemetryEndpointHost = "agent.connect.nginx.com"
4343
)
4444

45+
// usageReportParams holds the parameters for building the usage report configuration for PLUS.
46+
type usageReportParams struct {
47+
SecretName stringValidatingValue
48+
ClientSSLSecretName stringValidatingValue
49+
CASecretName stringValidatingValue
50+
Endpoint stringValidatingValue
51+
Resolver stringValidatingValue
52+
SkipVerify bool
53+
EnforceInitialReport bool
54+
}
55+
4556
func createRootCommand() *cobra.Command {
4657
rootCmd := &cobra.Command{
4758
Use: "gateway",
@@ -58,31 +69,32 @@ func createRootCommand() *cobra.Command {
5869
func createControllerCommand() *cobra.Command {
5970
// flag names
6071
const (
61-
configFlag = "config"
62-
serviceFlag = "service"
63-
agentTLSSecretFlag = "agent-tls-secret"
64-
nginxOneDataplaneKeySecretFlag = "nginx-one-dataplane-key-secret" //nolint:gosec // not credentials
65-
nginxOneTelemetryEndpointHostFlag = "nginx-one-telemetry-endpoint-host"
66-
nginxOneTelemetryEndpointPortFlag = "nginx-one-telemetry-endpoint-port"
67-
nginxOneTLSSkipVerifyFlag = "nginx-one-tls-skip-verify"
68-
metricsDisableFlag = "metrics-disable"
69-
metricsSecureFlag = "metrics-secure-serving"
70-
metricsPortFlag = "metrics-port"
71-
healthDisableFlag = "health-disable"
72-
healthPortFlag = "health-port"
73-
leaderElectionDisableFlag = "leader-election-disable"
74-
leaderElectionLockNameFlag = "leader-election-lock-name"
75-
productTelemetryDisableFlag = "product-telemetry-disable"
76-
gwAPIExperimentalFlag = "gateway-api-experimental-features"
77-
nginxDockerSecretFlag = "nginx-docker-secret" //nolint:gosec // not credentials
78-
usageReportSecretFlag = "usage-report-secret"
79-
usageReportEndpointFlag = "usage-report-endpoint"
80-
usageReportResolverFlag = "usage-report-resolver"
81-
usageReportSkipVerifyFlag = "usage-report-skip-verify"
82-
usageReportClientSSLSecretFlag = "usage-report-client-ssl-secret" //nolint:gosec // not credentials
83-
usageReportCASecretFlag = "usage-report-ca-secret" //nolint:gosec // not credentials
84-
snippetsFiltersFlag = "snippets-filters"
85-
nginxSCCFlag = "nginx-scc"
72+
configFlag = "config"
73+
serviceFlag = "service"
74+
agentTLSSecretFlag = "agent-tls-secret"
75+
nginxOneDataplaneKeySecretFlag = "nginx-one-dataplane-key-secret" //nolint:gosec // not credentials
76+
nginxOneTelemetryEndpointHostFlag = "nginx-one-telemetry-endpoint-host"
77+
nginxOneTelemetryEndpointPortFlag = "nginx-one-telemetry-endpoint-port"
78+
nginxOneTLSSkipVerifyFlag = "nginx-one-tls-skip-verify"
79+
metricsDisableFlag = "metrics-disable"
80+
metricsSecureFlag = "metrics-secure-serving"
81+
metricsPortFlag = "metrics-port"
82+
healthDisableFlag = "health-disable"
83+
healthPortFlag = "health-port"
84+
leaderElectionDisableFlag = "leader-election-disable"
85+
leaderElectionLockNameFlag = "leader-election-lock-name"
86+
productTelemetryDisableFlag = "product-telemetry-disable"
87+
gwAPIExperimentalFlag = "gateway-api-experimental-features"
88+
nginxDockerSecretFlag = "nginx-docker-secret" //nolint:gosec // not credentials
89+
usageReportSecretFlag = "usage-report-secret"
90+
usageReportEndpointFlag = "usage-report-endpoint"
91+
usageReportResolverFlag = "usage-report-resolver"
92+
usageReportSkipVerifyFlag = "usage-report-skip-verify"
93+
usageReportClientSSLSecretFlag = "usage-report-client-ssl-secret" //nolint:gosec // not credentials
94+
usageReportCASecretFlag = "usage-report-ca-secret" //nolint:gosec // not credentials
95+
usageReportEnforceInitialReportFlag = "usage-report-enforce-initial-report"
96+
snippetsFiltersFlag = "snippets-filters"
97+
nginxSCCFlag = "nginx-scc"
8698
)
8799

88100
// flag values
@@ -148,24 +160,26 @@ func createControllerCommand() *cobra.Command {
148160
nginxDockerSecrets = stringSliceValidatingValue{
149161
validator: validateResourceName,
150162
}
151-
usageReportSkipVerify bool
152-
usageReportSecretName = stringValidatingValue{
163+
)
164+
165+
usageReportParams := usageReportParams{
166+
SecretName: stringValidatingValue{
153167
validator: validateResourceName,
154168
value: "nplus-license",
155-
}
156-
usageReportEndpoint = stringValidatingValue{
169+
},
170+
Endpoint: stringValidatingValue{
157171
validator: validateEndpointOptionalPort,
158-
}
159-
usageReportResolver = stringValidatingValue{
172+
},
173+
Resolver: stringValidatingValue{
160174
validator: validateEndpointOptionalPort,
161-
}
162-
usageReportClientSSLSecretName = stringValidatingValue{
175+
},
176+
ClientSSLSecretName: stringValidatingValue{
163177
validator: validateResourceName,
164-
}
165-
usageReportCASecretName = stringValidatingValue{
178+
},
179+
CASecretName: stringValidatingValue{
166180
validator: validateResourceName,
167-
}
168-
)
181+
},
182+
}
169183

170184
cmd := &cobra.Command{
171185
Use: "controller",
@@ -212,18 +226,10 @@ func createControllerCommand() *cobra.Command {
212226
}
213227

214228
var usageReportConfig config.UsageReportConfig
215-
if plus && usageReportSecretName.value == "" {
216-
return errors.New("usage-report-secret is required when using NGINX Plus")
217-
}
218-
219229
if plus {
220-
usageReportConfig = config.UsageReportConfig{
221-
SecretName: usageReportSecretName.value,
222-
ClientSSLSecretName: usageReportClientSSLSecretName.value,
223-
CASecretName: usageReportCASecretName.value,
224-
Endpoint: usageReportEndpoint.value,
225-
Resolver: usageReportResolver.value,
226-
SkipVerify: usageReportSkipVerify,
230+
usageReportConfig, err = buildUsageReportConfig(usageReportParams)
231+
if err != nil {
232+
return err
227233
}
228234
}
229235

@@ -432,47 +438,54 @@ func createControllerCommand() *cobra.Command {
432438
)
433439

434440
cmd.Flags().Var(
435-
&usageReportSecretName,
441+
&usageReportParams.SecretName,
436442
usageReportSecretFlag,
437443
"The name of the Secret containing the JWT for NGINX Plus usage reporting. Must exist in the same namespace "+
438444
"that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway).",
439445
)
440446

441447
cmd.Flags().Var(
442-
&usageReportEndpoint,
448+
&usageReportParams.Endpoint,
443449
usageReportEndpointFlag,
444450
"The endpoint of the NGINX Plus usage reporting server.",
445451
)
446452

447453
cmd.Flags().Var(
448-
&usageReportResolver,
454+
&usageReportParams.Resolver,
449455
usageReportResolverFlag,
450456
"The nameserver used to resolve the NGINX Plus usage reporting endpoint. Used with NGINX Instance Manager.",
451457
)
452458

453459
cmd.Flags().BoolVar(
454-
&usageReportSkipVerify,
460+
&usageReportParams.SkipVerify,
455461
usageReportSkipVerifyFlag,
456462
false,
457463
"Disable client verification of the NGINX Plus usage reporting server certificate.",
458464
)
459465

460466
cmd.Flags().Var(
461-
&usageReportClientSSLSecretName,
467+
&usageReportParams.ClientSSLSecretName,
462468
usageReportClientSSLSecretFlag,
463469
"The name of the Secret containing the client certificate and key for authenticating with NGINX Instance Manager. "+
464470
"Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in "+
465471
"(default namespace: nginx-gateway).",
466472
)
467473

468474
cmd.Flags().Var(
469-
&usageReportCASecretName,
475+
&usageReportParams.CASecretName,
470476
usageReportCASecretFlag,
471477
"The name of the Secret containing the NGINX Instance Manager CA certificate. "+
472478
"Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in "+
473479
"(default namespace: nginx-gateway).",
474480
)
475481

482+
cmd.Flags().BoolVar(
483+
&usageReportParams.EnforceInitialReport,
484+
usageReportEnforceInitialReportFlag,
485+
true,
486+
"Enable enforcement of the initial NGINX Plus licensing report. If set to false, the initial report is not enforced.",
487+
)
488+
476489
cmd.Flags().BoolVar(
477490
&snippetsFilters,
478491
snippetsFiltersFlag,
@@ -491,6 +504,22 @@ func createControllerCommand() *cobra.Command {
491504
return cmd
492505
}
493506

507+
func buildUsageReportConfig(params usageReportParams) (config.UsageReportConfig, error) {
508+
if params.SecretName.value == "" {
509+
return config.UsageReportConfig{}, errors.New("usage-report-secret is required when using NGINX Plus")
510+
}
511+
512+
return config.UsageReportConfig{
513+
SecretName: params.SecretName.value,
514+
ClientSSLSecretName: params.ClientSSLSecretName.value,
515+
CASecretName: params.CASecretName.value,
516+
Endpoint: params.Endpoint.value,
517+
Resolver: params.Resolver.value,
518+
SkipVerify: params.SkipVerify,
519+
EnforceInitialReport: params.EnforceInitialReport,
520+
}, nil
521+
}
522+
494523
func createGenerateCertsCommand() *cobra.Command {
495524
// flag names
496525
const (

cmd/gateway/commands_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ func TestControllerCmdFlagValidation(t *testing.T) {
154154
"--usage-report-resolver=resolver.com",
155155
"--usage-report-ca-secret=ca-secret",
156156
"--usage-report-client-ssl-secret=client-secret",
157+
"--usage-report-enforce-initial-report",
157158
"--snippets-filters",
158159
"--nginx-scc=nginx-sscc-name",
159160
"--nginx-one-dataplane-key-secret=dataplane-key-secret",
@@ -854,3 +855,72 @@ func TestCreateGatewayPodConfig(t *testing.T) {
854855
g.Expect(err).To(MatchError(errors.New("environment variable POD_UID not set")))
855856
g.Expect(cfg).To(Equal(config.GatewayPodConfig{}))
856857
}
858+
859+
func TestUsageReportConfig(t *testing.T) {
860+
t.Parallel()
861+
862+
testCases := []struct {
863+
name string
864+
params usageReportParams
865+
expected config.UsageReportConfig
866+
expectError bool
867+
}{
868+
{
869+
name: "NGINX Plus enabled with all valid parameters",
870+
params: usageReportParams{
871+
SecretName: stringValidatingValue{value: "test-secret"},
872+
ClientSSLSecretName: stringValidatingValue{value: "client-ssl-secret"},
873+
CASecretName: stringValidatingValue{value: "ca-secret"},
874+
Endpoint: stringValidatingValue{value: "example.com"},
875+
Resolver: stringValidatingValue{value: "resolver.com"},
876+
SkipVerify: true,
877+
EnforceInitialReport: false,
878+
},
879+
expectError: false,
880+
expected: config.UsageReportConfig{
881+
SecretName: "test-secret",
882+
ClientSSLSecretName: "client-ssl-secret",
883+
CASecretName: "ca-secret",
884+
Endpoint: "example.com",
885+
Resolver: "resolver.com",
886+
SkipVerify: true,
887+
EnforceInitialReport: false,
888+
},
889+
},
890+
{
891+
name: "NGINX Plus enabled with missing secret",
892+
params: usageReportParams{
893+
SecretName: stringValidatingValue{value: ""},
894+
ClientSSLSecretName: stringValidatingValue{value: "client-ssl-secret"},
895+
CASecretName: stringValidatingValue{value: "ca-secret"},
896+
Endpoint: stringValidatingValue{value: "example.com"},
897+
Resolver: stringValidatingValue{value: "resolver.com"},
898+
SkipVerify: true,
899+
EnforceInitialReport: false,
900+
},
901+
expectError: true,
902+
expected: config.UsageReportConfig{},
903+
},
904+
}
905+
906+
for _, tc := range testCases {
907+
t.Run(tc.name, func(t *testing.T) {
908+
t.Parallel()
909+
result, err := buildUsageReportConfig(tc.params)
910+
911+
if tc.expectError {
912+
if err == nil {
913+
t.Errorf("expected an error but got none")
914+
}
915+
} else {
916+
if err != nil {
917+
t.Errorf("did not expect an error but got: %v", err)
918+
}
919+
920+
if result != tc.expected {
921+
t.Errorf("expected result %+v, but got %+v", tc.expected, result)
922+
}
923+
}
924+
})
925+
}
926+
}

deploy/experimental-nginx-plus/deploy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ spec:
271271
- --nginx-docker-secret=nginx-plus-registry-secret
272272
- --nginx-plus
273273
- --usage-report-secret=nplus-license
274+
- --usage-report-enforce-initial-report=true
274275
- --metrics-port=9113
275276
- --health-port=8081
276277
- --leader-election-lock-name=nginx-gateway-leader-election

deploy/nginx-plus/deploy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ spec:
267267
- --nginx-docker-secret=nginx-plus-registry-secret
268268
- --nginx-plus
269269
- --usage-report-secret=nplus-license
270+
- --usage-report-enforce-initial-report=true
270271
- --metrics-port=9113
271272
- --health-port=8081
272273
- --leader-election-lock-name=nginx-gateway-leader-election

deploy/snippets-filters-nginx-plus/deploy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ spec:
269269
- --nginx-docker-secret=nginx-plus-registry-secret
270270
- --nginx-plus
271271
- --usage-report-secret=nplus-license
272+
- --usage-report-enforce-initial-report=true
272273
- --metrics-port=9113
273274
- --health-port=8081
274275
- --leader-election-lock-name=nginx-gateway-leader-election

internal/controller/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ type UsageReportConfig struct {
125125
Resolver string
126126
// SkipVerify controls whether the nginx verifies the server certificate.
127127
SkipVerify bool
128+
// EnforceInitialReport controls whether the initial NGINX Plus licensing report is enforced.
129+
EnforceInitialReport bool
128130
}
129131

130132
// Flags contains the NGF command-line flag names and values.

0 commit comments

Comments
 (0)