Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
17
vendor/k8s.io/kubernetes/pkg/kubelet/network/BUILD
generated
vendored
Normal file
17
vendor/k8s.io/kubernetes/pkg/kubelet/network/BUILD
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubelet/network/dns:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
8
vendor/k8s.io/kubernetes/pkg/kubelet/network/OWNERS
generated
vendored
Normal file
8
vendor/k8s.io/kubernetes/pkg/kubelet/network/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
approvers:
|
||||
- thockin
|
||||
- dchen1107
|
||||
- matchstick
|
||||
- freehan
|
||||
reviewers:
|
||||
- sig-network-reviewers
|
||||
|
50
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/BUILD
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
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/apis/core/validation:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
|
||||
"//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/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["dns_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
|
||||
"//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/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature: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
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/OWNERS
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
approvers:
|
||||
- sig-network-approvers
|
||||
reviewers:
|
||||
- sig-network-reviewers
|
414
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/dns.go
generated
vendored
Normal file
414
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/dns.go
generated
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
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"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/format"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
// The default dns opt strings.
|
||||
defaultDNSOptions = []string{"ndots:5"}
|
||||
)
|
||||
|
||||
type podDNSType int
|
||||
|
||||
const (
|
||||
podDNSCluster podDNSType = iota
|
||||
podDNSHost
|
||||
podDNSNone
|
||||
)
|
||||
|
||||
// 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(strs []string) []string {
|
||||
uniqueStrs := make(map[string]bool)
|
||||
|
||||
var ret []string
|
||||
for _, str := range strs {
|
||||
if !uniqueStrs[str] {
|
||||
ret = append(ret, str)
|
||||
uniqueStrs[str] = true
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *Configurer) formDNSSearchFitsLimits(composedSearch []string, pod *v1.Pod) []string {
|
||||
limitsExceeded := false
|
||||
|
||||
if len(composedSearch) > validation.MaxDNSSearchPaths {
|
||||
composedSearch = composedSearch[:validation.MaxDNSSearchPaths]
|
||||
limitsExceeded = true
|
||||
}
|
||||
|
||||
if resolvSearchLineStrLen := len(strings.Join(composedSearch, " ")); resolvSearchLineStrLen > validation.MaxDNSSearchListChars {
|
||||
cutDomainsNum := 0
|
||||
cutDomainsLen := 0
|
||||
for i := len(composedSearch) - 1; i >= 0; i-- {
|
||||
cutDomainsLen += len(composedSearch[i]) + 1
|
||||
cutDomainsNum++
|
||||
|
||||
if (resolvSearchLineStrLen - cutDomainsLen) <= validation.MaxDNSSearchListChars {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
composedSearch = composedSearch[:(len(composedSearch) - cutDomainsNum)]
|
||||
limitsExceeded = true
|
||||
}
|
||||
|
||||
if limitsExceeded {
|
||||
log := fmt.Sprintf("Search Line limits were exceeded, some search paths have been omitted, the applied search line is: %s", strings.Join(composedSearch, " "))
|
||||
c.recorder.Event(pod, v1.EventTypeWarning, "DNSConfigForming", log)
|
||||
glog.Error(log)
|
||||
}
|
||||
return composedSearch
|
||||
}
|
||||
|
||||
func (c *Configurer) formDNSNameserversFitsLimits(nameservers []string, pod *v1.Pod) []string {
|
||||
if len(nameservers) > validation.MaxDNSNameservers {
|
||||
nameservers = nameservers[0:validation.MaxDNSNameservers]
|
||||
log := fmt.Sprintf("Nameserver limits were exceeded, some nameservers have been omitted, the applied nameserver line is: %s", strings.Join(nameservers, " "))
|
||||
c.recorder.Event(pod, v1.EventTypeWarning, "DNSConfigForming", log)
|
||||
glog.Error(log)
|
||||
}
|
||||
return nameservers
|
||||
}
|
||||
|
||||
func (c *Configurer) formDNSConfigFitsLimits(dnsConfig *runtimeapi.DNSConfig, pod *v1.Pod) *runtimeapi.DNSConfig {
|
||||
dnsConfig.Servers = c.formDNSNameserversFitsLimits(dnsConfig.Servers, pod)
|
||||
dnsConfig.Searches = c.formDNSSearchFitsLimits(dnsConfig.Searches, pod)
|
||||
return dnsConfig
|
||||
}
|
||||
|
||||
func (c *Configurer) generateSearchesForDNSClusterFirst(hostSearch []string, pod *v1.Pod) []string {
|
||||
if c.ClusterDomain == "" {
|
||||
return hostSearch
|
||||
}
|
||||
|
||||
nsSvcDomain := fmt.Sprintf("%s.svc.%s", pod.Namespace, c.ClusterDomain)
|
||||
svcDomain := fmt.Sprintf("svc.%s", c.ClusterDomain)
|
||||
clusterSearch := []string{nsSvcDomain, svcDomain, c.ClusterDomain}
|
||||
|
||||
return omitDuplicates(append(clusterSearch, hostSearch...))
|
||||
}
|
||||
|
||||
// CheckLimitsForResolvConf checks limits in resolv.conf.
|
||||
func (c *Configurer) CheckLimitsForResolvConf() {
|
||||
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
|
||||
}
|
||||
|
||||
domainCountLimit := validation.MaxDNSSearchPaths
|
||||
|
||||
if c.ClusterDomain != "" {
|
||||
domainCountLimit -= 3
|
||||
}
|
||||
|
||||
if len(hostSearch) > domainCountLimit {
|
||||
log := fmt.Sprintf("Resolv.conf file '%s' contains search line consisting of more than %d domains!", c.ResolverConfig, domainCountLimit)
|
||||
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
|
||||
glog.Error("CheckLimitsForResolvConf: " + log)
|
||||
return
|
||||
}
|
||||
|
||||
if len(strings.Join(hostSearch, " ")) > validation.MaxDNSSearchListChars {
|
||||
log := fmt.Sprintf("Resolv.conf file '%s' contains search line which length is more than allowed %d chars!", c.ResolverConfig, validation.MaxDNSSearchListChars)
|
||||
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.
|
||||
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:]
|
||||
}
|
||||
}
|
||||
|
||||
return nameservers, searches, options, nil
|
||||
}
|
||||
|
||||
func (c *Configurer) getHostDNSConfig(pod *v1.Pod) (*runtimeapi.DNSConfig, error) {
|
||||
var hostDNS, hostSearch, hostOptions []string
|
||||
// Get host DNS settings
|
||||
if c.ResolverConfig != "" {
|
||||
f, err := os.Open(c.ResolverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
hostDNS, hostSearch, hostOptions, err = parseResolvConf(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &runtimeapi.DNSConfig{
|
||||
Servers: hostDNS,
|
||||
Searches: hostSearch,
|
||||
Options: hostOptions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getPodDNSType(pod *v1.Pod) (podDNSType, error) {
|
||||
dnsPolicy := pod.Spec.DNSPolicy
|
||||
switch dnsPolicy {
|
||||
case v1.DNSNone:
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) {
|
||||
return podDNSNone, nil
|
||||
}
|
||||
// This should not happen as kube-apiserver should have rejected
|
||||
// setting dnsPolicy to DNSNone when feature gate is disabled.
|
||||
return podDNSCluster, fmt.Errorf(fmt.Sprintf("invalid DNSPolicy=%v: custom pod DNS is disabled", dnsPolicy))
|
||||
case v1.DNSClusterFirstWithHostNet:
|
||||
return podDNSCluster, nil
|
||||
case v1.DNSClusterFirst:
|
||||
if !kubecontainer.IsHostNetworkPod(pod) {
|
||||
return podDNSCluster, nil
|
||||
}
|
||||
// Fallback to DNSDefault for pod on hostnetowrk.
|
||||
fallthrough
|
||||
case v1.DNSDefault:
|
||||
return podDNSHost, nil
|
||||
}
|
||||
// This should not happen as kube-apiserver should have rejected
|
||||
// invalid dnsPolicy.
|
||||
return podDNSCluster, fmt.Errorf(fmt.Sprintf("invalid DNSPolicy=%v", dnsPolicy))
|
||||
}
|
||||
|
||||
// Merge DNS options. If duplicated, entries given by PodDNSConfigOption will
|
||||
// overwrite the existing ones.
|
||||
func mergeDNSOptions(existingDNSConfigOptions []string, dnsConfigOptions []v1.PodDNSConfigOption) []string {
|
||||
optionsMap := make(map[string]string)
|
||||
for _, op := range existingDNSConfigOptions {
|
||||
if index := strings.Index(op, ":"); index != -1 {
|
||||
optionsMap[op[:index]] = op[index+1:]
|
||||
} else {
|
||||
optionsMap[op] = ""
|
||||
}
|
||||
}
|
||||
for _, op := range dnsConfigOptions {
|
||||
if op.Value != nil {
|
||||
optionsMap[op.Name] = *op.Value
|
||||
} else {
|
||||
optionsMap[op.Name] = ""
|
||||
}
|
||||
}
|
||||
// Reconvert DNS options into a string array.
|
||||
options := []string{}
|
||||
for opName, opValue := range optionsMap {
|
||||
op := opName
|
||||
if opValue != "" {
|
||||
op = op + ":" + opValue
|
||||
}
|
||||
options = append(options, op)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// appendDNSConfig appends DNS servers, search paths and options given by
|
||||
// PodDNSConfig to the existing DNS config. Duplicated entries will be merged.
|
||||
// This assumes existingDNSConfig and dnsConfig are not nil.
|
||||
func appendDNSConfig(existingDNSConfig *runtimeapi.DNSConfig, dnsConfig *v1.PodDNSConfig) *runtimeapi.DNSConfig {
|
||||
existingDNSConfig.Servers = omitDuplicates(append(existingDNSConfig.Servers, dnsConfig.Nameservers...))
|
||||
existingDNSConfig.Searches = omitDuplicates(append(existingDNSConfig.Searches, dnsConfig.Searches...))
|
||||
existingDNSConfig.Options = mergeDNSOptions(existingDNSConfig.Options, dnsConfig.Options)
|
||||
return existingDNSConfig
|
||||
}
|
||||
|
||||
// GetPodDNS returns DNS settings for the pod.
|
||||
func (c *Configurer) GetPodDNS(pod *v1.Pod) (*runtimeapi.DNSConfig, error) {
|
||||
dnsConfig, err := c.getHostDNSConfig(pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dnsType, err := getPodDNSType(pod)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get DNS type for pod %q: %v. Falling back to DNSClusterFirst policy.", format.Pod(pod), err)
|
||||
dnsType = podDNSCluster
|
||||
}
|
||||
switch dnsType {
|
||||
case podDNSNone:
|
||||
// DNSNone should use empty DNS settings as the base.
|
||||
dnsConfig = &runtimeapi.DNSConfig{}
|
||||
case podDNSCluster:
|
||||
if len(c.clusterDNS) != 0 {
|
||||
// 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.
|
||||
dnsConfig.Servers = []string{}
|
||||
for _, ip := range c.clusterDNS {
|
||||
dnsConfig.Servers = append(dnsConfig.Servers, ip.String())
|
||||
}
|
||||
dnsConfig.Searches = c.generateSearchesForDNSClusterFirst(dnsConfig.Searches, pod)
|
||||
dnsConfig.Options = defaultDNSOptions
|
||||
break
|
||||
}
|
||||
// clusterDNS is not known. Pod with ClusterDNSFirst Policy cannot be created.
|
||||
nodeErrorMsg := fmt.Sprintf("kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. Falling back to %q policy.", v1.DNSClusterFirst, v1.DNSDefault)
|
||||
c.recorder.Eventf(c.nodeRef, v1.EventTypeWarning, "MissingClusterDNS", nodeErrorMsg)
|
||||
c.recorder.Eventf(pod, v1.EventTypeWarning, "MissingClusterDNS", "pod: %q. %s", format.Pod(pod), nodeErrorMsg)
|
||||
// Fallback to DNSDefault.
|
||||
fallthrough
|
||||
case podDNSHost:
|
||||
// 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 == "" {
|
||||
switch {
|
||||
case c.nodeIP == nil || c.nodeIP.To4() != nil:
|
||||
dnsConfig.Servers = []string{"127.0.0.1"}
|
||||
case c.nodeIP.To16() != nil:
|
||||
dnsConfig.Servers = []string{"::1"}
|
||||
}
|
||||
dnsConfig.Searches = []string{"."}
|
||||
}
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) && pod.Spec.DNSConfig != nil {
|
||||
dnsConfig = appendDNSConfig(dnsConfig, pod.Spec.DNSConfig)
|
||||
}
|
||||
return c.formDNSConfigFitsLimits(dnsConfig, pod), 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)
|
||||
}
|
||||
}
|
675
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/dns_test.go
generated
vendored
Normal file
675
vendor/k8s.io/kubernetes/pkg/kubelet/network/dns/dns_test.go
generated
vendored
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
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/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/tools/record"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
fetchEvent = func(recorder *record.FakeRecorder) string {
|
||||
select {
|
||||
case event := <-recorder.Events:
|
||||
return event
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
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 TestFormDNSSearchFitsLimits(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 {
|
||||
hostNames []string
|
||||
resultSearch []string
|
||||
events []string
|
||||
}{
|
||||
{
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
|
||||
[]string{},
|
||||
},
|
||||
|
||||
{
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
|
||||
[]string{},
|
||||
},
|
||||
|
||||
{
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", strings.Repeat("B", 256), "BBB"},
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA"},
|
||||
[]string{"Search Line limits were exceeded, some search paths have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA"},
|
||||
},
|
||||
|
||||
{
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC", "DDD"},
|
||||
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC"},
|
||||
[]string{"Search Line limits were exceeded, some search paths have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST AAA BBB CCC"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
dnsSearch := configurer.formDNSSearchFitsLimits(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, "DNSConfigForming", expectedEvent)
|
||||
event := fetchEvent(recorder)
|
||||
assert.Equal(t, expected, event, "test [%d]", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormDNSNameserversFitsLimits(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 {
|
||||
desc string
|
||||
nameservers []string
|
||||
expectedNameserver []string
|
||||
expectedEvent bool
|
||||
}{
|
||||
{
|
||||
desc: "valid: 1 nameserver",
|
||||
nameservers: []string{"127.0.0.1"},
|
||||
expectedNameserver: []string{"127.0.0.1"},
|
||||
expectedEvent: false,
|
||||
},
|
||||
{
|
||||
desc: "valid: 3 nameservers",
|
||||
nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
|
||||
expectedNameserver: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
|
||||
expectedEvent: false,
|
||||
},
|
||||
{
|
||||
desc: "invalid: 4 nameservers, trimmed to 3",
|
||||
nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8", "1.2.3.4"},
|
||||
expectedNameserver: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
|
||||
expectedEvent: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
appliedNameservers := configurer.formDNSNameserversFitsLimits(tc.nameservers, pod)
|
||||
assert.EqualValues(t, tc.expectedNameserver, appliedNameservers, tc.desc)
|
||||
event := fetchEvent(recorder)
|
||||
if tc.expectedEvent && len(event) == 0 {
|
||||
t.Errorf("%s: formDNSNameserversFitsLimits(%v) expected event, got no event.", tc.desc, tc.nameservers)
|
||||
} else if !tc.expectedEvent && len(event) > 0 {
|
||||
t.Errorf("%s: formDNSNameserversFitsLimits(%v) expected no event, got event: %v", tc.desc, tc.nameservers, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeDNSOptions(t *testing.T) {
|
||||
testOptionValue := "3"
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
existingDNSConfigOptions []string
|
||||
dnsConfigOptions []v1.PodDNSConfigOption
|
||||
expectedOptions []string
|
||||
}{
|
||||
{
|
||||
desc: "Empty dnsConfigOptions",
|
||||
existingDNSConfigOptions: []string{"ndots:5", "debug"},
|
||||
dnsConfigOptions: nil,
|
||||
expectedOptions: []string{"ndots:5", "debug"},
|
||||
},
|
||||
{
|
||||
desc: "No duplicated entries",
|
||||
existingDNSConfigOptions: []string{"ndots:5", "debug"},
|
||||
dnsConfigOptions: []v1.PodDNSConfigOption{
|
||||
{Name: "single-request"},
|
||||
{Name: "attempts", Value: &testOptionValue},
|
||||
},
|
||||
expectedOptions: []string{"ndots:5", "debug", "single-request", "attempts:3"},
|
||||
},
|
||||
{
|
||||
desc: "Overwrite duplicated entries",
|
||||
existingDNSConfigOptions: []string{"ndots:5", "debug"},
|
||||
dnsConfigOptions: []v1.PodDNSConfigOption{
|
||||
{Name: "ndots", Value: &testOptionValue},
|
||||
{Name: "debug"},
|
||||
{Name: "single-request"},
|
||||
{Name: "attempts", Value: &testOptionValue},
|
||||
},
|
||||
expectedOptions: []string{"ndots:3", "debug", "single-request", "attempts:3"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
options := mergeDNSOptions(tc.existingDNSConfigOptions, tc.dnsConfigOptions)
|
||||
// Options order may be changed after conversion.
|
||||
if !sets.NewString(options...).Equal(sets.NewString(tc.expectedOptions...)) {
|
||||
t.Errorf("%s: mergeDNSOptions(%v, %v)=%v, want %v", tc.desc, tc.existingDNSConfigOptions, tc.dnsConfigOptions, options, tc.expectedOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodDNSType(t *testing.T) {
|
||||
customDNSEnabled := utilfeature.DefaultFeatureGate.Enabled("CustomPodDNS")
|
||||
defer func() {
|
||||
// Restoring the old value.
|
||||
if err := utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("CustomPodDNS=%v", customDNSEnabled)); err != nil {
|
||||
t.Errorf("Failed to set CustomPodDNS feature gate: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
recorder := record.NewFakeRecorder(20)
|
||||
nodeRef := &v1.ObjectReference{
|
||||
Kind: "Node",
|
||||
Name: string("testNode"),
|
||||
UID: types.UID("testNode"),
|
||||
Namespace: "",
|
||||
}
|
||||
testClusterDNSDomain := "TEST"
|
||||
clusterNS := "203.0.113.1"
|
||||
testClusterDNS := []net.IP{net.ParseIP(clusterNS)}
|
||||
|
||||
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 {
|
||||
desc string
|
||||
customPodDNSFeatureGate bool
|
||||
hasClusterDNS bool
|
||||
hostNetwork bool
|
||||
dnsPolicy v1.DNSPolicy
|
||||
expectedDNSType podDNSType
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "valid DNSClusterFirst without hostnetwork",
|
||||
hasClusterDNS: true,
|
||||
dnsPolicy: v1.DNSClusterFirst,
|
||||
expectedDNSType: podDNSCluster,
|
||||
},
|
||||
{
|
||||
desc: "valid DNSClusterFirstWithHostNet with hostnetwork",
|
||||
hasClusterDNS: true,
|
||||
hostNetwork: true,
|
||||
dnsPolicy: v1.DNSClusterFirstWithHostNet,
|
||||
expectedDNSType: podDNSCluster,
|
||||
},
|
||||
{
|
||||
desc: "valid DNSClusterFirstWithHostNet without hostnetwork",
|
||||
hasClusterDNS: true,
|
||||
dnsPolicy: v1.DNSClusterFirstWithHostNet,
|
||||
expectedDNSType: podDNSCluster,
|
||||
},
|
||||
{
|
||||
desc: "valid DNSDefault without hostnetwork",
|
||||
dnsPolicy: v1.DNSDefault,
|
||||
expectedDNSType: podDNSHost,
|
||||
},
|
||||
{
|
||||
desc: "valid DNSDefault with hostnetwork",
|
||||
hostNetwork: true,
|
||||
dnsPolicy: v1.DNSDefault,
|
||||
expectedDNSType: podDNSHost,
|
||||
},
|
||||
{
|
||||
desc: "DNSClusterFirst with hostnetwork, fallback to DNSDefault",
|
||||
hasClusterDNS: true,
|
||||
hostNetwork: true,
|
||||
dnsPolicy: v1.DNSClusterFirst,
|
||||
expectedDNSType: podDNSHost,
|
||||
},
|
||||
{
|
||||
desc: "valid DNSNone with feature gate",
|
||||
customPodDNSFeatureGate: true,
|
||||
dnsPolicy: v1.DNSNone,
|
||||
expectedDNSType: podDNSNone,
|
||||
},
|
||||
{
|
||||
desc: "DNSNone without feature gate, should return error",
|
||||
dnsPolicy: v1.DNSNone,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid DNS policy, should return error",
|
||||
dnsPolicy: "invalidPolicy",
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if err := utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("CustomPodDNS=%v", tc.customPodDNSFeatureGate)); err != nil {
|
||||
t.Errorf("Failed to set CustomPodDNS feature gate: %v", err)
|
||||
}
|
||||
|
||||
if tc.hasClusterDNS {
|
||||
configurer.clusterDNS = testClusterDNS
|
||||
} else {
|
||||
configurer.clusterDNS = nil
|
||||
}
|
||||
pod.Spec.DNSPolicy = tc.dnsPolicy
|
||||
pod.Spec.HostNetwork = tc.hostNetwork
|
||||
|
||||
resType, err := getPodDNSType(pod)
|
||||
if tc.expectedError {
|
||||
if err == nil {
|
||||
t.Errorf("%s: GetPodDNSType(%v) got no error, want error", tc.desc, pod)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if resType != tc.expectedDNSType {
|
||||
t.Errorf("%s: GetPodDNSType(%v)=%v, want %v", tc.desc, pod, resType, tc.expectedDNSType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodDNS(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
|
||||
dnsConfig, err := configurer.GetPodDNS(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate container options: %v", err)
|
||||
}
|
||||
options[i].DNS, options[i].DNSSearch = dnsConfig.Servers, dnsConfig.Searches
|
||||
}
|
||||
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
|
||||
dnsConfig, err := configurer.GetPodDNS(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate container options: %v", err)
|
||||
}
|
||||
options[i].DNS, options[i].DNSSearch = dnsConfig.Servers, dnsConfig.Searches
|
||||
}
|
||||
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 TestGetPodDNSCustom(t *testing.T) {
|
||||
customDNSEnabled := utilfeature.DefaultFeatureGate.Enabled("CustomPodDNS")
|
||||
defer func() {
|
||||
// Restoring the old value.
|
||||
if err := utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("CustomPodDNS=%v", customDNSEnabled)); err != nil {
|
||||
t.Errorf("Failed to set CustomPodDNS feature gate: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
recorder := record.NewFakeRecorder(20)
|
||||
nodeRef := &v1.ObjectReference{
|
||||
Kind: "Node",
|
||||
Name: string("testNode"),
|
||||
UID: types.UID("testNode"),
|
||||
Namespace: "",
|
||||
}
|
||||
|
||||
testPodNamespace := "testNS"
|
||||
testClusterNameserver := "10.0.0.10"
|
||||
testClusterDNSDomain := "kubernetes.io"
|
||||
testSvcDomain := fmt.Sprintf("svc.%s", testClusterDNSDomain)
|
||||
testNsSvcDomain := fmt.Sprintf("%s.svc.%s", testPodNamespace, testClusterDNSDomain)
|
||||
testNdotsOptionValue := "3"
|
||||
testHostNameserver := "8.8.8.8"
|
||||
testHostDomain := "host.domain"
|
||||
|
||||
testPod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test_pod",
|
||||
Namespace: testPodNamespace,
|
||||
},
|
||||
}
|
||||
|
||||
resolvConfContent := []byte(fmt.Sprintf("nameserver %s\nsearch %s\n", testHostNameserver, testHostDomain))
|
||||
tmpfile, err := ioutil.TempFile("", "tmpResolvConf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
if _, err := tmpfile.Write(resolvConfContent); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
configurer := NewConfigurer(recorder, nodeRef, nil, []net.IP{net.ParseIP(testClusterNameserver)}, testClusterDNSDomain, tmpfile.Name())
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
customPodDNSFeatureGate bool
|
||||
hostnetwork bool
|
||||
dnsPolicy v1.DNSPolicy
|
||||
dnsConfig *v1.PodDNSConfig
|
||||
expectedDNSConfig *runtimeapi.DNSConfig
|
||||
}{
|
||||
{
|
||||
desc: "feature gate is disabled, DNSNone should fallback to DNSClusterFirst",
|
||||
dnsPolicy: v1.DNSNone,
|
||||
expectedDNSConfig: &runtimeapi.DNSConfig{
|
||||
Servers: []string{testClusterNameserver},
|
||||
Searches: []string{testNsSvcDomain, testSvcDomain, testClusterDNSDomain, testHostDomain},
|
||||
Options: []string{"ndots:5"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "feature gate is enabled, DNSNone without DNSConfig should have empty DNS settings",
|
||||
customPodDNSFeatureGate: true,
|
||||
dnsPolicy: v1.DNSNone,
|
||||
expectedDNSConfig: &runtimeapi.DNSConfig{},
|
||||
},
|
||||
{
|
||||
desc: "feature gate is enabled, DNSNone with DNSConfig should have a merged DNS settings",
|
||||
customPodDNSFeatureGate: true,
|
||||
dnsPolicy: v1.DNSNone,
|
||||
dnsConfig: &v1.PodDNSConfig{
|
||||
Nameservers: []string{"203.0.113.1"},
|
||||
Searches: []string{"my.domain", "second.domain"},
|
||||
Options: []v1.PodDNSConfigOption{
|
||||
{Name: "ndots", Value: &testNdotsOptionValue},
|
||||
{Name: "debug"},
|
||||
},
|
||||
},
|
||||
expectedDNSConfig: &runtimeapi.DNSConfig{
|
||||
Servers: []string{"203.0.113.1"},
|
||||
Searches: []string{"my.domain", "second.domain"},
|
||||
Options: []string{"ndots:3", "debug"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "feature gate is enabled, DNSClusterFirst with DNSConfig should have a merged DNS settings",
|
||||
customPodDNSFeatureGate: true,
|
||||
dnsPolicy: v1.DNSClusterFirst,
|
||||
dnsConfig: &v1.PodDNSConfig{
|
||||
Nameservers: []string{"10.0.0.11"},
|
||||
Searches: []string{"my.domain"},
|
||||
Options: []v1.PodDNSConfigOption{
|
||||
{Name: "ndots", Value: &testNdotsOptionValue},
|
||||
{Name: "debug"},
|
||||
},
|
||||
},
|
||||
expectedDNSConfig: &runtimeapi.DNSConfig{
|
||||
Servers: []string{testClusterNameserver, "10.0.0.11"},
|
||||
Searches: []string{testNsSvcDomain, testSvcDomain, testClusterDNSDomain, testHostDomain, "my.domain"},
|
||||
Options: []string{"ndots:3", "debug"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "feature gate is enabled, DNSClusterFirstWithHostNet with DNSConfig should have a merged DNS settings",
|
||||
customPodDNSFeatureGate: true,
|
||||
hostnetwork: true,
|
||||
dnsPolicy: v1.DNSClusterFirstWithHostNet,
|
||||
dnsConfig: &v1.PodDNSConfig{
|
||||
Nameservers: []string{"10.0.0.11"},
|
||||
Searches: []string{"my.domain"},
|
||||
Options: []v1.PodDNSConfigOption{
|
||||
{Name: "ndots", Value: &testNdotsOptionValue},
|
||||
{Name: "debug"},
|
||||
},
|
||||
},
|
||||
expectedDNSConfig: &runtimeapi.DNSConfig{
|
||||
Servers: []string{testClusterNameserver, "10.0.0.11"},
|
||||
Searches: []string{testNsSvcDomain, testSvcDomain, testClusterDNSDomain, testHostDomain, "my.domain"},
|
||||
Options: []string{"ndots:3", "debug"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "feature gate is enabled, DNSDefault with DNSConfig should have a merged DNS settings",
|
||||
customPodDNSFeatureGate: true,
|
||||
dnsPolicy: v1.DNSDefault,
|
||||
dnsConfig: &v1.PodDNSConfig{
|
||||
Nameservers: []string{"10.0.0.11"},
|
||||
Searches: []string{"my.domain"},
|
||||
Options: []v1.PodDNSConfigOption{
|
||||
{Name: "ndots", Value: &testNdotsOptionValue},
|
||||
{Name: "debug"},
|
||||
},
|
||||
},
|
||||
expectedDNSConfig: &runtimeapi.DNSConfig{
|
||||
Servers: []string{testHostNameserver, "10.0.0.11"},
|
||||
Searches: []string{testHostDomain, "my.domain"},
|
||||
Options: []string{"ndots:3", "debug"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if err := utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("CustomPodDNS=%v", tc.customPodDNSFeatureGate)); err != nil {
|
||||
t.Errorf("Failed to set CustomPodDNS feature gate: %v", err)
|
||||
}
|
||||
|
||||
testPod.Spec.HostNetwork = tc.hostnetwork
|
||||
testPod.Spec.DNSConfig = tc.dnsConfig
|
||||
testPod.Spec.DNSPolicy = tc.dnsPolicy
|
||||
|
||||
resDNSConfig, err := configurer.GetPodDNS(testPod)
|
||||
if err != nil {
|
||||
t.Errorf("%s: GetPodDNS(%v), unexpected error: %v", tc.desc, testPod, err)
|
||||
}
|
||||
if !dnsConfigsAreEqual(resDNSConfig, tc.expectedDNSConfig) {
|
||||
t.Errorf("%s: GetPodDNS(%v)=%v, want %v", tc.desc, testPod, resDNSConfig, tc.expectedDNSConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dnsConfigsAreEqual(resConfig, expectedConfig *runtimeapi.DNSConfig) bool {
|
||||
if len(resConfig.Servers) != len(expectedConfig.Servers) ||
|
||||
len(resConfig.Searches) != len(expectedConfig.Searches) ||
|
||||
len(resConfig.Options) != len(expectedConfig.Options) {
|
||||
return false
|
||||
}
|
||||
for i, server := range resConfig.Servers {
|
||||
if expectedConfig.Servers[i] != server {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i, search := range resConfig.Searches {
|
||||
if expectedConfig.Searches[i] != search {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Options order may be changed after conversion.
|
||||
return sets.NewString(resConfig.Options...).Equal(sets.NewString(expectedConfig.Options...))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user