mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Move DNS related kubelet codes into its own package
This commit is contained in:
parent
54e60aca17
commit
0bc2e1f62f
@ -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",
|
||||||
|
44
pkg/kubelet/network/dns/BUILD
Normal file
44
pkg/kubelet/network/dns/BUILD
Normal file
@ -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"],
|
||||||
|
)
|
4
pkg/kubelet/network/dns/OWNERS
Normal file
4
pkg/kubelet/network/dns/OWNERS
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
approvers:
|
||||||
|
- sig-network-approvers
|
||||||
|
reviewers:
|
||||||
|
- sig-network-reviewers
|
314
pkg/kubelet/network/dns/dns.go
Normal file
314
pkg/kubelet/network/dns/dns.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
262
pkg/kubelet/network/dns/dns_test.go
Normal file
262
pkg/kubelet/network/dns/dns_test.go
Normal file
@ -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
Block a user