Skip to content

Commit

Permalink
ipv6 support for node-local-dns (#10707)
Browse files Browse the repository at this point in the history
Co-authored-by: docktofuture <[email protected]>
  • Loading branch information
gardener-ci-robot and DockToFuture authored Oct 23, 2024
1 parent 3e11034 commit 8bafcc6
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 20 deletions.
2 changes: 2 additions & 0 deletions pkg/component/networking/nodelocaldns/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package constants
const (
// IPVSAddress is the IPv4 address used by node-local-dns when IPVS is used.
IPVSAddress = "169.254.20.10"
// IPVSIPv6Address is the IPv6 address used by node-local-dns when IPVS is used.
IPVSIPv6Address = "fd30:1319:f1e:230b::1"
// LabelValue is the value of a label used for the identification of node-local-dns pods.
LabelValue = "node-local-dns"
)
13 changes: 13 additions & 0 deletions pkg/component/networking/nodelocaldns/mock/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 43 additions & 8 deletions pkg/component/networking/nodelocaldns/nodelocaldns.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package nodelocaldns

import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
Expand All @@ -19,6 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
vpaautoscalingv1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -64,6 +67,7 @@ type Interface interface {
component.DeployWaiter
SetClusterDNS([]string)
SetDNSServers([]string)
SetIPFamilies([]gardencorev1beta1.IPFamily)
}

// Values is a set of configuration values for the node-local-dns component.
Expand All @@ -80,6 +84,8 @@ type Values struct {
DNSServers []string
// KubernetesVersion is the Kubernetes version of the Shoot.
KubernetesVersion *semver.Version
// IPFamilies specifies the IP protocol versions to use for node local dns.
IPFamilies []gardencorev1beta1.IPFamily
}

// New creates a new instance of DeployWaiter for node-local-dns.
Expand Down Expand Up @@ -189,9 +195,12 @@ func (n *nodeLocalDNS) WaitCleanup(ctx context.Context) error {
}

func (n *nodeLocalDNS) computeResourcesData() (map[string][]byte, error) {
var (
registry = managedresources.NewRegistry(kubernetes.ShootScheme, kubernetes.ShootCodec, kubernetes.ShootSerializer)
if n.getHealthAddress() == "" {
return nil, errors.New("empty IPVSAddress")
}

var (
registry = managedresources.NewRegistry(kubernetes.ShootScheme, kubernetes.ShootCodec, kubernetes.ShootSerializer)
serviceAccount = &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: "node-local-dns",
Expand Down Expand Up @@ -222,7 +231,7 @@ func (n *nodeLocalDNS) computeResourcesData() (map[string][]byte, error) {
` + n.forceTcpToClusterDNS() + `
}
prometheus :` + strconv.Itoa(prometheusPort) + `
health ` + nodelocaldnsconstants.IPVSAddress + `:` + strconv.Itoa(livenessProbePort) + `
health ` + n.getHealthAddress() + `:` + strconv.Itoa(livenessProbePort) + `
}
in-addr.arpa:53 {
errors
Expand Down Expand Up @@ -404,7 +413,7 @@ ip6.arpa:53 {
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Host: nodelocaldnsconstants.IPVSAddress,
Host: n.getIPVSAddress(),
Path: "/health",
Port: intstr.FromInt32(livenessProbePort),
},
Expand Down Expand Up @@ -513,16 +522,16 @@ ip6.arpa:53 {

func (n *nodeLocalDNS) bindIP() string {
if len(n.values.DNSServers) > 0 {
return nodelocaldnsconstants.IPVSAddress + " " + strings.Join(n.values.DNSServers, " ")
return n.getIPVSAddress() + " " + strings.Join(n.values.DNSServers, " ")
}
return nodelocaldnsconstants.IPVSAddress
return n.getIPVSAddress()
}

func (n *nodeLocalDNS) containerArg() string {
if len(n.values.DNSServers) > 0 {
return nodelocaldnsconstants.IPVSAddress + "," + strings.Join(n.values.DNSServers, ",")
return n.getIPVSAddress() + "," + strings.Join(n.values.DNSServers, ",")
}
return nodelocaldnsconstants.IPVSAddress
return n.getIPVSAddress()
}

func (n *nodeLocalDNS) forceTcpToClusterDNS() string {
Expand Down Expand Up @@ -557,3 +566,29 @@ func (n *nodeLocalDNS) SetClusterDNS(dns []string) {
func (n *nodeLocalDNS) SetDNSServers(servers []string) {
n.values.DNSServers = servers
}

func (n *nodeLocalDNS) SetIPFamilies(ipfamilies []gardencorev1beta1.IPFamily) {
n.values.IPFamilies = ipfamilies
}

func (n *nodeLocalDNS) getIPVSAddress() (ipvsAddress string) {
return n.getAddress(false)
}

func (n *nodeLocalDNS) getHealthAddress() (healthAddress string) {
return n.getAddress(true)
}

func (n *nodeLocalDNS) getAddress(useIPv6Brackets bool) string {
ipFamiliesSet := sets.New[gardencorev1beta1.IPFamily](n.values.IPFamilies...)
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) {
return nodelocaldnsconstants.IPVSAddress
}
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) {
if useIPv6Brackets {
return fmt.Sprintf("[%s]", nodelocaldnsconstants.IPVSIPv6Address)
}
return nodelocaldnsconstants.IPVSIPv6Address
}
return ""
}
105 changes: 96 additions & 9 deletions pkg/component/networking/nodelocaldns/nodelocaldns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
Expand Down Expand Up @@ -226,6 +227,7 @@ var _ = Describe("NodeLocalDNS", func() {
values = Values{
Image: image,
KubernetesVersion: semver.MustParse("1.26.1"),
IPFamilies: []gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv4},
}

managedResource = &resourcesv1alpha1.ManagedResource{
Expand Down Expand Up @@ -270,7 +272,7 @@ data:
` + forceTcpToClusterDNS + `
}
prometheus :` + strconv.Itoa(prometheusPort) + `
health ` + ipvsAddress + `:` + strconv.Itoa(livenessProbePort) + `
health ` + healthAddress(values) + `:` + strconv.Itoa(livenessProbePort) + `
}
in-addr.arpa:53 {
errors
Expand Down Expand Up @@ -620,7 +622,7 @@ status: {}
` + forceTcpToClusterDNS + `
}
prometheus :` + strconv.Itoa(prometheusPort) + `
health ` + ipvsAddress + `:` + strconv.Itoa(livenessProbePort) + `
health ` + healthAddress(values) + `:` + strconv.Itoa(livenessProbePort) + `
}
in-addr.arpa:53 {
errors
Expand Down Expand Up @@ -892,7 +894,7 @@ ip6.arpa:53 {
` + forceTcpToClusterDNS + `
}
prometheus :` + strconv.Itoa(prometheusPort) + `
health ` + ipvsAddress + `:` + strconv.Itoa(livenessProbePort) + `
health ` + healthAddress(values) + `:` + strconv.Itoa(livenessProbePort) + `
}
in-addr.arpa:53 {
errors
Expand Down Expand Up @@ -1110,6 +1112,57 @@ ip6.arpa:53 {
})
})
})

