Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial zone_sync values to the ConfigMap #7239

Merged
merged 10 commits into from
Feb 4, 2025
8 changes: 8 additions & 0 deletions internal/configs/config_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type ConfigParams struct {
UseClusterIP bool
VariablesHashBucketSize uint64
VariablesHashMaxSize uint64
ZoneSync ZoneSync

RealIPHeader string
RealIPRecursive bool
Expand Down Expand Up @@ -175,6 +176,13 @@ type Listener struct {
Protocol string
}

// ZoneSync holds zone sync values for state sharing.
type ZoneSync struct {
Enable bool
Port int
Domain string
}

// MGMTSecrets holds mgmt block secret names
type MGMTSecrets struct {
License string
Expand Down
50 changes: 50 additions & 0 deletions internal/configs/configmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"context"
"errors"
"fmt"
"os"
"strings"
"time"

Expand Down Expand Up @@ -399,6 +400,47 @@
}
}

if zoneSync, exists, err := GetMapKeyAsBool(cfgm.Data, "zone-sync", cfgm); exists {
if err != nil {
nl.Error(l, err)
eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, err.Error())
configOk = false

Check warning on line 407 in internal/configs/configmaps.go

View check run for this annotation

Codecov / codecov/patch

internal/configs/configmaps.go#L405-L407

Added lines #L405 - L407 were not covered by tests
} else {
if nginxPlus {
cfgParams.ZoneSync.Enable = zoneSync
} else {
errorText := fmt.Sprintf("ConfigMap %s/%s key %s requires NGINX Plus", cfgm.Namespace, cfgm.Name, "zone-sync")
nl.Warn(l, errorText)
eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText)
configOk = false
}

Check warning on line 416 in internal/configs/configmaps.go

View check run for this annotation

Codecov / codecov/patch

internal/configs/configmaps.go#L412-L416

Added lines #L412 - L416 were not covered by tests
}
}

if zoneSyncPort, exists, err := GetMapKeyAsInt(cfgm.Data, "zone-sync-port", cfgm); exists {
if err != nil {
nl.Error(l, err)
eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, err.Error())
configOk = false
} else {
if cfgParams.ZoneSync.Enable {
portValidationError := validation.ValidatePort(zoneSyncPort)
if portValidationError != nil {
nl.Error(l, portValidationError)
eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, portValidationError.Error())
configOk = false

Check warning on line 431 in internal/configs/configmaps.go

View check run for this annotation

Codecov / codecov/patch

internal/configs/configmaps.go#L429-L431

Added lines #L429 - L431 were not covered by tests
} else {
cfgParams.ZoneSync.Port = zoneSyncPort
}
} else {
errorText := fmt.Sprintf("ConfigMap %s/%s key %s requires 'zone-sync' to be enabled", cfgm.Namespace, cfgm.Name, "zone-sync-port")
nl.Warn(l, errorText)
eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText)
configOk = false
}
}
}

if upstreamZoneSize, exists := cfgm.Data["upstream-zone-size"]; exists {
cfgParams.UpstreamZoneSize = upstreamZoneSize
}
Expand Down Expand Up @@ -777,6 +819,13 @@
}
}

podNamespace := os.Getenv("POD_NAMESPACE")
zoneSyncConfig := version1.ZoneSyncConfig{
Enable: config.ZoneSync.Enable,
Port: config.ZoneSync.Port,
Domain: fmt.Sprintf("%s-headless.%s.svc.cluster.local", podNamespace, podNamespace),
}

nginxCfg := &version1.MainConfig{
AccessLog: config.MainAccessLog,
DefaultServerAccessLogOff: config.DefaultServerAccessLogOff,
Expand Down Expand Up @@ -850,6 +899,7 @@
InternalRouteServerName: staticCfgParams.InternalRouteServerName,
LatencyMetrics: staticCfgParams.EnableLatencyMetrics,
OIDC: staticCfgParams.EnableOIDC,
ZoneSyncConfig: zoneSyncConfig,
DynamicSSLReloadEnabled: staticCfgParams.DynamicSSLReload,
StaticSSLPath: staticCfgParams.StaticSSLPath,
NginxVersion: staticCfgParams.NginxVersion,
Expand Down
187 changes: 187 additions & 0 deletions internal/configs/configmaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package configs

import (
"context"
"fmt"
"os"
"testing"

"github.com/nginx/kubernetes-ingress/internal/configs/version1"

v1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"
)
Expand Down Expand Up @@ -788,6 +792,189 @@ func TestParseMGMTConfigMapUsageReportEndpoint(t *testing.T) {
}
}

