mirror of https://github.com/k3s-io/k3s
Merge pull request #55651 from MrHohn/kubelet-dns-pkg
Automatic merge from submit-queue (batch tested with PRs 55657, 54758, 47584, 55758, 55651). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Move DNS related kubelet codes into its own package **What this PR does / why we need it**: Ref https://github.com/kubernetes/features/issues/504, this PR rearranges DNS related kubelet codes into its own pacakge and adds an OWNERS file. Again, there is no functional changes, just that codes are moved around and couple fields (`clusterDomain`, `clusterDNS`, `resolverConfig`) are replaced with a `dnsConfigurer` struct. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #NONE **Special notes for your reviewer**: /assign @bowei @thockin **Release note**: ```release-note NONE ```pull/6/head
commit
6e950cc629
|
@ -66,6 +66,7 @@ go_library(
|
||||||
"//pkg/kubelet/metrics:go_default_library",
|
"//pkg/kubelet/metrics:go_default_library",
|
||||||
"//pkg/kubelet/mountpod:go_default_library",
|
"//pkg/kubelet/mountpod:go_default_library",
|
||||||
"//pkg/kubelet/network:go_default_library",
|
"//pkg/kubelet/network:go_default_library",
|
||||||
|
"//pkg/kubelet/network/dns:go_default_library",
|
||||||
"//pkg/kubelet/pleg:go_default_library",
|
"//pkg/kubelet/pleg:go_default_library",
|
||||||
"//pkg/kubelet/pod:go_default_library",
|
"//pkg/kubelet/pod:go_default_library",
|
||||||
"//pkg/kubelet/preemption:go_default_library",
|
"//pkg/kubelet/preemption:go_default_library",
|
||||||
|
|
|
@ -77,6 +77,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/network/dns"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/pleg"
|
"k8s.io/kubernetes/pkg/kubelet/pleg"
|
||||||
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/preemption"
|
"k8s.io/kubernetes/pkg/kubelet/preemption"
|
||||||
|
@ -477,6 +478,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
httpClient := &http.Client{}
|
httpClient := &http.Client{}
|
||||||
|
parsedNodeIP := net.ParseIP(nodeIP)
|
||||||
|
|
||||||
klet := &Kubelet{
|
klet := &Kubelet{
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
|
@ -488,8 +490,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||||
sourcesReady: config.NewSourcesReady(kubeDeps.PodConfig.SeenAllSources),
|
sourcesReady: config.NewSourcesReady(kubeDeps.PodConfig.SeenAllSources),
|
||||||
registerNode: kubeCfg.RegisterNode,
|
registerNode: kubeCfg.RegisterNode,
|
||||||
registerSchedulable: registerSchedulable,
|
registerSchedulable: registerSchedulable,
|
||||||
clusterDomain: kubeCfg.ClusterDomain,
|
dnsConfigurer: dns.NewConfigurer(kubeDeps.Recorder, nodeRef, parsedNodeIP, clusterDNS, kubeCfg.ClusterDomain, kubeCfg.ResolverConfig),
|
||||||
clusterDNS: clusterDNS,
|
|
||||||
serviceLister: serviceLister,
|
serviceLister: serviceLister,
|
||||||
nodeInfo: nodeInfo,
|
nodeInfo: nodeInfo,
|
||||||
masterServiceNamespace: masterServiceNamespace,
|
masterServiceNamespace: masterServiceNamespace,
|
||||||
|
@ -512,11 +513,10 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||||
maxPods: int(kubeCfg.MaxPods),
|
maxPods: int(kubeCfg.MaxPods),
|
||||||
podsPerCore: int(kubeCfg.PodsPerCore),
|
podsPerCore: int(kubeCfg.PodsPerCore),
|
||||||
syncLoopMonitor: atomic.Value{},
|
syncLoopMonitor: atomic.Value{},
|
||||||
resolverConfig: kubeCfg.ResolverConfig,
|
|
||||||
daemonEndpoints: daemonEndpoints,
|
daemonEndpoints: daemonEndpoints,
|
||||||
containerManager: kubeDeps.ContainerManager,
|
containerManager: kubeDeps.ContainerManager,
|
||||||
containerRuntimeName: containerRuntime,
|
containerRuntimeName: containerRuntime,
|
||||||
nodeIP: net.ParseIP(nodeIP),
|
nodeIP: parsedNodeIP,
|
||||||
clock: clock.RealClock{},
|
clock: clock.RealClock{},
|
||||||
enableControllerAttachDetach: kubeCfg.EnableControllerAttachDetach,
|
enableControllerAttachDetach: kubeCfg.EnableControllerAttachDetach,
|
||||||
iptClient: utilipt.New(utilexec.New(), utildbus.New(), utilipt.ProtocolIpv4),
|
iptClient: utilipt.New(utilexec.New(), utildbus.New(), utilipt.ProtocolIpv4),
|
||||||
|
@ -806,7 +806,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||||
experimentalCheckNodeCapabilitiesBeforeMount = false
|
experimentalCheckNodeCapabilitiesBeforeMount = false
|
||||||
// Replace the nameserver in containerized-mounter's rootfs/etc/resolve.conf with kubelet.ClusterDNS
|
// Replace the nameserver in containerized-mounter's rootfs/etc/resolve.conf with kubelet.ClusterDNS
|
||||||
// so that service name could be resolved
|
// so that service name could be resolved
|
||||||
klet.setupDNSinContainerizedMounter(experimentalMounterPath)
|
klet.dnsConfigurer.SetupDNSinContainerizedMounter(experimentalMounterPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup volumeManager
|
// setup volumeManager
|
||||||
|
@ -948,11 +948,8 @@ type Kubelet struct {
|
||||||
// for internal book keeping; access only from within registerWithApiserver
|
// for internal book keeping; access only from within registerWithApiserver
|
||||||
registrationCompleted bool
|
registrationCompleted bool
|
||||||
|
|
||||||
// If non-empty, use this for container DNS search.
|
// dnsConfigurer is used for setting up DNS resolver configuration when launching pods.
|
||||||
clusterDomain string
|
dnsConfigurer *dns.Configurer
|
||||||
|
|
||||||
// If non-nil, use this for container DNS server.
|
|
||||||
clusterDNS []net.IP
|
|
||||||
|
|
||||||
// masterServiceNamespace is the namespace that the master service is exposed in.
|
// masterServiceNamespace is the namespace that the master service is exposed in.
|
||||||
masterServiceNamespace string
|
masterServiceNamespace string
|
||||||
|
@ -1097,11 +1094,6 @@ type Kubelet struct {
|
||||||
// Channel for sending pods to kill.
|
// Channel for sending pods to kill.
|
||||||
podKillingCh chan *kubecontainer.PodPair
|
podKillingCh chan *kubecontainer.PodPair
|
||||||
|
|
||||||
// The configuration file used as the base to generate the container's
|
|
||||||
// DNS resolver configuration file. This can be used in conjunction with
|
|
||||||
// clusterDomain and clusterDNS.
|
|
||||||
resolverConfig string
|
|
||||||
|
|
||||||
// Information about the ports which are opened by daemons on Node running this Kubelet server.
|
// Information about the ports which are opened by daemons on Node running this Kubelet server.
|
||||||
daemonEndpoints *v1.NodeDaemonEndpoints
|
daemonEndpoints *v1.NodeDaemonEndpoints
|
||||||
|
|
||||||
|
@ -1375,8 +1367,8 @@ func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) {
|
||||||
go wait.Until(kl.podKiller, 1*time.Second, wait.NeverStop)
|
go wait.Until(kl.podKiller, 1*time.Second, wait.NeverStop)
|
||||||
|
|
||||||
// Start gorouting responsible for checking limits in resolv.conf
|
// Start gorouting responsible for checking limits in resolv.conf
|
||||||
if kl.resolverConfig != "" {
|
if kl.dnsConfigurer.ResolverConfig != "" {
|
||||||
go wait.Until(func() { kl.checkLimitsForResolvConf() }, 30*time.Second, wait.NeverStop)
|
go wait.Until(func() { kl.dnsConfigurer.CheckLimitsForResolvConf() }, 30*time.Second, wait.NeverStop)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start component sync loops.
|
// Start component sync loops.
|
||||||
|
|
|
@ -18,11 +18,6 @@ package kubelet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
@ -31,7 +26,6 @@ import (
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
|
||||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -164,256 +158,6 @@ func (kl *Kubelet) providerRequiresNetworkingConfiguration() bool {
|
||||||
return supported
|
return supported
|
||||||
}
|
}
|
||||||
|
|
||||||
func omitDuplicates(pod *v1.Pod, combinedSearch []string) []string {
|
|
||||||
uniqueDomains := map[string]bool{}
|
|
||||||
|
|
||||||
for _, dnsDomain := range combinedSearch {
|
|
||||||
if _, exists := uniqueDomains[dnsDomain]; !exists {
|
|
||||||
combinedSearch[len(uniqueDomains)] = dnsDomain
|
|
||||||
uniqueDomains[dnsDomain] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinedSearch[:len(uniqueDomains)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *Kubelet) formDNSSearchFitsLimits(pod *v1.Pod, composedSearch []string) []string {
|
|
||||||
// resolver file Search line current limitations
|
|
||||||
resolvSearchLineDNSDomainsLimit := 6
|
|
||||||
resolvSearchLineLenLimit := 255
|
|
||||||
limitsExceeded := false
|
|
||||||
|
|
||||||
if len(composedSearch) > resolvSearchLineDNSDomainsLimit {
|
|
||||||
composedSearch = composedSearch[:resolvSearchLineDNSDomainsLimit]
|
|
||||||
limitsExceeded = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if resolvSearchhLineStrLen := len(strings.Join(composedSearch, " ")); resolvSearchhLineStrLen > resolvSearchLineLenLimit {
|
|
||||||
cutDomainsNum := 0
|
|
||||||
cutDoaminsLen := 0
|
|
||||||
for i := len(composedSearch) - 1; i >= 0; i-- {
|
|
||||||
cutDoaminsLen += len(composedSearch[i]) + 1
|
|
||||||
cutDomainsNum++
|
|
||||||
|
|
||||||
if (resolvSearchhLineStrLen - cutDoaminsLen) <= resolvSearchLineLenLimit {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
composedSearch = composedSearch[:(len(composedSearch) - cutDomainsNum)]
|
|
||||||
limitsExceeded = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if limitsExceeded {
|
|
||||||
log := fmt.Sprintf("Search Line limits were exceeded, some dns names have been omitted, the applied search line is: %s", strings.Join(composedSearch, " "))
|
|
||||||
kl.recorder.Event(pod, v1.EventTypeWarning, "DNSSearchForming", log)
|
|
||||||
glog.Error(log)
|
|
||||||
}
|
|
||||||
return composedSearch
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *Kubelet) formDNSSearchForDNSDefault(hostSearch []string, pod *v1.Pod) []string {
|
|
||||||
return kl.formDNSSearchFitsLimits(pod, hostSearch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *Kubelet) formDNSSearch(hostSearch []string, pod *v1.Pod) []string {
|
|
||||||
if kl.clusterDomain == "" {
|
|
||||||
kl.formDNSSearchFitsLimits(pod, hostSearch)
|
|
||||||
return hostSearch
|
|
||||||
}
|
|
||||||
|
|
||||||
nsSvcDomain := fmt.Sprintf("%s.svc.%s", pod.Namespace, kl.clusterDomain)
|
|
||||||
svcDomain := fmt.Sprintf("svc.%s", kl.clusterDomain)
|
|
||||||
dnsSearch := []string{nsSvcDomain, svcDomain, kl.clusterDomain}
|
|
||||||
|
|
||||||
combinedSearch := append(dnsSearch, hostSearch...)
|
|
||||||
|
|
||||||
combinedSearch = omitDuplicates(pod, combinedSearch)
|
|
||||||
return kl.formDNSSearchFitsLimits(pod, combinedSearch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kl *Kubelet) checkLimitsForResolvConf() {
|
|
||||||
// resolver file Search line current limitations
|
|
||||||
resolvSearchLineDNSDomainsLimit := 6
|
|
||||||
resolvSearchLineLenLimit := 255
|
|
||||||
|
|
||||||
f, err := os.Open(kl.resolverConfig)
|
|
||||||
if err != nil {
|
|
||||||
kl.recorder.Event(kl.nodeRef, v1.EventTypeWarning, "checkLimitsForResolvConf", err.Error())
|
|
||||||
glog.Error("checkLimitsForResolvConf: " + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
_, hostSearch, _, err := kl.parseResolvConf(f)
|
|
||||||
if err != nil {
|
|
||||||
kl.recorder.Event(kl.nodeRef, v1.EventTypeWarning, "checkLimitsForResolvConf", err.Error())
|
|
||||||
glog.Error("checkLimitsForResolvConf: " + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
domainCntLimit := resolvSearchLineDNSDomainsLimit
|
|
||||||
|
|
||||||
if kl.clusterDomain != "" {
|
|
||||||
domainCntLimit -= 3
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hostSearch) > domainCntLimit {
|
|
||||||
log := fmt.Sprintf("Resolv.conf file '%s' contains search line consisting of more than %d domains!", kl.resolverConfig, domainCntLimit)
|
|
||||||
kl.recorder.Event(kl.nodeRef, v1.EventTypeWarning, "checkLimitsForResolvConf", log)
|
|
||||||
glog.Error("checkLimitsForResolvConf: " + log)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(strings.Join(hostSearch, " ")) > resolvSearchLineLenLimit {
|
|
||||||
log := fmt.Sprintf("Resolv.conf file '%s' contains search line which length is more than allowed %d chars!", kl.resolverConfig, resolvSearchLineLenLimit)
|
|
||||||
kl.recorder.Event(kl.nodeRef, v1.EventTypeWarning, "checkLimitsForResolvConf", log)
|
|
||||||
glog.Error("checkLimitsForResolvConf: " + log)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseResolveConf reads a resolv.conf file from the given reader, and parses
|
|
||||||
// it into nameservers, searches and options, possibly returning an error.
|
|
||||||
// TODO: move to utility package
|
|
||||||
func (kl *Kubelet) parseResolvConf(reader io.Reader) (nameservers []string, searches []string, options []string, err error) {
|
|
||||||
file, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lines of the form "nameserver 1.2.3.4" accumulate.
|
|
||||||
nameservers = []string{}
|
|
||||||
|
|
||||||
// Lines of the form "search example.com" overrule - last one wins.
|
|
||||||
searches = []string{}
|
|
||||||
|
|
||||||
// Lines of the form "option ndots:5 attempts:2" overrule - last one wins.
|
|
||||||
// Each option is recorded as an element in the array.
|
|
||||||
options = []string{}
|
|
||||||
|
|
||||||
lines := strings.Split(string(file), "\n")
|
|
||||||
for l := range lines {
|
|
||||||
trimmed := strings.TrimSpace(lines[l])
|
|
||||||
if strings.HasPrefix(trimmed, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields := strings.Fields(trimmed)
|
|
||||||
if len(fields) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fields[0] == "nameserver" && len(fields) >= 2 {
|
|
||||||
nameservers = append(nameservers, fields[1])
|
|
||||||
}
|
|
||||||
if fields[0] == "search" {
|
|
||||||
searches = fields[1:]
|
|
||||||
}
|
|
||||||
if fields[0] == "options" {
|
|
||||||
options = fields[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There used to be code here to scrub DNS for each cloud, but doesn't
|
|
||||||
// make sense anymore since cloudproviders are being factored out.
|
|
||||||
// contact @thockin or @wlan0 for more information
|
|
||||||
|
|
||||||
return nameservers, searches, options, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClusterDNS returns a list of the DNS servers, a list of the DNS search
|
|
||||||
// domains of the cluster, and a list of resolv.conf options.
|
|
||||||
func (kl *Kubelet) GetClusterDNS(pod *v1.Pod) ([]string, []string, []string, bool, error) {
|
|
||||||
var hostDNS, hostSearch, hostOptions []string
|
|
||||||
// Get host DNS settings
|
|
||||||
if kl.resolverConfig != "" {
|
|
||||||
f, err := os.Open(kl.resolverConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, false, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
hostDNS, hostSearch, hostOptions, err = kl.parseResolvConf(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
useClusterFirstPolicy := ((pod.Spec.DNSPolicy == v1.DNSClusterFirst && !kubecontainer.IsHostNetworkPod(pod)) || pod.Spec.DNSPolicy == v1.DNSClusterFirstWithHostNet)
|
|
||||||
if useClusterFirstPolicy && len(kl.clusterDNS) == 0 {
|
|
||||||
// clusterDNS is not known.
|
|
||||||
// pod with ClusterDNSFirst Policy cannot be created
|
|
||||||
kl.recorder.Eventf(pod, v1.EventTypeWarning, "MissingClusterDNS", "kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. Falling back to DNSDefault policy.", pod.Spec.DNSPolicy)
|
|
||||||
log := fmt.Sprintf("kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. pod: %q. Falling back to DNSDefault policy.", pod.Spec.DNSPolicy, format.Pod(pod))
|
|
||||||
kl.recorder.Eventf(kl.nodeRef, v1.EventTypeWarning, "MissingClusterDNS", log)
|
|
||||||
|
|
||||||
// fallback to DNSDefault
|
|
||||||
useClusterFirstPolicy = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !useClusterFirstPolicy {
|
|
||||||
// When the kubelet --resolv-conf flag is set to the empty string, use
|
|
||||||
// DNS settings that override the docker default (which is to use
|
|
||||||
// /etc/resolv.conf) and effectively disable DNS lookups. According to
|
|
||||||
// the bind documentation, the behavior of the DNS client library when
|
|
||||||
// "nameservers" are not specified is to "use the nameserver on the
|
|
||||||
// local machine". A nameserver setting of localhost is equivalent to
|
|
||||||
// this documented behavior.
|
|
||||||
if kl.resolverConfig == "" {
|
|
||||||
hostSearch = []string{"."}
|
|
||||||
switch {
|
|
||||||
case kl.nodeIP == nil || kl.nodeIP.To4() != nil:
|
|
||||||
hostDNS = []string{"127.0.0.1"}
|
|
||||||
case kl.nodeIP.To16() != nil:
|
|
||||||
hostDNS = []string{"::1"}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hostSearch = kl.formDNSSearchForDNSDefault(hostSearch, pod)
|
|
||||||
}
|
|
||||||
return hostDNS, hostSearch, hostOptions, useClusterFirstPolicy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// for a pod with DNSClusterFirst policy, the cluster DNS server is the only nameserver configured for
|
|
||||||
// the pod. The cluster DNS server itself will forward queries to other nameservers that is configured to use,
|
|
||||||
// in case the cluster DNS server cannot resolve the DNS query itself
|
|
||||||
dns := make([]string, len(kl.clusterDNS))
|
|
||||||
for i, ip := range kl.clusterDNS {
|
|
||||||
dns[i] = ip.String()
|
|
||||||
}
|
|
||||||
dnsSearch := kl.formDNSSearch(hostSearch, pod)
|
|
||||||
|
|
||||||
return dns, dnsSearch, hostOptions, useClusterFirstPolicy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the nameserver in containerized-mounter's rootfs/etc/resolve.conf with kubelet.ClusterDNS
|
|
||||||
func (kl *Kubelet) setupDNSinContainerizedMounter(mounterPath string) {
|
|
||||||
resolvePath := filepath.Join(strings.TrimSuffix(mounterPath, "/mounter"), "rootfs", "etc", "resolv.conf")
|
|
||||||
dnsString := ""
|
|
||||||
for _, dns := range kl.clusterDNS {
|
|
||||||
dnsString = dnsString + fmt.Sprintf("nameserver %s\n", dns)
|
|
||||||
}
|
|
||||||
if kl.resolverConfig != "" {
|
|
||||||
f, err := os.Open(kl.resolverConfig)
|
|
||||||
defer f.Close()
|
|
||||||
if err != nil {
|
|
||||||
glog.Error("Could not open resolverConf file")
|
|
||||||
} else {
|
|
||||||
_, hostSearch, _, err := kl.parseResolvConf(f)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("Error for parsing the reslov.conf file: %v", err)
|
|
||||||
} else {
|
|
||||||
dnsString = dnsString + "search"
|
|
||||||
for _, search := range hostSearch {
|
|
||||||
dnsString = dnsString + fmt.Sprintf(" %s", search)
|
|
||||||
}
|
|
||||||
dnsString = dnsString + "\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(resolvePath, []byte(dnsString), 0600); err != nil {
|
|
||||||
glog.Errorf("Could not write dns nameserver in file %s, with error %v", resolvePath, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// syncNetworkStatus updates the network state
|
// syncNetworkStatus updates the network state
|
||||||
func (kl *Kubelet) syncNetworkStatus() {
|
func (kl *Kubelet) syncNetworkStatus() {
|
||||||
// For cri integration, network state will be updated in updateRuntimeUp,
|
// For cri integration, network state will be updated in updateRuntimeUp,
|
||||||
|
@ -536,3 +280,11 @@ func getIPTablesMark(bit int) string {
|
||||||
value := 1 << uint(bit)
|
value := 1 << uint(bit)
|
||||||
return fmt.Sprintf("%#08x/%#08x", value, value)
|
return fmt.Sprintf("%#08x/%#08x", value, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClusterDNS returns a list of the DNS servers, a list of the DNS search
|
||||||
|
// domains of the cluster, and a list of resolv.conf options.
|
||||||
|
// This function is defined in kubecontainer.RuntimeHelper interface so we
|
||||||
|
// have to implement it.
|
||||||
|
func (kl *Kubelet) GetClusterDNS(pod *v1.Pod) ([]string, []string, []string, bool, error) {
|
||||||
|
return kl.dnsConfigurer.GetClusterDNS(pod)
|
||||||
|
}
|
||||||
|
|
|
@ -19,13 +19,9 @@ package kubelet
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
"k8s.io/client-go/tools/record"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNetworkHostGetsPodNotFound(t *testing.T) {
|
func TestNetworkHostGetsPodNotFound(t *testing.T) {
|
||||||
|
@ -189,207 +185,6 @@ func TestNodeIPParam(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseResolvConf(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
data string
|
|
||||||
nameservers []string
|
|
||||||
searches []string
|
|
||||||
options []string
|
|
||||||
}{
|
|
||||||
{"", []string{}, []string{}, []string{}},
|
|
||||||
{" ", []string{}, []string{}, []string{}},
|
|
||||||
{"\n", []string{}, []string{}, []string{}},
|
|
||||||
{"\t\n\t", []string{}, []string{}, []string{}},
|
|
||||||
{"#comment\n", []string{}, []string{}, []string{}},
|
|
||||||
{" #comment\n", []string{}, []string{}, []string{}},
|
|
||||||
{"#comment\n#comment", []string{}, []string{}, []string{}},
|
|
||||||
{"#comment\nnameserver", []string{}, []string{}, []string{}},
|
|
||||||
{"#comment\nnameserver\nsearch", []string{}, []string{}, []string{}},
|
|
||||||
{"nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
|
||||||
{" nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
|
||||||
{"\tnameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
|
||||||
{"nameserver\t1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
|
||||||
{"nameserver \t 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
|
||||||
{"nameserver 1.2.3.4\nnameserver 5.6.7.8", []string{"1.2.3.4", "5.6.7.8"}, []string{}, []string{}},
|
|
||||||
{"nameserver 1.2.3.4 #comment", []string{"1.2.3.4"}, []string{}, []string{}},
|
|
||||||
{"search foo", []string{}, []string{"foo"}, []string{}},
|
|
||||||
{"search foo bar", []string{}, []string{"foo", "bar"}, []string{}},
|
|
||||||
{"search foo bar bat\n", []string{}, []string{"foo", "bar", "bat"}, []string{}},
|
|
||||||
{"search foo\nsearch bar", []string{}, []string{"bar"}, []string{}},
|
|
||||||
{"nameserver 1.2.3.4\nsearch foo bar", []string{"1.2.3.4"}, []string{"foo", "bar"}, []string{}},
|
|
||||||
{"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}, []string{}},
|
|
||||||
{"#comment\nnameserver 1.2.3.4\n#comment\nsearch foo\ncomment", []string{"1.2.3.4"}, []string{"foo"}, []string{}},
|
|
||||||
{"options ndots:5 attempts:2", []string{}, []string{}, []string{"ndots:5", "attempts:2"}},
|
|
||||||
{"options ndots:1\noptions ndots:5 attempts:3", []string{}, []string{}, []string{"ndots:5", "attempts:3"}},
|
|
||||||
{"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar\noptions ndots:5 attempts:4", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}, []string{"ndots:5", "attempts:4"}},
|
|
||||||
}
|
|
||||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
|
||||||
defer testKubelet.Cleanup()
|
|
||||||
kubelet := testKubelet.kubelet
|
|
||||||
for i, tc := range testCases {
|
|
||||||
ns, srch, opts, err := kubelet.parseResolvConf(strings.NewReader(tc.data))
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.EqualValues(t, tc.nameservers, ns, "test case [%d]: name servers", i)
|
|
||||||
assert.EqualValues(t, tc.searches, srch, "test case [%d] searches", i)
|
|
||||||
assert.EqualValues(t, tc.options, opts, "test case [%d] options", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComposeDNSSearch(t *testing.T) {
|
|
||||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
|
||||||
defer testKubelet.Cleanup()
|
|
||||||
kubelet := testKubelet.kubelet
|
|
||||||
|
|
||||||
recorder := record.NewFakeRecorder(20)
|
|
||||||
kubelet.recorder = recorder
|
|
||||||
|
|
||||||
pod := podWithUIDNameNs("", "test_pod", "testNS")
|
|
||||||
kubelet.clusterDomain = "TEST"
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
dnsNames []string
|
|
||||||
hostNames []string
|
|
||||||
resultSearch []string
|
|
||||||
events []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
|
||||||
[]string{},
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
|
||||||
[]string{},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
|
||||||
[]string{"AAA", "svc.TEST", "BBB", "TEST"},
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
|
|
||||||
[]string{},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
|
||||||
[]string{"AAA", strings.Repeat("B", 256), "BBB"},
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA"},
|
|
||||||
[]string{"Search Line limits were exceeded, some dns names have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA"},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
|
||||||
[]string{"AAA", "TEST", "BBB", "TEST", "CCC", "DDD"},
|
|
||||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC"},
|
|
||||||
[]string{
|
|
||||||
"Search Line limits were exceeded, some dns names have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA BBB CCC",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchEvent := func(recorder *record.FakeRecorder) string {
|
|
||||||
select {
|
|
||||||
case event := <-recorder.Events:
|
|
||||||
return event
|
|
||||||
default:
|
|
||||||
return "No more events!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
dnsSearch := kubelet.formDNSSearch(tc.hostNames, pod)
|
|
||||||
assert.EqualValues(t, tc.resultSearch, dnsSearch, "test [%d]", i)
|
|
||||||
for _, expectedEvent := range tc.events {
|
|
||||||
expected := fmt.Sprintf("%s %s %s", v1.EventTypeWarning, "DNSSearchForming", expectedEvent)
|
|
||||||
event := fetchEvent(recorder)
|
|
||||||
assert.Equal(t, expected, event, "test [%d]", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetClusterDNS(t *testing.T) {
|
|
||||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
|
||||||
defer testKubelet.Cleanup()
|
|
||||||
kubelet := testKubelet.kubelet
|
|
||||||
|
|
||||||
clusterNS := "203.0.113.1"
|
|
||||||
kubelet.clusterDomain = "kubernetes.io"
|
|
||||||
kubelet.clusterDNS = []net.IP{net.ParseIP(clusterNS)}
|
|
||||||
|
|
||||||
pods := newTestPods(4)
|
|
||||||
pods[0].Spec.DNSPolicy = v1.DNSClusterFirstWithHostNet
|
|
||||||
pods[1].Spec.DNSPolicy = v1.DNSClusterFirst
|
|
||||||
pods[2].Spec.DNSPolicy = v1.DNSClusterFirst
|
|
||||||
pods[2].Spec.HostNetwork = false
|
|
||||||
pods[3].Spec.DNSPolicy = v1.DNSDefault
|
|
||||||
|
|
||||||
options := make([]struct {
|
|
||||||
DNS []string
|
|
||||||
DNSSearch []string
|
|
||||||
}, 4)
|
|
||||||
for i, pod := range pods {
|
|
||||||
var err error
|
|
||||||
options[i].DNS, options[i].DNSSearch, _, _, err = kubelet.GetClusterDNS(pod)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to generate container options: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(options[0].DNS) != 1 || options[0].DNS[0] != clusterNS {
|
|
||||||
t.Errorf("expected nameserver %s, got %+v", clusterNS, options[0].DNS)
|
|
||||||
}
|
|
||||||
if len(options[0].DNSSearch) == 0 || options[0].DNSSearch[0] != ".svc."+kubelet.clusterDomain {
|
|
||||||
t.Errorf("expected search %s, got %+v", ".svc."+kubelet.clusterDomain, options[0].DNSSearch)
|
|
||||||
}
|
|
||||||
if len(options[1].DNS) != 1 || options[1].DNS[0] != "127.0.0.1" {
|
|
||||||
t.Errorf("expected nameserver 127.0.0.1, got %+v", options[1].DNS)
|
|
||||||
}
|
|
||||||
if len(options[1].DNSSearch) != 1 || options[1].DNSSearch[0] != "." {
|
|
||||||
t.Errorf("expected search \".\", got %+v", options[1].DNSSearch)
|
|
||||||
}
|
|
||||||
if len(options[2].DNS) != 1 || options[2].DNS[0] != clusterNS {
|
|
||||||
t.Errorf("expected nameserver %s, got %+v", clusterNS, options[2].DNS)
|
|
||||||
}
|
|
||||||
if len(options[2].DNSSearch) == 0 || options[2].DNSSearch[0] != ".svc."+kubelet.clusterDomain {
|
|
||||||
t.Errorf("expected search %s, got %+v", ".svc."+kubelet.clusterDomain, options[2].DNSSearch)
|
|
||||||
}
|
|
||||||
if len(options[3].DNS) != 1 || options[3].DNS[0] != "127.0.0.1" {
|
|
||||||
t.Errorf("expected nameserver 127.0.0.1, got %+v", options[3].DNS)
|
|
||||||
}
|
|
||||||
if len(options[3].DNSSearch) != 1 || options[3].DNSSearch[0] != "." {
|
|
||||||
t.Errorf("expected search \".\", got %+v", options[3].DNSSearch)
|
|
||||||
}
|
|
||||||
|
|
||||||
kubelet.resolverConfig = "/etc/resolv.conf"
|
|
||||||
for i, pod := range pods {
|
|
||||||
var err error
|
|
||||||
options[i].DNS, options[i].DNSSearch, _, _, err = kubelet.GetClusterDNS(pod)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to generate container options: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Logf("nameservers %+v", options[1].DNS)
|
|
||||||
if len(options[0].DNS) != 1 {
|
|
||||||
t.Errorf("expected cluster nameserver only, got %+v", options[0].DNS)
|
|
||||||
} else if options[0].DNS[0] != clusterNS {
|
|
||||||
t.Errorf("expected nameserver %s, got %v", clusterNS, options[0].DNS[0])
|
|
||||||
}
|
|
||||||
expLength := len(options[1].DNSSearch) + 3
|
|
||||||
if expLength > 6 {
|
|
||||||
expLength = 6
|
|
||||||
}
|
|
||||||
if len(options[0].DNSSearch) != expLength {
|
|
||||||
t.Errorf("expected prepend of cluster domain, got %+v", options[0].DNSSearch)
|
|
||||||
} else if options[0].DNSSearch[0] != ".svc."+kubelet.clusterDomain {
|
|
||||||
t.Errorf("expected domain %s, got %s", ".svc."+kubelet.clusterDomain, options[0].DNSSearch)
|
|
||||||
}
|
|
||||||
if len(options[2].DNS) != 1 {
|
|
||||||
t.Errorf("expected cluster nameserver only, got %+v", options[2].DNS)
|
|
||||||
} else if options[2].DNS[0] != clusterNS {
|
|
||||||
t.Errorf("expected nameserver %s, got %v", clusterNS, options[2].DNS[0])
|
|
||||||
}
|
|
||||||
if len(options[2].DNSSearch) != expLength {
|
|
||||||
t.Errorf("expected prepend of cluster domain, got %+v", options[2].DNSSearch)
|
|
||||||
} else if options[2].DNSSearch[0] != ".svc."+kubelet.clusterDomain {
|
|
||||||
t.Errorf("expected domain %s, got %s", ".svc."+kubelet.clusterDomain, options[0].DNSSearch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetIPTablesMark(t *testing.T) {
|
func TestGetIPTablesMark(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
bit int
|
bit int
|
||||||
|
|
|
@ -346,7 +346,7 @@ func truncatePodHostnameIfNeeded(podName, hostname string) (string, error) {
|
||||||
// given that pod's spec and annotations or returns an error.
|
// given that pod's spec and annotations or returns an error.
|
||||||
func (kl *Kubelet) GeneratePodHostNameAndDomain(pod *v1.Pod) (string, string, error) {
|
func (kl *Kubelet) GeneratePodHostNameAndDomain(pod *v1.Pod) (string, string, error) {
|
||||||
// TODO(vmarmol): Handle better.
|
// TODO(vmarmol): Handle better.
|
||||||
clusterDomain := kl.clusterDomain
|
clusterDomain := kl.dnsConfigurer.ClusterDomain
|
||||||
|
|
||||||
hostname := pod.Name
|
hostname := pod.Name
|
||||||
if len(pod.Spec.Hostname) > 0 {
|
if len(pod.Spec.Hostname) > 0 {
|
||||||
|
|
|
@ -41,6 +41,7 @@ filegroup(
|
||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
"//pkg/kubelet/network/cni:all-srcs",
|
"//pkg/kubelet/network/cni:all-srcs",
|
||||||
|
"//pkg/kubelet/network/dns:all-srcs",
|
||||||
"//pkg/kubelet/network/hairpin:all-srcs",
|
"//pkg/kubelet/network/hairpin:all-srcs",
|
||||||
"//pkg/kubelet/network/hostport:all-srcs",
|
"//pkg/kubelet/network/hostport:all-srcs",
|
||||||
"//pkg/kubelet/network/kubenet:all-srcs",
|
"//pkg/kubelet/network/kubenet:all-srcs",
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["dns.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/kubelet/network/dns",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/kubelet/container:go_default_library",
|
||||||
|
"//pkg/kubelet/util/format:go_default_library",
|
||||||
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["dns_test.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/kubelet/network/dns",
|
||||||
|
library = ":go_default_library",
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
|
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
approvers:
|
||||||
|
- sig-network-approvers
|
||||||
|
reviewers:
|
||||||
|
- sig-network-reviewers
|
|
@ -0,0 +1,314 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configurer is used for setting up DNS resolver configuration when launching pods.
|
||||||
|
type Configurer struct {
|
||||||
|
recorder record.EventRecorder
|
||||||
|
nodeRef *v1.ObjectReference
|
||||||
|
nodeIP net.IP
|
||||||
|
|
||||||
|
// If non-nil, use this for container DNS server.
|
||||||
|
clusterDNS []net.IP
|
||||||
|
// If non-empty, use this for container DNS search.
|
||||||
|
ClusterDomain string
|
||||||
|
// The path to the DNS resolver configuration file used as the base to generate
|
||||||
|
// the container's DNS resolver configuration file. This can be used in
|
||||||
|
// conjunction with clusterDomain and clusterDNS.
|
||||||
|
ResolverConfig string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfigurer returns a DNS configurer for launching pods.
|
||||||
|
func NewConfigurer(recorder record.EventRecorder, nodeRef *v1.ObjectReference, nodeIP net.IP, clusterDNS []net.IP, clusterDomain, resolverConfig string) *Configurer {
|
||||||
|
return &Configurer{
|
||||||
|
recorder: recorder,
|
||||||
|
nodeRef: nodeRef,
|
||||||
|
nodeIP: nodeIP,
|
||||||
|
clusterDNS: clusterDNS,
|
||||||
|
ClusterDomain: clusterDomain,
|
||||||
|
ResolverConfig: resolverConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func omitDuplicates(pod *v1.Pod, combinedSearch []string) []string {
|
||||||
|
uniqueDomains := map[string]bool{}
|
||||||
|
|
||||||
|
for _, dnsDomain := range combinedSearch {
|
||||||
|
if _, exists := uniqueDomains[dnsDomain]; !exists {
|
||||||
|
combinedSearch[len(uniqueDomains)] = dnsDomain
|
||||||
|
uniqueDomains[dnsDomain] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combinedSearch[:len(uniqueDomains)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configurer) formDNSSearchFitsLimits(pod *v1.Pod, composedSearch []string) []string {
|
||||||
|
// resolver file Search line current limitations
|
||||||
|
resolvSearchLineDNSDomainsLimit := 6
|
||||||
|
resolvSearchLineLenLimit := 255
|
||||||
|
limitsExceeded := false
|
||||||
|
|
||||||
|
if len(composedSearch) > resolvSearchLineDNSDomainsLimit {
|
||||||
|
composedSearch = composedSearch[:resolvSearchLineDNSDomainsLimit]
|
||||||
|
limitsExceeded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if resolvSearchhLineStrLen := len(strings.Join(composedSearch, " ")); resolvSearchhLineStrLen > resolvSearchLineLenLimit {
|
||||||
|
cutDomainsNum := 0
|
||||||
|
cutDoaminsLen := 0
|
||||||
|
for i := len(composedSearch) - 1; i >= 0; i-- {
|
||||||
|
cutDoaminsLen += len(composedSearch[i]) + 1
|
||||||
|
cutDomainsNum++
|
||||||
|
|
||||||
|
if (resolvSearchhLineStrLen - cutDoaminsLen) <= resolvSearchLineLenLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
composedSearch = composedSearch[:(len(composedSearch) - cutDomainsNum)]
|
||||||
|
limitsExceeded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if limitsExceeded {
|
||||||
|
log := fmt.Sprintf("Search Line limits were exceeded, some dns names have been omitted, the applied search line is: %s", strings.Join(composedSearch, " "))
|
||||||
|
c.recorder.Event(pod, v1.EventTypeWarning, "DNSSearchForming", log)
|
||||||
|
glog.Error(log)
|
||||||
|
}
|
||||||
|
return composedSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configurer) formDNSSearchForDNSDefault(hostSearch []string, pod *v1.Pod) []string {
|
||||||
|
return c.formDNSSearchFitsLimits(pod, hostSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Configurer) formDNSSearch(hostSearch []string, pod *v1.Pod) []string {
|
||||||
|
if c.ClusterDomain == "" {
|
||||||
|
c.formDNSSearchFitsLimits(pod, hostSearch)
|
||||||
|
return hostSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSvcDomain := fmt.Sprintf("%s.svc.%s", pod.Namespace, c.ClusterDomain)
|
||||||
|
svcDomain := fmt.Sprintf("svc.%s", c.ClusterDomain)
|
||||||
|
dnsSearch := []string{nsSvcDomain, svcDomain, c.ClusterDomain}
|
||||||
|
|
||||||
|
combinedSearch := append(dnsSearch, hostSearch...)
|
||||||
|
|
||||||
|
combinedSearch = omitDuplicates(pod, combinedSearch)
|
||||||
|
return c.formDNSSearchFitsLimits(pod, combinedSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckLimitsForResolvConf checks limits in resolv.conf.
|
||||||
|
func (c *Configurer) CheckLimitsForResolvConf() {
|
||||||
|
// resolver file Search line current limitations
|
||||||
|
resolvSearchLineDNSDomainsLimit := 6
|
||||||
|
resolvSearchLineLenLimit := 255
|
||||||
|
|
||||||
|
f, err := os.Open(c.ResolverConfig)
|
||||||
|
if err != nil {
|
||||||
|
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", err.Error())
|
||||||
|
glog.Error("CheckLimitsForResolvConf: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, hostSearch, _, err := parseResolvConf(f)
|
||||||
|
if err != nil {
|
||||||
|
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", err.Error())
|
||||||
|
glog.Error("CheckLimitsForResolvConf: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
domainCntLimit := resolvSearchLineDNSDomainsLimit
|
||||||
|
|
||||||
|
if c.ClusterDomain != "" {
|
||||||
|
domainCntLimit -= 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hostSearch) > domainCntLimit {
|
||||||
|
log := fmt.Sprintf("Resolv.conf file '%s' contains search line consisting of more than %d domains!", c.ResolverConfig, domainCntLimit)
|
||||||
|
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
|
||||||
|
glog.Error("CheckLimitsForResolvConf: " + log)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(strings.Join(hostSearch, " ")) > resolvSearchLineLenLimit {
|
||||||
|
log := fmt.Sprintf("Resolv.conf file '%s' contains search line which length is more than allowed %d chars!", c.ResolverConfig, resolvSearchLineLenLimit)
|
||||||
|
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
|
||||||
|
glog.Error("CheckLimitsForResolvConf: " + log)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseResolveConf reads a resolv.conf file from the given reader, and parses
|
||||||
|
// it into nameservers, searches and options, possibly returning an error.
|
||||||
|
// TODO: move to utility package
|
||||||
|
func parseResolvConf(reader io.Reader) (nameservers []string, searches []string, options []string, err error) {
|
||||||
|
file, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines of the form "nameserver 1.2.3.4" accumulate.
|
||||||
|
nameservers = []string{}
|
||||||
|
|
||||||
|
// Lines of the form "search example.com" overrule - last one wins.
|
||||||
|
searches = []string{}
|
||||||
|
|
||||||
|
// Lines of the form "option ndots:5 attempts:2" overrule - last one wins.
|
||||||
|
// Each option is recorded as an element in the array.
|
||||||
|
options = []string{}
|
||||||
|
|
||||||
|
lines := strings.Split(string(file), "\n")
|
||||||
|
for l := range lines {
|
||||||
|
trimmed := strings.TrimSpace(lines[l])
|
||||||
|
if strings.HasPrefix(trimmed, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(trimmed)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fields[0] == "nameserver" && len(fields) >= 2 {
|
||||||
|
nameservers = append(nameservers, fields[1])
|
||||||
|
}
|
||||||
|
if fields[0] == "search" {
|
||||||
|
searches = fields[1:]
|
||||||
|
}
|
||||||
|
if fields[0] == "options" {
|
||||||
|
options = fields[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There used to be code here to scrub DNS for each cloud, but doesn't
|
||||||
|
// make sense anymore since cloudproviders are being factored out.
|
||||||
|
// contact @thockin or @wlan0 for more information
|
||||||
|
|
||||||
|
return nameservers, searches, options, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClusterDNS returns a list of the DNS servers, a list of the DNS search
|
||||||
|
// domains of the cluster, and a list of resolv.conf options.
|
||||||
|
// TODO: This should return a struct.
|
||||||
|
func (c *Configurer) GetClusterDNS(pod *v1.Pod) ([]string, []string, []string, bool, error) {
|
||||||
|
var hostDNS, hostSearch, hostOptions []string
|
||||||
|
// Get host DNS settings
|
||||||
|
if c.ResolverConfig != "" {
|
||||||
|
f, err := os.Open(c.ResolverConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, false, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
hostDNS, hostSearch, hostOptions, err = parseResolvConf(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useClusterFirstPolicy := ((pod.Spec.DNSPolicy == v1.DNSClusterFirst && !kubecontainer.IsHostNetworkPod(pod)) || pod.Spec.DNSPolicy == v1.DNSClusterFirstWithHostNet)
|
||||||
|
if useClusterFirstPolicy && len(c.clusterDNS) == 0 {
|
||||||
|
// clusterDNS is not known.
|
||||||
|
// pod with ClusterDNSFirst Policy cannot be created
|
||||||
|
c.recorder.Eventf(pod, v1.EventTypeWarning, "MissingClusterDNS", "kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. Falling back to DNSDefault policy.", pod.Spec.DNSPolicy)
|
||||||
|
log := fmt.Sprintf("kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. pod: %q. Falling back to DNSDefault policy.", pod.Spec.DNSPolicy, format.Pod(pod))
|
||||||
|
c.recorder.Eventf(c.nodeRef, v1.EventTypeWarning, "MissingClusterDNS", log)
|
||||||
|
|
||||||
|
// fallback to DNSDefault
|
||||||
|
useClusterFirstPolicy = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !useClusterFirstPolicy {
|
||||||
|
// When the kubelet --resolv-conf flag is set to the empty string, use
|
||||||
|
// DNS settings that override the docker default (which is to use
|
||||||
|
// /etc/resolv.conf) and effectively disable DNS lookups. According to
|
||||||
|
// the bind documentation, the behavior of the DNS client library when
|
||||||
|
// "nameservers" are not specified is to "use the nameserver on the
|
||||||
|
// local machine". A nameserver setting of localhost is equivalent to
|
||||||
|
// this documented behavior.
|
||||||
|
if c.ResolverConfig == "" {
|
||||||
|
hostSearch = []string{"."}
|
||||||
|
switch {
|
||||||
|
case c.nodeIP == nil || c.nodeIP.To4() != nil:
|
||||||
|
hostDNS = []string{"127.0.0.1"}
|
||||||
|
case c.nodeIP.To16() != nil:
|
||||||
|
hostDNS = []string{"::1"}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hostSearch = c.formDNSSearchForDNSDefault(hostSearch, pod)
|
||||||
|
}
|
||||||
|
return hostDNS, hostSearch, hostOptions, useClusterFirstPolicy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// for a pod with DNSClusterFirst policy, the cluster DNS server is the only nameserver configured for
|
||||||
|
// the pod. The cluster DNS server itself will forward queries to other nameservers that is configured to use,
|
||||||
|
// in case the cluster DNS server cannot resolve the DNS query itself
|
||||||
|
dns := make([]string, len(c.clusterDNS))
|
||||||
|
for i, ip := range c.clusterDNS {
|
||||||
|
dns[i] = ip.String()
|
||||||
|
}
|
||||||
|
dnsSearch := c.formDNSSearch(hostSearch, pod)
|
||||||
|
|
||||||
|
return dns, dnsSearch, hostOptions, useClusterFirstPolicy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupDNSinContainerizedMounter replaces the nameserver in containerized-mounter's rootfs/etc/resolve.conf with kubelet.ClusterDNS
|
||||||
|
func (c *Configurer) SetupDNSinContainerizedMounter(mounterPath string) {
|
||||||
|
resolvePath := filepath.Join(strings.TrimSuffix(mounterPath, "/mounter"), "rootfs", "etc", "resolv.conf")
|
||||||
|
dnsString := ""
|
||||||
|
for _, dns := range c.clusterDNS {
|
||||||
|
dnsString = dnsString + fmt.Sprintf("nameserver %s\n", dns)
|
||||||
|
}
|
||||||
|
if c.ResolverConfig != "" {
|
||||||
|
f, err := os.Open(c.ResolverConfig)
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("Could not open resolverConf file")
|
||||||
|
} else {
|
||||||
|
_, hostSearch, _, err := parseResolvConf(f)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error for parsing the reslov.conf file: %v", err)
|
||||||
|
} else {
|
||||||
|
dnsString = dnsString + "search"
|
||||||
|
for _, search := range hostSearch {
|
||||||
|
dnsString = dnsString + fmt.Sprintf(" %s", search)
|
||||||
|
}
|
||||||
|
dnsString = dnsString + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(resolvePath, []byte(dnsString), 0600); err != nil {
|
||||||
|
glog.Errorf("Could not write dns nameserver in file %s, with error %v", resolvePath, err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/client-go/tools/record"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseResolvConf(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
data string
|
||||||
|
nameservers []string
|
||||||
|
searches []string
|
||||||
|
options []string
|
||||||
|
}{
|
||||||
|
{"", []string{}, []string{}, []string{}},
|
||||||
|
{" ", []string{}, []string{}, []string{}},
|
||||||
|
{"\n", []string{}, []string{}, []string{}},
|
||||||
|
{"\t\n\t", []string{}, []string{}, []string{}},
|
||||||
|
{"#comment\n", []string{}, []string{}, []string{}},
|
||||||
|
{" #comment\n", []string{}, []string{}, []string{}},
|
||||||
|
{"#comment\n#comment", []string{}, []string{}, []string{}},
|
||||||
|
{"#comment\nnameserver", []string{}, []string{}, []string{}},
|
||||||
|
{"#comment\nnameserver\nsearch", []string{}, []string{}, []string{}},
|
||||||
|
{"nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
||||||
|
{" nameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
||||||
|
{"\tnameserver 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
||||||
|
{"nameserver\t1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
||||||
|
{"nameserver \t 1.2.3.4", []string{"1.2.3.4"}, []string{}, []string{}},
|
||||||
|
{"nameserver 1.2.3.4\nnameserver 5.6.7.8", []string{"1.2.3.4", "5.6.7.8"}, []string{}, []string{}},
|
||||||
|
{"nameserver 1.2.3.4 #comment", []string{"1.2.3.4"}, []string{}, []string{}},
|
||||||
|
{"search foo", []string{}, []string{"foo"}, []string{}},
|
||||||
|
{"search foo bar", []string{}, []string{"foo", "bar"}, []string{}},
|
||||||
|
{"search foo bar bat\n", []string{}, []string{"foo", "bar", "bat"}, []string{}},
|
||||||
|
{"search foo\nsearch bar", []string{}, []string{"bar"}, []string{}},
|
||||||
|
{"nameserver 1.2.3.4\nsearch foo bar", []string{"1.2.3.4"}, []string{"foo", "bar"}, []string{}},
|
||||||
|
{"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}, []string{}},
|
||||||
|
{"#comment\nnameserver 1.2.3.4\n#comment\nsearch foo\ncomment", []string{"1.2.3.4"}, []string{"foo"}, []string{}},
|
||||||
|
{"options ndots:5 attempts:2", []string{}, []string{}, []string{"ndots:5", "attempts:2"}},
|
||||||
|
{"options ndots:1\noptions ndots:5 attempts:3", []string{}, []string{}, []string{"ndots:5", "attempts:3"}},
|
||||||
|
{"nameserver 1.2.3.4\nsearch foo\nnameserver 5.6.7.8\nsearch bar\noptions ndots:5 attempts:4", []string{"1.2.3.4", "5.6.7.8"}, []string{"bar"}, []string{"ndots:5", "attempts:4"}},
|
||||||
|
}
|
||||||
|
for i, tc := range testCases {
|
||||||
|
ns, srch, opts, err := parseResolvConf(strings.NewReader(tc.data))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, tc.nameservers, ns, "test case [%d]: name servers", i)
|
||||||
|
assert.EqualValues(t, tc.searches, srch, "test case [%d] searches", i)
|
||||||
|
assert.EqualValues(t, tc.options, opts, "test case [%d] options", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComposeDNSSearch(t *testing.T) {
|
||||||
|
recorder := record.NewFakeRecorder(20)
|
||||||
|
nodeRef := &v1.ObjectReference{
|
||||||
|
Kind: "Node",
|
||||||
|
Name: string("testNode"),
|
||||||
|
UID: types.UID("testNode"),
|
||||||
|
Namespace: "",
|
||||||
|
}
|
||||||
|
testClusterDNSDomain := "TEST"
|
||||||
|
|
||||||
|
configurer := NewConfigurer(recorder, nodeRef, nil, nil, testClusterDNSDomain, "")
|
||||||
|
|
||||||
|
pod := &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "",
|
||||||
|
Name: "test_pod",
|
||||||
|
Namespace: "testNS",
|
||||||
|
Annotations: map[string]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
dnsNames []string
|
||||||
|
hostNames []string
|
||||||
|
resultSearch []string
|
||||||
|
events []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||||
|
[]string{},
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||||
|
[]string{"AAA", "svc.TEST", "BBB", "TEST"},
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||||
|
[]string{"AAA", strings.Repeat("B", 256), "BBB"},
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA"},
|
||||||
|
[]string{"Search Line limits were exceeded, some dns names have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA"},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||||
|
[]string{"AAA", "TEST", "BBB", "TEST", "CCC", "DDD"},
|
||||||
|
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC"},
|
||||||
|
[]string{
|
||||||
|
"Search Line limits were exceeded, some dns names have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA BBB CCC",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchEvent := func(recorder *record.FakeRecorder) string {
|
||||||
|
select {
|
||||||
|
case event := <-recorder.Events:
|
||||||
|
return event
|
||||||
|
default:
|
||||||
|
return "No more events!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
dnsSearch := configurer.formDNSSearch(tc.hostNames, pod)
|
||||||
|
assert.EqualValues(t, tc.resultSearch, dnsSearch, "test [%d]", i)
|
||||||
|
for _, expectedEvent := range tc.events {
|
||||||
|
expected := fmt.Sprintf("%s %s %s", v1.EventTypeWarning, "DNSSearchForming", expectedEvent)
|
||||||
|
event := fetchEvent(recorder)
|
||||||
|
assert.Equal(t, expected, event, "test [%d]", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetClusterDNS(t *testing.T) {
|
||||||
|
recorder := record.NewFakeRecorder(20)
|
||||||
|
nodeRef := &v1.ObjectReference{
|
||||||
|
Kind: "Node",
|
||||||
|
Name: string("testNode"),
|
||||||
|
UID: types.UID("testNode"),
|
||||||
|
Namespace: "",
|
||||||
|
}
|
||||||
|
clusterNS := "203.0.113.1"
|
||||||
|
testClusterDNSDomain := "kubernetes.io"
|
||||||
|
testClusterDNS := []net.IP{net.ParseIP(clusterNS)}
|
||||||
|
|
||||||
|
configurer := NewConfigurer(recorder, nodeRef, nil, testClusterDNS, testClusterDNSDomain, "")
|
||||||
|
|
||||||
|
pods := newTestPods(4)
|
||||||
|
pods[0].Spec.DNSPolicy = v1.DNSClusterFirstWithHostNet
|
||||||
|
pods[1].Spec.DNSPolicy = v1.DNSClusterFirst
|
||||||
|
pods[2].Spec.DNSPolicy = v1.DNSClusterFirst
|
||||||
|
pods[2].Spec.HostNetwork = false
|
||||||
|
pods[3].Spec.DNSPolicy = v1.DNSDefault
|
||||||
|
|
||||||
|
options := make([]struct {
|
||||||
|
DNS []string
|
||||||
|
DNSSearch []string
|
||||||
|
}, 4)
|
||||||
|
for i, pod := range pods {
|
||||||
|
var err error
|
||||||
|
options[i].DNS, options[i].DNSSearch, _, _, err = configurer.GetClusterDNS(pod)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate container options: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(options[0].DNS) != 1 || options[0].DNS[0] != clusterNS {
|
||||||
|
t.Errorf("expected nameserver %s, got %+v", clusterNS, options[0].DNS)
|
||||||
|
}
|
||||||
|
if len(options[0].DNSSearch) == 0 || options[0].DNSSearch[0] != ".svc."+configurer.ClusterDomain {
|
||||||
|
t.Errorf("expected search %s, got %+v", ".svc."+configurer.ClusterDomain, options[0].DNSSearch)
|
||||||
|
}
|
||||||
|
if len(options[1].DNS) != 1 || options[1].DNS[0] != "127.0.0.1" {
|
||||||
|
t.Errorf("expected nameserver 127.0.0.1, got %+v", options[1].DNS)
|
||||||
|
}
|
||||||
|
if len(options[1].DNSSearch) != 1 || options[1].DNSSearch[0] != "." {
|
||||||
|
t.Errorf("expected search \".\", got %+v", options[1].DNSSearch)
|
||||||
|
}
|
||||||
|
if len(options[2].DNS) != 1 || options[2].DNS[0] != clusterNS {
|
||||||
|
t.Errorf("expected nameserver %s, got %+v", clusterNS, options[2].DNS)
|
||||||
|
}
|
||||||
|
if len(options[2].DNSSearch) == 0 || options[2].DNSSearch[0] != ".svc."+configurer.ClusterDomain {
|
||||||
|
t.Errorf("expected search %s, got %+v", ".svc."+configurer.ClusterDomain, options[2].DNSSearch)
|
||||||
|
}
|
||||||
|
if len(options[3].DNS) != 1 || options[3].DNS[0] != "127.0.0.1" {
|
||||||
|
t.Errorf("expected nameserver 127.0.0.1, got %+v", options[3].DNS)
|
||||||
|
}
|
||||||
|
if len(options[3].DNSSearch) != 1 || options[3].DNSSearch[0] != "." {
|
||||||
|
t.Errorf("expected search \".\", got %+v", options[3].DNSSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
testResolverConfig := "/etc/resolv.conf"
|
||||||
|
configurer = NewConfigurer(recorder, nodeRef, nil, testClusterDNS, testClusterDNSDomain, testResolverConfig)
|
||||||
|
for i, pod := range pods {
|
||||||
|
var err error
|
||||||
|
options[i].DNS, options[i].DNSSearch, _, _, err = configurer.GetClusterDNS(pod)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate container options: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("nameservers %+v", options[1].DNS)
|
||||||
|
if len(options[0].DNS) != 1 {
|
||||||
|
t.Errorf("expected cluster nameserver only, got %+v", options[0].DNS)
|
||||||
|
} else if options[0].DNS[0] != clusterNS {
|
||||||
|
t.Errorf("expected nameserver %s, got %v", clusterNS, options[0].DNS[0])
|
||||||
|
}
|
||||||
|
expLength := len(options[1].DNSSearch) + 3
|
||||||
|
if expLength > 6 {
|
||||||
|
expLength = 6
|
||||||
|
}
|
||||||
|
if len(options[0].DNSSearch) != expLength {
|
||||||
|
t.Errorf("expected prepend of cluster domain, got %+v", options[0].DNSSearch)
|
||||||
|
} else if options[0].DNSSearch[0] != ".svc."+configurer.ClusterDomain {
|
||||||
|
t.Errorf("expected domain %s, got %s", ".svc."+configurer.ClusterDomain, options[0].DNSSearch)
|
||||||
|
}
|
||||||
|
if len(options[2].DNS) != 1 {
|
||||||
|
t.Errorf("expected cluster nameserver only, got %+v", options[2].DNS)
|
||||||
|
} else if options[2].DNS[0] != clusterNS {
|
||||||
|
t.Errorf("expected nameserver %s, got %v", clusterNS, options[2].DNS[0])
|
||||||
|
}
|
||||||
|
if len(options[2].DNSSearch) != expLength {
|
||||||
|
t.Errorf("expected prepend of cluster domain, got %+v", options[2].DNSSearch)
|
||||||
|
} else if options[2].DNSSearch[0] != ".svc."+configurer.ClusterDomain {
|
||||||
|
t.Errorf("expected domain %s, got %s", ".svc."+configurer.ClusterDomain, options[0].DNSSearch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestPods(count int) []*v1.Pod {
|
||||||
|
pods := make([]*v1.Pod, count)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
pods[i] = &v1.Pod{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: types.UID(10000 + i),
|
||||||
|
Name: fmt.Sprintf("pod%d", i),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pods
|
||||||
|
}
|
Loading…
Reference in New Issue