Skip to content

Commit

Permalink
depresolver
Browse files Browse the repository at this point in the history
  • Loading branch information
kuritka committed Feb 10, 2025
1 parent cc3f5c4 commit 00f5cb6
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 6 deletions.
11 changes: 11 additions & 0 deletions controllers/depresolver/depresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ type Config struct {
EdgeDNSZone string `env:"EDGE_DNS_ZONE"`
// DNSZone controlled by gslb; e.g. cloud.example.com
DNSZone string `env:"DNS_ZONE"`
// DelegationZones
DelegationZones []DelegationZoneInfo
// DelegationZones pairs of DNSZone ad EdgeDNSZone, eg: DNS_ZONES=example.com:cloud.example.com;example.io:cloud.example.io
dnsZones string `env:"DNS_ZONES"`
// K8gbNamespace k8gb namespace
K8gbNamespace string `env:"POD_NAMESPACE"`
// Infoblox configuration
Expand All @@ -161,6 +165,13 @@ type Config struct {
OtelExporterOtlpEndpoint string `env:"OTEL_EXPORTER_OTLP_ENDPOINT, default=localhost:4318"`
}

type DelegationZoneInfo struct {
Domain string
NSNames []string
IPs []string
Zone string
}

// DependencyResolver resolves configuration for GSLB
type DependencyResolver struct {
config *Config
Expand Down
45 changes: 45 additions & 0 deletions controllers/depresolver/depresolver_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import (
"github.com/rs/zerolog"
)

// TODO: refactor with kong.CLI to read envvars into Config
// TODO: refactor with go-playground/validator to validate

// Environment variables keys
const (
ReconcileRequeueSecondsKey = "RECONCILE_REQUEUE_SECONDS"
Expand All @@ -41,6 +44,7 @@ const (
EdgeDNSServersKey = "EDGE_DNS_SERVERS"
EdgeDNSZoneKey = "EDGE_DNS_ZONE"
DNSZoneKey = "DNS_ZONE"
DNSZonesKey = "DNS_ZONES"
InfobloxGridHostKey = "INFOBLOX_GRID_HOST"
InfobloxVersionKey = "INFOBLOX_WAPI_VERSION"
InfobloxPortKey = "INFOBLOX_WAPI_PORT"
Expand Down Expand Up @@ -87,6 +91,7 @@ func (dr *DependencyResolver) ResolveOperatorConfig() (*Config, error) {
// calculation
fallbackDNS := fmt.Sprintf("%s:%v", dr.config.fallbackEdgeDNSServerName, dr.config.fallbackEdgeDNSServerPort)
edgeDNSServerList := env.GetEnvAsArrayOfStringsOrFallback(EdgeDNSServersKey, []string{fallbackDNS})
dr.config.DelegationZones = parseDelegationZones(dr.config.dnsZones, dr.config.EdgeDNSZone, dr.config.DNSZone)
dr.config.EdgeDNSServers = parseEdgeDNSServers(edgeDNSServerList)
dr.config.ExtClustersGeoTags = excludeGeoTag(dr.config.ExtClustersGeoTags, dr.config.ClusterGeoTag)
dr.config.Log.Level, _ = zerolog.ParseLevel(strings.ToLower(dr.config.Log.level))
Expand Down Expand Up @@ -423,3 +428,43 @@ func getNsName(tag, dnsZone, edgeDNSZone, edgeDNSServer string) string {
func getHeartbeatFQDN(name, geoTag, edgeDNSZone string) string {
return fmt.Sprintf("%s-heartbeat-%s.%s", name, geoTag, edgeDNSZone)
}

func parseDelegationZones(zones, edgeDNSZone, dnsZone string) []DelegationZoneInfo {
getEnvAsArrayOfPairsOrFallback := func(zones string, fallback map[string]string) map[string]string {
pairs := make(map[string]string)
slice := strings.Split(zones, ";")
if len(slice) == 0 {
return fallback
}
for _, z := range slice {
pair := strings.Split(z, ":")
if len(pair) != 2 {
return fallback
}
pairs[strings.Trim(pair[0], " ")] = strings.Trim(pair[1], " ")
}
for k, v := range fallback {
if _, found := pairs[k]; !found {
pairs[k] = v
}
}
return pairs
}
var dzi []DelegationZoneInfo
if edgeDNSZone == "" || dnsZone == "" {
return dzi
}
zones = strings.TrimSuffix(strings.TrimSuffix(zones, ";"), " ")
fallbackDNSZone := map[string]string{edgeDNSZone: dnsZone}
di := getEnvAsArrayOfPairsOrFallback(zones, fallbackDNSZone)

for edge, zone := range di {
dzi = append(dzi, DelegationZoneInfo{
Domain: zone,
Zone: edge,
IPs: []string{},
NSNames: []string{},
})
}
return dzi
}
163 changes: 157 additions & 6 deletions controllers/depresolver/depresolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
defaultClusterGeoTagUs2 = "us-east-1"
defaultClusterGeoTagEu = "eu-central-1"
defaultEdgeDNSServerIP = "10.0.40.2"
defaultDNSZones = "example.com:cloud.example.com"
)

var predefinedConfig = Config{
Expand All @@ -67,11 +68,20 @@ var predefinedConfig = Config{
},
fallbackEdgeDNSServerName: "",
fallbackEdgeDNSServerPort: 53,
EdgeDNSZone: "example.com",
DNSZone: defaultEdgeDNSZone,
K8gbNamespace: "k8gb",
SplitBrainCheck: true,
MetricsAddress: "0.0.0.0:8080",
dnsZones: defaultDNSZones,
DelegationZones: []DelegationZoneInfo{
{
Zone: "example.com",
Domain: "cloud.example.com",
NSNames: []string{},
IPs: []string{},
},
},
EdgeDNSZone: "example.com",
DNSZone: defaultEdgeDNSZone,
K8gbNamespace: "k8gb",
SplitBrainCheck: true,
MetricsAddress: "0.0.0.0:8080",
Infoblox: Infoblox{
"Infoblox.host.com",
"0.0.3",
Expand Down Expand Up @@ -605,6 +615,7 @@ func TestResolveConfigWithEmptyEdgeDnsZone(t *testing.T) {
defer cleanup()
expected := predefinedConfig
expected.EdgeDNSZone = ""
expected.DelegationZones = nil
// act,assert
arrangeVariablesAndAssert(t, expected, assert.Error)
}
Expand All @@ -614,6 +625,20 @@ func TestResolveConfigWithHostnameEdgeDnsZone(t *testing.T) {
defer cleanup()
expected := predefinedConfig
expected.EdgeDNSZone = "company.2l.com"
expected.DelegationZones = []DelegationZoneInfo{
{
Domain: "cloud.example.com",
NSNames: []string{},
IPs: []string{},
Zone: "example.com",
},
{
Domain: "cloud.example.com",
NSNames: []string{},
IPs: []string{},
Zone: "company.2l.com",
},
}
// act,assert
arrangeVariablesAndAssert(t, expected, assert.NoError)
}
Expand All @@ -623,6 +648,20 @@ func TestResolveConfigWithInvalidHostnameEdgeDnsZone(t *testing.T) {
defer cleanup()
expected := predefinedConfig
expected.EdgeDNSZone = "https://zone.com"
expected.DelegationZones = []DelegationZoneInfo{
{
Domain: "cloud.example.com",
NSNames: []string{},
IPs: []string{},
Zone: "example.com",
},
{
Domain: "cloud.example.com",
NSNames: []string{},
IPs: []string{},
Zone: "https://zone.com",
},
}
// act,assert
arrangeVariablesAndAssert(t, expected, assert.Error)
}
Expand Down Expand Up @@ -663,6 +702,7 @@ func TestResolveEmptyExtGeoTags(t *testing.T) {
defer cleanup()
expected := predefinedConfig
expected.DNSZone = ""
expected.DelegationZones = nil
// act,assert
arrangeVariablesAndAssert(t, expected, assert.Error, DNSZoneKey)
}
Expand Down Expand Up @@ -1527,7 +1567,7 @@ func cleanup() {
EdgeDNSServersKey, ExtDNSEnabledKey, InfobloxGridHostKey, InfobloxVersionKey, InfobloxPortKey, InfobloxUsernameKey,
InfobloxPasswordKey, K8gbNamespaceKey, CoreDNSExposedKey, InfobloxHTTPRequestTimeoutKey,
InfobloxHTTPPoolConnectionsKey, LogLevelKey, LogFormatKey, LogNoColorKey, MetricsAddressKey, SplitBrainCheckKey, TracingEnabled,
TracingSamplingRatio, OtelExporterOtlpEndpoint} {
TracingSamplingRatio, OtelExporterOtlpEndpoint, DNSZonesKey} {
if os.Unsetenv(s) != nil {
panic(fmt.Errorf("cleanup %s", s))
}
Expand Down Expand Up @@ -1562,6 +1602,7 @@ func configureEnvVar(config Config) {
_ = os.Setenv(TracingEnabled, strconv.FormatBool(config.TracingEnabled))
_ = os.Setenv(TracingSamplingRatio, strconv.FormatFloat(config.TracingSamplingRatio, 'f', 2, 64))
_ = os.Setenv(OtelExporterOtlpEndpoint, config.OtelExporterOtlpEndpoint)
_ = os.Setenv(DNSZonesKey, config.dnsZones)
}

func getTestContext(testData string) (client.Client, *k8gbv1beta1.Gslb) {
Expand All @@ -1585,3 +1626,113 @@ func getTestContext(testData string) (client.Client, *k8gbv1beta1.Gslb) {
cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...).Build()
return cl, gslb
}

//nolint:goconst
func TestParseDNSZones(t *testing.T) {
contains := func(dzi []DelegationZoneInfo, compare func(info DelegationZoneInfo) bool) bool {
for _, v := range dzi {
if compare(v) {
return true
}
}
return false
}

tests := []struct {
name string
dnsZones string
dnsZone string
edgeDNSZone string
expectedLen int
assert func(zoneInfo []DelegationZoneInfo)
}{
{
name: "multiple zones",
dnsZones: "example.com:cloud.example.com;example.io:cloud.example.io",
dnsZone: "cloud.example.org",
edgeDNSZone: "example.org",
expectedLen: 3,
assert: func(zoneInfo []DelegationZoneInfo) {
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.com" && info.Domain == "cloud.example.com"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.io" && info.Domain == "cloud.example.io"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.org" && info.Domain == "cloud.example.org"
}))
},
},
{
name: "backward compatibility",
dnsZones: "",
dnsZone: "cloud.example.org",
edgeDNSZone: "example.org",
expectedLen: 1,
assert: func(zoneInfo []DelegationZoneInfo) {
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.org" && info.Domain == "cloud.example.org"
}))
},
},
{
name: "override",
dnsZones: "example.com:cloud.example.com;example.io:cloud.example.io",
dnsZone: "dc.example.com",
edgeDNSZone: "example.com",
expectedLen: 2,
assert: func(zoneInfo []DelegationZoneInfo) {
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.com" && info.Domain == "cloud.example.com"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.io" && info.Domain == "cloud.example.io"
}))
},
},
{
name: "ends with semicolon",
dnsZones: "example.com:cloud.example.com;example.io:cloud.example.io;",
dnsZone: "cloud.example.org",
edgeDNSZone: "example.org",
expectedLen: 3,
assert: func(zoneInfo []DelegationZoneInfo) {
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.com" && info.Domain == "cloud.example.com"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.io" && info.Domain == "cloud.example.io"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.org" && info.Domain == "cloud.example.org"
}))
},
},
{
name: "trimmed spaces and semicolons",
dnsZones: "example.com: cloud.example.com; example.io:cloud.example.io ;",
dnsZone: "cloud.example.org",
edgeDNSZone: "example.org",
expectedLen: 3,
assert: func(zoneInfo []DelegationZoneInfo) {
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.com" && info.Domain == "cloud.example.com"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.io" && info.Domain == "cloud.example.io"
}))
assert.True(t, contains(zoneInfo, func(info DelegationZoneInfo) bool {
return info.Zone == "example.org" && info.Domain == "cloud.example.org"
}))
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
zoneInfo := parseDelegationZones(test.dnsZones, test.edgeDNSZone, test.dnsZone)
test.assert(zoneInfo)
assert.Equal(t, test.expectedLen, len(zoneInfo))
})
}
}

0 comments on commit 00f5cb6

Please sign in to comment.