Skip to content

Commit 5155c91

Browse files
authored
Merge pull request #5429 from arkadeepsen/fix-dnsnameresolver-address-set
Fix dnsnameresolver address set
2 parents 30176f6 + 8eb02f9 commit 5155c91

File tree

6 files changed

+151
-21
lines changed

6 files changed

+151
-21
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,14 +454,14 @@ jobs:
454454
- {"target": "shard-conformance", "ha": "HA", "gateway-mode": "local", "ipfamily": "dualstack", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default"}
455455
- {"target": "shard-conformance", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones"}
456456
- {"target": "shard-conformance", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones"}
457-
- {"target": "control-plane", "ha": "HA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-disabled", "dns-name-resolver": "enable-dns-name-resolver"}
457+
- {"target": "control-plane", "ha": "HA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-disabled"}
458458
- {"target": "control-plane", "ha": "HA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-disabled", "traffic-flow-tests": "1,2,3"}
459459
- {"target": "control-plane-helm","ha": "HA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-disabled", "dns-name-resolver": "enable-dns-name-resolver"}
460460
- {"target": "control-plane-helm","ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "dns-name-resolver": "enable-dns-name-resolver"}
461461
- {"target": "control-plane", "ha": "noHA", "gateway-mode": "local", "ipfamily": "ipv4", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "dns-name-resolver": "enable-dns-name-resolver"}
462462
- {"target": "control-plane", "ha": "noHA", "gateway-mode": "local", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones"}
463463
- {"target": "control-plane", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "2br", "ic": "ic-single-node-zones"}
464-
- {"target": "control-plane", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "2br", "ic": "ic-single-node-zones", "dns-name-resolver": "enable-dns-name-resolver"}
464+
- {"target": "control-plane", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "2br", "ic": "ic-single-node-zones"}
465465
- {"target": "multi-homing", "ha": "noHA", "gateway-mode": "local", "ipfamily": "ipv4", "disable-snat-multiple-gws": "SnatGW", "second-bridge": "1br", "ic": "ic-disabled"}
466466
- {"target": "multi-homing-helm", "ha": "HA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-disabled", "network-segmentation": "enable-network-segmentation"}
467467
- {"target": "node-ip-mac-migration", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "SnatGW", "second-bridge": "1br", "ic": "ic-disabled"}
@@ -481,8 +481,8 @@ jobs:
481481
- {"target": "network-segmentation", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "dualstack", "disable-snat-multiple-gws": "SnatGW", "second-bridge": "1br", "ic": "ic-disabled"}
482482
- {"target": "network-segmentation", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones"}
483483
- {"target": "network-segmentation", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones"}
484-
- {"target": "bgp", "ha": "noHA", "gateway-mode": "local", "ipfamily": "dualstack", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation"}
485-
- {"target": "bgp", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "dualstack", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation"}
484+
- {"target": "bgp", "ha": "noHA", "gateway-mode": "local", "ipfamily": "dualstack", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation", "dns-name-resolver": "enable-dns-name-resolver"}
485+
- {"target": "bgp", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "dualstack", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation", "dns-name-resolver": "enable-dns-name-resolver"}
486486
- {"target": "traffic-flow-test-only","ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "traffic-flow-tests": "1-24", "network-segmentation": "enable-network-segmentation"}
487487
- {"target": "tools", "ha": "noHA", "gateway-mode": "local", "ipfamily": "dualstack", "disable-snat-multiple-gws": "SnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "network-segmentation": "enable-network-segmentation"}
488488
needs: [ build-pr ]

go-controller/pkg/ovn/dns_name_resolver/external_dns.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func (extEgDNS *ExternalEgressDNS) reconcileDNSNameResolver(key string) error {
159159
addresses = append(addresses, resolvedAddress.IP)
160160
}
161161
}
162-
err = extEgDNS.dnsTracker.addDNSName(dnsName, addresses)
162+
err = extEgDNS.dnsTracker.addOrUpdateDNSName(dnsName, addresses)
163163
return err
164164
}
165165

go-controller/pkg/ovn/dns_name_resolver/external_dns_test.go

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,15 @@ func newDNSNameResolverObject(name, namespace, dnsName string, addresses []strin
5252
}
5353

5454
func expectDNSNameWithAddresses(extEgDNS *ExternalEgressDNS, dnsName string, expectedAddresses []string) {
55-
var resolvedName *dnsResolvedName
56-
err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, 5*time.Minute, true, func(context.Context) (done bool, err error) {
57-
var exists bool
58-
resolvedName, exists = extEgDNS.getResolvedName(dnsName)
55+
gomega.Eventually(func() []string {
56+
resolvedName, exists := extEgDNS.getResolvedName(dnsName)
5957
if !exists {
60-
return false, nil
58+
return []string{}
6159
}
62-
63-
return true, nil
64-
})
65-
gomega.Expect(err).NotTo(gomega.HaveOccurred())
66-
67-
v4, v6 := resolvedName.dnsAddressSet.GetAddresses()
68-
ipStrings := append(v4, v6...)
69-
gomega.Expect(ipStrings).To(gomega.ConsistOf(expectedAddresses))
60+
v4, v6 := resolvedName.dnsAddressSet.GetAddresses()
61+
ipStrings := append(v4, v6...)
62+
return ipStrings
63+
}).Should(gomega.ConsistOf(expectedAddresses))
7064
}
7165

7266
var _ = ginkgo.Describe("Egress Firewall External DNS Operations", func() {
@@ -227,6 +221,37 @@ var _ = ginkgo.Describe("Egress Firewall External DNS Operations", func() {
227221
})
228222
})
229223

224+
ginkgo.Context("on dns name resolver resource update", func() {
225+
ginkgo.It("Should update addresses for a dns name", func() {
226+
start()
227+
228+
config.IPv4Mode = true
229+
config.IPv6Mode = true
230+
231+
addresses := []string{"1.1.1.1", "2.2.2.2", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}
232+
dnsNameResolver := newDNSNameResolverObject("dns-default", config.Kubernetes.OVNConfigNamespace, dnsName, addresses)
233+
234+
_, err := fakeClient.OCPNetworkClient.NetworkV1alpha1().DNSNameResolvers(dnsNameResolver.Namespace).
235+
Create(context.TODO(), dnsNameResolver, metav1.CreateOptions{})
236+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
237+
238+
expectDNSNameWithAddresses(extEgDNS, dnsName, addresses)
239+
240+
addresses = []string{"2.2.2.2", "3.3.3.3", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:7335"}
241+
var resolvedAddresses []ocpnetworkapiv1alpha1.DNSNameResolverResolvedAddress
242+
for _, address := range addresses {
243+
resolvedAddresses = append(resolvedAddresses, ocpnetworkapiv1alpha1.DNSNameResolverResolvedAddress{IP: address})
244+
}
245+
dnsNameResolver.Status.ResolvedNames[0].ResolvedAddresses = resolvedAddresses
246+
247+
_, err = fakeClient.OCPNetworkClient.NetworkV1alpha1().DNSNameResolvers(dnsNameResolver.Namespace).
248+
Update(context.TODO(), dnsNameResolver, metav1.UpdateOptions{})
249+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
250+
251+
expectDNSNameWithAddresses(extEgDNS, dnsName, addresses)
252+
})
253+
})
254+
230255
ginkgo.It("Should not delete added addresses if DNS name is still used in a namespace", func() {
231256
start()
232257

go-controller/pkg/ovn/dns_name_resolver/external_dns_tracker.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ func newDNSTracker(addressSetFactory addressset.AddressSetFactory, controllerNam
5959
}
6060
}
6161

62-
// addDNSName is called whenever a DNS name is needed to be added or updated.
63-
func (dnsTracker *dnsTracker) addDNSName(dnsName string, addresses []string) error {
62+
// addOrUpdateDNSName is called whenever a DNS name is needed to be added or updated.
63+
func (dnsTracker *dnsTracker) addOrUpdateDNSName(dnsName string, addresses []string) error {
6464
dnsTracker.dnsLock.Lock()
6565
defer dnsTracker.dnsLock.Unlock()
6666

@@ -92,7 +92,7 @@ func (dnsTracker *dnsTracker) addDNSName(dnsName string, addresses []string) err
9292
addresses = filteredIPs
9393
}
9494

95-
if err := resolvedName.dnsAddressSet.AddAddresses(addresses); err != nil {
95+
if err := resolvedName.dnsAddressSet.SetAddresses(addresses); err != nil {
9696
return fmt.Errorf("cannot add IPs to AddressSet for DNS name %s: %v", dnsName, err)
9797
}
9898

test/e2e/egress_firewall.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"k8s.io/kubernetes/test/e2e/framework"
3030
e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
3131
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
32+
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
3233
utilnet "k8s.io/utils/net"
3334
)
3435

@@ -664,4 +665,103 @@ spec:
664665
table.Entry("", false),
665666
table.Entry("with chaos testing using many dnsNames", true),
666667
)
668+
669+
ginkgo.Context("with DNS name resolver", func() {
670+
ginkgo.BeforeEach(func() {
671+
// DNS resolution for external DNS names does not work on IPv6 clusters. Skip
672+
// the test if DNS name resolver is not enabled or IPv4 is not supported.
673+
if !isDNSNameResolverEnabled() {
674+
e2eskipper.Skipf("DNS name resolver is not enabled")
675+
return
676+
}
677+
if !isIPv4Supported(f.ClientSet) {
678+
e2eskipper.Skipf("IPv4 is not supported")
679+
return
680+
}
681+
})
682+
683+
getMinTTLForDNSName := func(dnsName string, srcPodName string) int {
684+
// Get the minimum TTL for the DNS name from the nslookup output.
685+
// Ignore the error as it will always return an error because the
686+
// cluster local DNS lookup will fail for the following DNS names:
687+
// - <dnsName>.<namespace>.svc.<cluster-domain>
688+
// - <dnsName>.svc.<cluster-domain>.
689+
// - <dnsName>.<cluster-domain>
690+
nslookupOutput, _ := e2ekubectl.RunKubectl(f.Namespace.Name, "exec", srcPodName, "--", "nslookup", "-debug", "-timeout=2", dnsName)
691+
lines := strings.Split(nslookupOutput, "\n")
692+
minTTL := -1
693+
for i := 0; i < len(lines); i++ {
694+
answerLine := strings.TrimSpace(lines[i])
695+
// Skip lines until we find the answer line for the DNS name
696+
if !strings.HasPrefix(answerLine, fmt.Sprintf("-> %s", dnsName)) {
697+
continue
698+
}
699+
// Find the TTL line for the DNS name
700+
for i++; i < len(lines); i++ {
701+
ttlLine := strings.TrimSpace(lines[i])
702+
if strings.HasPrefix(ttlLine, "ttl =") {
703+
// Extract TTL value
704+
ttlParts := strings.Split(ttlLine, "ttl =")
705+
if len(ttlParts) == 2 {
706+
ttlStr := strings.TrimSpace(ttlParts[1])
707+
ttl, err := strconv.Atoi(ttlStr)
708+
gomega.Expect(err).NotTo(gomega.HaveOccurred(), "failed to parse TTL value '%s': %v", ttlStr, err)
709+
710+
// Update minimum TTL
711+
if minTTL == -1 || ttl < minTTL {
712+
minTTL = ttl
713+
}
714+
}
715+
break
716+
}
717+
}
718+
}
719+
return minTTL
720+
}
721+
722+
ginkgo.It("Should validate that egressfirewall policy functionality for allowed DNS name", func() {
723+
dnsName := "www.google.com"
724+
srcPodName := "e2e-egress-fw-src-pod"
725+
726+
// egress firewall crd yaml configuration
727+
var egressFirewallConfig = fmt.Sprintf(`kind: EgressFirewall
728+
apiVersion: k8s.ovn.org/v1
729+
metadata:
730+
name: default
731+
namespace: %s
732+
spec:
733+
egress:
734+
- type: Allow
735+
to:
736+
dnsName: %s
737+
- type: Deny
738+
to:
739+
cidrSelector: %s
740+
`, f.Namespace.Name, dnsName, denyAllCIDR)
741+
applyEF(egressFirewallConfig, f.Namespace.Name)
742+
743+
// create the pod that will be used as the source for the connectivity test
744+
createSrcPod(srcPodName, serverNodeInfo.name, retryInterval, retryTimeout, f)
745+
746+
ginkgo.By(fmt.Sprintf("Verifying connectivity to DNS name %s is permitted", dnsName))
747+
url := fmt.Sprintf("https://%s", dnsName)
748+
_, err := e2ekubectl.RunKubectl(f.Namespace.Name, "exec", srcPodName, "--", "curl", "-g", "--max-time", "5", url)
749+
framework.ExpectNoError(err, "failed to curl DNS name %s", dnsName)
750+
751+
ginkgo.By(fmt.Sprintf("Getting the minimum TTL for DNS name %s", dnsName))
752+
minTTL := getMinTTLForDNSName(dnsName, srcPodName)
753+
gomega.Expect(minTTL).NotTo(gomega.Equal(-1), "failed to parse nslookup output for DNS name %s", dnsName)
754+
framework.Logf("Minimum TTL for DNS name %s is %d", dnsName, minTTL)
755+
756+
ginkgo.By(fmt.Sprintf("Waiting for the minimum TTL + 5 seconds for IP addresses of DNS name %s to be refreshed", dnsName))
757+
time.Sleep(time.Duration(minTTL+5) * time.Second)
758+
759+
ginkgo.By(fmt.Sprintf("Verifying connectivity to DNS name %s is still permitted", dnsName))
760+
_, err = e2ekubectl.RunKubectl(f.Namespace.Name, "exec", srcPodName, "--", "curl", "-g", "--max-time", "5", url)
761+
framework.ExpectNoError(err, "failed to curl DNS name %s", dnsName)
762+
763+
framework.Logf("Deleting EgressFirewall in namespace %s", f.Namespace.Name)
764+
e2ekubectl.RunKubectlOrDie(f.Namespace.Name, "delete", "egressfirewall", "default")
765+
})
766+
})
667767
})

test/e2e/util.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,3 +1540,8 @@ func executeFileTemplate(templates *template.Template, directory, name string, d
15401540
}
15411541
return nil
15421542
}
1543+
1544+
func isDNSNameResolverEnabled() bool {
1545+
val, present := os.LookupEnv("OVN_ENABLE_DNSNAMERESOLVER")
1546+
return present && val == "true"
1547+
}

0 commit comments

Comments
 (0)