Context("With IPv6:", func() {
BeforeEach(func() {
values.IPFamilies = []gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv6}
values.Config = &gardencorev1beta1.NodeLocalDNS{Enabled: true,
ForceTCPToClusterDNS: ptr.To(false),
ForceTCPToUpstreamDNS: ptr.To(false),
DisableForwardToUpstreamDNS: ptr.To(false),
}
forceTcpToClusterDNS = "prefer_udp"
forceTcpToUpstreamDNS = "prefer_udp"
ipvsAddress = "fd30:1319:f1e:230b::1"
})

Context("w/o VPA", func() {
BeforeEach(func() {
values.VPAEnabled = false
values.IPFamilies = []gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv6}
})

It("should successfully deploy all resources", func() {
expectedManifests = nil
expectedManifests = append(expectedManifests, configMapYAMLFor())
Expect(manifests).To(ContainElements(expectedManifests))
managedResourceDaemonset, err := extractDaemonSet(manifests, kubernetes.ShootCodec.UniversalDeserializer())
Expect(err).ToNot(HaveOccurred())
daemonset := daemonSetYAMLFor()
utilruntime.Must(references.InjectAnnotations(daemonset))
Expect(daemonset).To(DeepEqual(managedResourceDaemonset))
})
})

Context("w/ VPA", func() {
BeforeEach(func() {
values.VPAEnabled = true
values.IPFamilies = []gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv6}

})

It("should successfully deploy all resources", func() {
expectedManifests = append(expectedManifests, configMapYAMLFor(), vpaYAML)
Expect(manifests).To(ContainElements(expectedManifests))

managedResourceDaemonset, err := extractDaemonSet(manifests, kubernetes.ShootCodec.UniversalDeserializer())
Expect(err).ToNot(HaveOccurred())
daemonset := daemonSetYAMLFor()
utilruntime.Must(references.InjectAnnotations(daemonset))
Expect(daemonset).To(DeepEqual(managedResourceDaemonset))
})
})
})
})
})
})
Expand Down Expand Up @@ -1231,18 +1284,52 @@ ip6.arpa:53 {

})

func healthAddress(values Values) string {
ipFamiliesSet := sets.New[gardencorev1beta1.IPFamily](values.IPFamilies...)
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) {
return "169.254.20.10"
}
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) {
if len(values.DNSServers) > 0 {
return "fd30:1319:f1e:230b::1 " + strings.Join(values.DNSServers, " ")
}
return "[fd30:1319:f1e:230b::1]"
}
return ""
}

func bindIP(values Values) string {
if len(values.DNSServers) > 0 {
return "169.254.20.10 " + strings.Join(values.DNSServers, " ")
ipFamiliesSet := sets.New[gardencorev1beta1.IPFamily](values.IPFamilies...)
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) {
if len(values.DNSServers) > 0 {
return "169.254.20.10 " + strings.Join(values.DNSServers, " ")
}
return "169.254.20.10"
}
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) {
if len(values.DNSServers) > 0 {
return "fd30:1319:f1e:230b::1 " + strings.Join(values.DNSServers, " ")
}
return "fd30:1319:f1e:230b::1"
}
return "169.254.20.10"
return ""
}

