Add feature gate ExpandedDNSConfig

ExpandedDNSConfig allows kubernetes to have expanded DNS(Domain Name
System) configuration
This commit is contained in:
Gunju Kim
2021-05-24 20:10:02 +09:00
parent 819059f641
commit 6317ce63c6
6 changed files with 296 additions and 45 deletions

View File

@@ -28,9 +28,11 @@ import (
"k8s.io/api/core/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/record"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/util/format"
@@ -100,8 +102,13 @@ func omitDuplicates(strs []string) []string {
func (c *Configurer) formDNSSearchFitsLimits(composedSearch []string, pod *v1.Pod) []string {
limitsExceeded := false
if len(composedSearch) > validation.MaxDNSSearchPaths {
composedSearch = composedSearch[:validation.MaxDNSSearchPaths]
maxDNSSearchPaths, maxDNSSearchListChars := validation.MaxDNSSearchPathsLegacy, validation.MaxDNSSearchListCharsLegacy
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandedDNSConfig) {
maxDNSSearchPaths, maxDNSSearchListChars = validation.MaxDNSSearchPathsExpanded, validation.MaxDNSSearchListCharsExpanded
}
if len(composedSearch) > maxDNSSearchPaths {
composedSearch = composedSearch[:maxDNSSearchPaths]
limitsExceeded = true
}
@@ -118,14 +125,14 @@ func (c *Configurer) formDNSSearchFitsLimits(composedSearch []string, pod *v1.Po
}
composedSearch = composedSearch[:l]
if resolvSearchLineStrLen := len(strings.Join(composedSearch, " ")); resolvSearchLineStrLen > validation.MaxDNSSearchListChars {
if resolvSearchLineStrLen := len(strings.Join(composedSearch, " ")); resolvSearchLineStrLen > maxDNSSearchListChars {
cutDomainsNum := 0
cutDomainsLen := 0
for i := len(composedSearch) - 1; i >= 0; i-- {
cutDomainsLen += len(composedSearch[i]) + 1
cutDomainsNum++
if (resolvSearchLineStrLen - cutDomainsLen) <= validation.MaxDNSSearchListChars {
if (resolvSearchLineStrLen - cutDomainsLen) <= maxDNSSearchListChars {
break
}
}
@@ -187,7 +194,10 @@ func (c *Configurer) CheckLimitsForResolvConf() {
return
}
domainCountLimit := validation.MaxDNSSearchPaths
domainCountLimit, maxDNSSearchListChars := validation.MaxDNSSearchPathsLegacy, validation.MaxDNSSearchListCharsLegacy
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandedDNSConfig) {
domainCountLimit, maxDNSSearchListChars = validation.MaxDNSSearchPathsExpanded, validation.MaxDNSSearchListCharsExpanded
}
if c.ClusterDomain != "" {
domainCountLimit -= 3
@@ -200,8 +210,17 @@ func (c *Configurer) CheckLimitsForResolvConf() {
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)
for _, search := range hostSearch {
if len(search) > utilvalidation.DNS1123SubdomainMaxLength {
log := fmt.Sprintf("Resolv.conf file %q contains a search path which length is more than allowed %d chars!", c.ResolverConfig, utilvalidation.DNS1123SubdomainMaxLength)
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
klog.V(4).InfoS("Check limits for resolv.conf failed", "eventlog", log)
return
}
}
if len(strings.Join(hostSearch, " ")) > maxDNSSearchListChars {
log := fmt.Sprintf("Resolv.conf file '%s' contains search line which length is more than allowed %d chars!", c.ResolverConfig, maxDNSSearchListChars)
c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
klog.V(4).InfoS("Check limits for resolv.conf failed", "eventlog", log)
return

View File

@@ -29,8 +29,12 @@ import (
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"
featuregatetesting "k8s.io/component-base/featuregate/testing"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -100,6 +104,26 @@ func TestParseResolvConf(t *testing.T) {
}
func TestFormDNSSearchFitsLimits(t *testing.T) {
searchPathList2048Chars := []string{
// 2048 = 128 + 127 * 15 + 15
strings.Repeat("A", 128),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
strings.Repeat("A", 127),
}
recorder := record.NewFakeRecorder(20)
nodeRef := &v1.ObjectReference{
Kind: "Node",
@@ -121,43 +145,93 @@ func TestFormDNSSearchFitsLimits(t *testing.T) {
}
testCases := []struct {
hostNames []string
resultSearch []string
events []string
desc string
hostNames []string
resultSearch []string
events []string
expandedDNSConfig bool
}{
{
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
[]string{"testNS.svc.TEST", "svc.TEST", "TEST"},
[]string{},
desc: "valid: 3 search paths",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST"},
events: []string{},
},
{
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
[]string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
[]string{},
desc: "valid: 5 search paths",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
events: []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"},
desc: "invalid: longer than 256 characters in search path list",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", strings.Repeat("B", 256), "BBB"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
events: []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"},
},
{
[]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"},
desc: "valid ExpandedDNSConfig: 2048 characters in search path list",
hostNames: searchPathList2048Chars,
resultSearch: searchPathList2048Chars,
events: []string{},
expandedDNSConfig: true,
},
{
desc: "invalid ExpandedDNSConfig: 2050 characters in search path list",
hostNames: append(searchPathList2048Chars, "B"),
resultSearch: searchPathList2048Chars,
events: []string{fmt.Sprintf("Search Line limits were exceeded, some search paths have been omitted, the applied search line is: %s", strings.Join(searchPathList2048Chars, " "))},
expandedDNSConfig: true,
},
{
desc: "invalid ExpandedDNSConfig: 256 characters search path",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", strings.Repeat("B", 256), "BBB"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB"},
events: []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"},
expandedDNSConfig: true,
},
{
desc: "invalid: 7 search paths",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC", "DDD"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "AAA", "BBB", "CCC"},
events: []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"},
},
{
desc: "valid ExpandedDNSConfig: 32 search paths",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"},
events: []string{},
expandedDNSConfig: true,
},
{
desc: "invalid ExpandedDNSConfig: 33 search paths",
hostNames: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33"},
resultSearch: []string{"testNS.svc.TEST", "svc.TEST", "TEST", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"},
events: []string{"Search Line limits were exceeded, some search paths have been omitted, the applied search line is: testNS.svc.TEST svc.TEST TEST 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32"},
expandedDNSConfig: true,
},
}
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)
}
t.Run(tc.desc, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandedDNSConfig, tc.expandedDNSConfig)()
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)
}
})
}
}
@@ -371,6 +445,29 @@ func TestGetPodDNSType(t *testing.T) {
}
func TestGetPodDNS(t *testing.T) {
testCases := []struct {
desc string
expandedDNSConfig bool
}{
{
desc: "Not ExpandedDNSConfig",
expandedDNSConfig: false,
},
{
desc: "ExpandedDNSConfig",
expandedDNSConfig: true,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandedDNSConfig, tc.expandedDNSConfig)()
testGetPodDNS(t)
})
}
}
func testGetPodDNS(t *testing.T) {
recorder := record.NewFakeRecorder(20)
nodeRef := &v1.ObjectReference{
Kind: "Node",
@@ -445,8 +542,14 @@ func TestGetPodDNS(t *testing.T) {
t.Errorf("expected nameserver %s, got %v", clusterNS, options[0].DNS[0])
}
expLength := len(options[1].DNSSearch) + 3
if expLength > 6 {
expLength = 6
maxDNSSearchPaths := validation.MaxDNSSearchPathsLegacy
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandedDNSConfig) {
maxDNSSearchPaths = validation.MaxDNSSearchPathsExpanded
}
if expLength > maxDNSSearchPaths {
expLength = maxDNSSearchPaths
}
if len(options[0].DNSSearch) != expLength {
t.Errorf("expected prepend of cluster domain, got %+v", options[0].DNSSearch)