Skip to content

Commit 245b516

Browse files
authored
Write deployment context in init container (#2871)
Problems: * When running with N+, the first usage report does not contain the deployment context. The deployment context is needed in order to associate the instance with NGF. * When collecting the info for the deployment context, it is possible to fail. In this case we send a report to N1 without the deployment context. * The installation ID is the deployment ID, but it should be the Pod ID. Solutions: * When running with N+, write the deployment context to the shared volume mount in the init container. * Use the Pod UID instead of the deployment UID as the installation ID * Always set the static values of the deployment context in the nginx config. Static values include the integration and installation IDs.
1 parent 58801be commit 245b516

38 files changed

+1246
-285
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@ data:
2929
ssl_certificate_key /etc/nginx/certs-bootstrap/tls.key;
3030
{{- end }}
3131
enforce_initial_report off;
32+
deployment_context /etc/nginx/main-includes/deployment_ctx.json;
3233
}
3334
{{- end }}

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,26 @@ spec:
3434
{{- toYaml .Values.topologySpreadConstraints | nindent 8 }}
3535
{{- end }}
3636
initContainers:
37-
- name: copy-nginx-config
37+
- name: init
3838
image: {{ .Values.nginxGateway.image.repository }}:{{ default .Chart.AppVersion .Values.nginxGateway.image.tag }}
3939
imagePullPolicy: {{ .Values.nginxGateway.image.pullPolicy }}
4040
command:
4141
- /usr/bin/gateway
42-
- copy
42+
- initialize
4343
- --source
4444
- /includes/main.conf
4545
{{- if .Values.nginx.plus }}
4646
- --source
4747
- /includes/mgmt.conf
48+
- --nginx-plus
4849
{{- end }}
4950
- --destination
5051
- /etc/nginx/main-includes
52+
env:
53+
- name: POD_UID
54+
valueFrom:
55+
fieldRef:
56+
fieldPath: metadata.uid
5157
securityContext:
5258
seccompProfile:
5359
type: RuntimeDefault
@@ -132,6 +138,10 @@ spec:
132138
valueFrom:
133139
fieldRef:
134140
fieldPath: metadata.name
141+
- name: POD_UID
142+
valueFrom:
143+
fieldRef:
144+
fieldPath: metadata.uid
135145
image: {{ .Values.nginxGateway.image.repository }}:{{ default .Chart.AppVersion .Values.nginxGateway.image.tag }}
136146
imagePullPolicy: {{ .Values.nginxGateway.image.pullPolicy }}
137147
name: nginx-gateway

cmd/gateway/commands.go