func containerArg(values Values) string {
if len(values.DNSServers) > 0 {
return "169.254.20.10," + strings.Join(values.DNSServers, ",")
ipFamiliesSet := sets.New[gardencorev1beta1.IPFamily](values.IPFamilies...)
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) {
if len(values.DNSServers) > 0 {
return "169.254.20.10," + strings.Join(values.DNSServers, ",")
}
return "169.254.20.10"
}
if ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv6) && !ipFamiliesSet.Has(gardencorev1beta1.IPFamilyIPv4) {
if len(values.DNSServers) > 0 {
return "fd30:1319:f1e:230b::1," + strings.Join(values.DNSServers, ",")
}
return "fd30:1319:f1e:230b::1"
}
return "169.254.20.10"
return ""
}

func extractDaemonSet(manifests []string, decoder runtime.Decoder) (*appsv1.DaemonSet, error) {
Expand Down
8 changes: 7 additions & 1 deletion pkg/controller/networkpolicy/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,10 +490,16 @@ func (r *Reconciler) reconcileNetworkPolicyAllowToDNS(ctx context.Context, log l
// required for node local dns feature, allows egress traffic to node local dns cache
{
IPBlock: &networkingv1.IPBlock{
// node local dns feature is only supported for shoots with IPv4 single-stack networking
// node local dns feature is only supported for shoots with IPv4 or IPv6 single-stack networking
CIDR: fmt.Sprintf("%s/32", nodelocaldnsconstants.IPVSAddress),
},
},
{
IPBlock: &networkingv1.IPBlock{
// node local dns feature is only supported for shoots with IPv4 or IPv6 single-stack networking
CIDR: fmt.Sprintf("%s/128", nodelocaldnsconstants.IPVSIPv6Address),
},
},
},
Ports: []networkingv1.NetworkPolicyPort{
{Protocol: ptr.To(corev1.ProtocolUDP), Port: ptr.To(intstr.FromInt32(corednsconstants.PortServiceServer))},
Expand Down
2 changes: 1 addition & 1 deletion pkg/gardenlet/operation/botanist/nodelocaldns.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (b *Botanist) ReconcileNodeLocalDNS(ctx context.Context) error {
}
b.Shoot.Components.SystemComponents.NodeLocalDNS.SetClusterDNS(clusterDNS)
b.Shoot.Components.SystemComponents.NodeLocalDNS.SetDNSServers(dnsServers)

b.Shoot.Components.SystemComponents.NodeLocalDNS.SetIPFamilies(b.Shoot.GetInfo().Spec.Networking.IPFamilies)
if b.Shoot.NodeLocalDNSEnabled {
return b.Shoot.Components.SystemComponents.NodeLocalDNS.Deploy(ctx)
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/gardenlet/operation/botanist/nodelocaldns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var _ = Describe("NodeLocalDNS", func() {
Kubernetes: gardencorev1beta1.Kubernetes{
Version: "1.26.1",
},
Networking: &gardencorev1beta1.Networking{IPFamilies: []gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv4}},
},
})
})
Expand Down Expand Up @@ -105,12 +106,33 @@ var _ = Describe("NodeLocalDNS", func() {
})

It("should fail when the deploy function fails", func() {
nodelocaldns.EXPECT().SetIPFamilies([]gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv4})
nodelocaldns.EXPECT().Deploy(ctx).Return(fakeErr)

Expect(botanist.ReconcileNodeLocalDNS(ctx)).To(MatchError(fakeErr))
})

It("should successfully deploy when enabled", func() {
nodelocaldns.EXPECT().SetIPFamilies([]gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv4})
nodelocaldns.EXPECT().Deploy(ctx)

Expect(botanist.ReconcileNodeLocalDNS(ctx)).To(Succeed())
})
It("should successfully deploy when enabled with ipfamily IPv6", func() {
botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{
Spec: gardencorev1beta1.ShootSpec{
SystemComponents: &gardencorev1beta1.SystemComponents{
NodeLocalDNS: &gardencorev1beta1.NodeLocalDNS{
Enabled: true,
},
},
Kubernetes: gardencorev1beta1.Kubernetes{
Version: "1.26.1",
},
Networking: &gardencorev1beta1.Networking{IPFamilies: []gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv6}},
},
})
nodelocaldns.EXPECT().SetIPFamilies([]gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv6})
nodelocaldns.EXPECT().Deploy(ctx)

Expect(botanist.ReconcileNodeLocalDNS(ctx)).To(Succeed())
Expand All @@ -119,6 +141,7 @@ var _ = Describe("NodeLocalDNS", func() {
Context("node-local-dns disabled", func() {
BeforeEach(func() {
botanist.Shoot.NodeLocalDNSEnabled = false
nodelocaldns.EXPECT().SetIPFamilies([]gardencorev1beta1.IPFamily{gardencorev1beta1.IPFamilyIPv4})
})

Context("but still node with label existing", func() {
Expand Down
Loading

0 comments on commit 8bafcc6

Please sign in to comment.