func TestParseZoneSync(t *testing.T) {
t.Parallel()
tests := []struct {
configMap *v1.ConfigMap
want *ZoneSync
msg string
}{
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync": "true",
},
},
want: &ZoneSync{
Enable: true,
},
msg: "zone-sync set to true",
},
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync": "false",
},
},
want: &ZoneSync{
Enable: false,
},
msg: "zone-sync set to false",
},
}

for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
result, _ := ParseConfigMap(context.Background(), test.configMap, true, false, false, false, makeEventLogger())
if result.ZoneSync.Enable != test.want.Enable {
t.Errorf("Enable: want %v, got %v", test.want.Enable, result.ZoneSync)
}
})
}
}

func TestParseZoneSyncPort(t *testing.T) {
t.Parallel()
tests := []struct {
configMap *v1.ConfigMap
want *ZoneSync
msg string
}{
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync": "true",
"zone-sync-port": "1234",
},
},
want: &ZoneSync{
Enable: true,
Port: 1234,
},
msg: "zone-sync-port set to 1234",
},
}

nginxPlus := true
hasAppProtect := true
hasAppProtectDos := false
hasTLSPassthrough := false

for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
result, _ := ParseConfigMap(context.Background(), test.configMap, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
if result.ZoneSync.Port != test.want.Port {
t.Errorf("Port: want %v, got %v", test.want.Port, result.ZoneSync.Port)
}
})
}
}

func TestParseZoneSyncPortErrors(t *testing.T) {
t.Parallel()
tests := []struct {
configMap *v1.ConfigMap
configOk bool
msg string
}{
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync-port": "0",
},
},
configOk: false,
msg: "port out of range (0)",
},
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync-port": "-1",
},
},
configOk: false,
msg: "port out of range (negative)",
},
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync-port": "65536",
},
},
configOk: false,
msg: "port out of range (greater than 65535)",
},
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync-port": "not-a-number",
},
},
configOk: false,
msg: "invalid non-numeric port",
},
{
configMap: &v1.ConfigMap{
Data: map[string]string{
"zone-sync-port": "",
},
},
configOk: false,
msg: "missing port value",
},
}

nginxPlus := true
hasAppProtect := true
hasAppProtectDos := false
hasTLSPassthrough := false

for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
_, err := ParseConfigMap(context.Background(), test.configMap, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, makeEventLogger())
if err == !test.configOk {
t.Error("Expected error, got nil")
}
})
}
}

func TestZoneSyncDomainMultipleNamespaces(t *testing.T) {
testCases := []struct {
name string
namespace string
want string
}{
{
name: "nginx-ingress",
namespace: "nginx-ingress",
want: "nginx-ingress-headless.nginx-ingress.svc.cluster.local",
},
{
name: "my-release-nginx-ingress",
namespace: "my-release-nginx-ingress",
want: "my-release-nginx-ingress-headless.my-release-nginx-ingress.svc.cluster.local",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_ = os.Setenv("POD_NAMESPACE", tc.namespace)

zoneSyncConfig := version1.ZoneSyncConfig{
Enable: true,
Domain: fmt.Sprintf("%s-headless.%s.svc.cluster.local",
os.Getenv("POD_NAMESPACE"),
os.Getenv("POD_NAMESPACE")),
}

if zoneSyncConfig.Domain != tc.want {
t.Errorf("want %q, got %q", tc.want, zoneSyncConfig.Domain)
}
})
}
}

func makeEventLogger() record.EventRecorder {
return record.NewFakeRecorder(1024)
}
8 changes: 8 additions & 0 deletions internal/configs/version1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ type Location struct {
MinionIngress *Ingress
}

// ZoneSyncConfig is tbe configuration for the zone_sync directives for state sharing.
type ZoneSyncConfig struct {
Enable bool
Port int
Domain string
}

// MGMTConfig is tbe configuration for the MGMT block.
type MGMTConfig struct {
SSLVerify *bool
Expand Down Expand Up @@ -276,6 +283,7 @@ type MainConfig struct {
InternalRouteServer bool
InternalRouteServerName string
LatencyMetrics bool
ZoneSyncConfig ZoneSyncConfig
OIDC bool
DynamicSSLReloadEnabled bool
StaticSSLPath string
Expand Down
Loading