+105-55
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ package main
33
import (
44
"errors"
55
"fmt"
6-
"io"
76
"os"
8-
"path/filepath"
97
"runtime/debug"
108
"strconv"
119
"time"
@@ -16,14 +14,20 @@ import (
1614
"k8s.io/apimachinery/pkg/types"
1715
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1816
"k8s.io/klog/v2"
17+
ctlr "sigs.k8s.io/controller-runtime"
18+
"sigs.k8s.io/controller-runtime/pkg/client"
1919
"sigs.k8s.io/controller-runtime/pkg/log"
2020
ctlrZap "sigs.k8s.io/controller-runtime/pkg/log/zap"
2121

2222
"github.com./nginxinc/nginx-gateway-fabric/internal/mode/provisioner"
2323
"github.com./nginxinc/nginx-gateway-fabric/internal/mode/static"
2424
"github.com./nginxinc/nginx-gateway-fabric/internal/mode/static/config"
25+
"github.com./nginxinc/nginx-gateway-fabric/internal/mode/static/licensing"
26+
ngxConfig "github.com./nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config"
27+
"github.com./nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/file"
2528
)
2629

30+
// These flags are shared by multiple commands.
2731
const (
2832
domain = "gateway.nginx.org"
2933
gatewayClassFlag = "gatewayclass"
@@ -32,6 +36,7 @@ const (
3236
gatewayCtlrNameFlag = "gateway-ctlr-name"
3337
gatewayCtlrNameUsageFmt = `The name of the Gateway controller. ` +
3438
`The controller name must be of the form: DOMAIN/PATH. The controller's domain is '%s'`
39+
plusFlag = "nginx-plus"
3540
)
3641

3742
func createRootCommand() *cobra.Command {
@@ -47,7 +52,6 @@ func createRootCommand() *cobra.Command {
4752
return rootCmd
4853
}
4954

50-
//nolint:gocyclo
5155
func createStaticModeCommand() *cobra.Command {
5256
// flag names
5357
const (
@@ -63,7 +67,6 @@ func createStaticModeCommand() *cobra.Command {
6367
leaderElectionDisableFlag = "leader-election-disable"
6468
leaderElectionLockNameFlag = "leader-election-lock-name"
6569
productTelemetryDisableFlag = "product-telemetry-disable"
66-
plusFlag = "nginx-plus"
6770
gwAPIExperimentalFlag = "gateway-api-experimental-features"
6871
usageReportSecretFlag = "usage-report-secret"
6972
usageReportEndpointFlag = "usage-report-endpoint"
@@ -159,21 +162,6 @@ func createStaticModeCommand() *cobra.Command {
159162
return fmt.Errorf("error validating ports: %w", err)
160163
}
161164

162-
podIP := os.Getenv("POD_IP")
163-
if err := validateIP(podIP); err != nil {
164-
return fmt.Errorf("error validating POD_IP environment variable: %w", err)
165-
}
166-
167-
namespace := os.Getenv("POD_NAMESPACE")
168-
if namespace == "" {
169-
return errors.New("POD_NAMESPACE environment variable must be set")
170-
}
171-
172-
podName := os.Getenv("POD_NAME")
173-
if podName == "" {
174-
return errors.New("POD_NAME environment variable must be set")
175-
}
176-
177165
imageSource := os.Getenv("BUILD_AGENT")
178166
if imageSource != "gha" && imageSource != "local" {
179167
imageSource = "unknown"
@@ -218,6 +206,11 @@ func createStaticModeCommand() *cobra.Command {
218206

219207
flagKeys, flagValues := parseFlags(cmd.Flags())
220208

209+
podConfig, err := createGatewayPodConfig(serviceName.value)
210+
if err != nil {
211+
return fmt.Errorf("error creating gateway pod config: %w", err)
212+
}
213+
221214
conf := config.Config{
222215
GatewayCtlrName: gatewayCtlrName.value,
223216
ConfigName: configName.String(),
@@ -226,12 +219,7 @@ func createStaticModeCommand() *cobra.Command {
226219
GatewayClassName: gatewayClassName.value,
227220
GatewayNsName: gwNsName,
228221
UpdateGatewayClassStatus: updateGCStatus,
229-
GatewayPodConfig: config.GatewayPodConfig{
230-
PodIP: podIP,
231-
ServiceName: serviceName.value,
232-
Namespace: namespace,
233-
Name: podName,
234-
},
222+
GatewayPodConfig: podConfig,
235223
HealthConfig: config.HealthConfig{
236224
Enabled: !disableHealth,
237225
Port: healthListenPort.value,
@@ -244,7 +232,7 @@ func createStaticModeCommand() *cobra.Command {
244232
LeaderElection: config.LeaderElectionConfig{
245233
Enabled: !disableLeaderElection,
246234
LockName: leaderElectionLockName.String(),
247-
Identity: podName,
235+
Identity: podConfig.Name,
248236
},
249237
UsageReportConfig: usageReportConfig,
250238
ProductTelemetryConfig: config.ProductTelemetryConfig{
@@ -524,29 +512,63 @@ func createSleepCommand() *cobra.Command {
524512
return cmd
525513
}
526514

527-
func createCopyCommand() *cobra.Command {
515+
func createInitializeCommand() *cobra.Command {
528516
// flag names
529517
const srcFlag = "source"
530518
const destFlag = "destination"
519+
531520
// flag values
532521
var srcFiles []string
533522
var dest string
523+
var plus bool
534524

535525
cmd := &cobra.Command{
536-
Use: "copy",
537-
Short: "Copy files to another directory",
526+
Use: "initialize",
527+
Short: "Write initial configuration files",
538528
RunE: func(_ *cobra.Command, _ []string) error {
539-
if err := validateSleepArgs(srcFiles, dest); err != nil {
529+
if err := validateCopyArgs(srcFiles, dest); err != nil {
540530
return err
541531
}
542532

543-
for _, src := range srcFiles {
544-
if err := copyFile(src, dest); err != nil {
545-
return err
546-
}
533+
podUID, err := getValueFromEnv("POD_UID")
534+
if err != nil {
535+
return fmt.Errorf("could not get pod UID: %w", err)
547536
}
548537

549-
return nil
538+
clusterCfg := ctlr.GetConfigOrDie()
539+
k8sReader, err := client.New(clusterCfg, client.Options{})
540+
if err != nil {
541+
return fmt.Errorf("unable to initialize k8s client: %w", err)
542+
}
543+
544+
logger := ctlrZap.New()
545+
klog.SetLogger(logger)
546+
logger.Info(
547+
"Starting init container",
548+
"source filenames to copy", srcFiles,
549+
"destination directory", dest,
550+
"nginx-plus",
551+
plus,
552+
)
553+
log.SetLogger(logger)
554+
555+
dcc := licensing.NewDeploymentContextCollector(licensing.DeploymentContextCollectorConfig{
556+
K8sClientReader: k8sReader,
557+
PodUID: podUID,
558+
Logger: logger.WithName("deployCtxCollector"),
559+
})
560+
561+
return initialize(initializeConfig{
562+
fileManager: file.NewStdLibOSFileManager(),
563+
fileGenerator: ngxConfig.NewGeneratorImpl(plus, nil, logger.WithName("generator")),
564+
logger: logger,
565+
plus: plus,
566+
collector: dcc,
567+
copy: copyFiles{
568+
srcFileNames: srcFiles,
569+
destDirName: dest,
570+
},
571+
})
550572
},
551573
}
552574

@@ -564,31 +586,18 @@ func createCopyCommand() *cobra.Command {
564586
"The destination directory for the source files to be copied to",
565587
)
566588

589+
cmd.Flags().BoolVar(
590+
&plus,
591+
plusFlag,
592+
false,
593+
"Use NGINX Plus",
594+
)
595+
567596
cmd.MarkFlagsRequiredTogether(srcFlag, destFlag)
568597

569598
return cmd
570599
}
571600

572-
func copyFile(src, dest string) error {
573-
srcFile, err := os.Open(src)
574-
if err != nil {
575-
return fmt.Errorf("error opening source file: %w", err)
576-
}
577-
defer srcFile.Close()
578-
579-
destFile, err := os.Create(filepath.Join(dest, filepath.Base(src)))
580-
if err != nil {
581-
return fmt.Errorf("error creating destination file: %w", err)
582-
}
583-
defer destFile.Close()
584-
585-
if _, err := io.Copy(destFile, srcFile); err != nil {
586-
return fmt.Errorf("error copying file contents: %w", err)
587-
}
588-
589-
return nil
590-
}
591-
592601
func parseFlags(flags *pflag.FlagSet) ([]string, []string) {
593602
var flagKeys, flagValues []string
594603

@@ -634,3 +643,44 @@ func getBuildInfo() (commitHash string, commitTime string, dirtyBuild string) {
634643

635644
return
636645
}
646+
647+
func createGatewayPodConfig(svcName string) (config.GatewayPodConfig, error) {
648+
podIP, err := getValueFromEnv("POD_IP")
649+
if err != nil {
650+
return config.GatewayPodConfig{}, err
651+
}
652+
653+
podUID, err := getValueFromEnv("POD_UID")
654+
if err != nil {
655+
return config.GatewayPodConfig{}, err
656+
}
657+
658+
ns, err := getValueFromEnv("POD_NAMESPACE")
659+
if err != nil {
660+
return config.GatewayPodConfig{}, err
661+
}
662+
663+
name, err := getValueFromEnv("POD_NAME")
664+
if err != nil {
665+
return config.GatewayPodConfig{}, err
666+
}
667+
668+
c := config.GatewayPodConfig{
669+
PodIP: podIP,
670+
ServiceName: svcName,
671+
Namespace: ns,
672+
Name: name,
673+
UID: podUID,
674+
}
675+
676+
return c, nil
677+
}
678+
679+
func getValueFromEnv(key string) (string, error) {
680+
val := os.Getenv(key)
681+
if val == "" {
682+
return "", fmt.Errorf("environment variable %s not set", key)
683+
}
684+
685+
return val, nil
686+
}

0 commit comments

Comments
 (0)