mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 23:37:01 +00:00
Merge pull request #30931 from Clarifai/ext-svc-ref-dns
Automatic merge from submit-queue Add ExternalName kube-dns e2e test ExternalName allows kubedns to return CNAME records for external services. No proxying is involved. Built on top of and includes #30599 See original issue at https://github.com/kubernetes/kubernetes/issues/13748 Feature tracking at https://github.com/kubernetes/features/issues/33 The e2e test is at least as comprehensive as the one for headless services (namely, only to some degree) ```release-note Add ExternalName services as CNAME references to external ones ```
This commit is contained in:
commit
36a6aee27f
@ -44,7 +44,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kubedns
|
- name: kubedns
|
||||||
image: gcr.io/google_containers/kubedns-amd64:1.6
|
image: gcr.io/google_containers/kubedns-amd64:1.7
|
||||||
resources:
|
resources:
|
||||||
# TODO: Set memory limits when we've profiled the container for large
|
# TODO: Set memory limits when we've profiled the container for large
|
||||||
# clusters, then set request = limit to keep this container in
|
# clusters, then set request = limit to keep this container in
|
||||||
|
@ -44,7 +44,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kubedns
|
- name: kubedns
|
||||||
image: gcr.io/google_containers/kubedns-amd64:1.6
|
image: gcr.io/google_containers/kubedns-amd64:1.7
|
||||||
resources:
|
resources:
|
||||||
# TODO: Set memory limits when we've profiled the container for large
|
# TODO: Set memory limits when we've profiled the container for large
|
||||||
# clusters, then set request = limit to keep this container in
|
# clusters, then set request = limit to keep this container in
|
||||||
|
@ -44,7 +44,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kubedns
|
- name: kubedns
|
||||||
image: gcr.io/google_containers/kubedns-amd64:1.6
|
image: gcr.io/google_containers/kubedns-amd64:1.7
|
||||||
resources:
|
resources:
|
||||||
# TODO: Set memory limits when we've profiled the container for large
|
# TODO: Set memory limits when we've profiled the container for large
|
||||||
# clusters, then set request = limit to keep this container in
|
# clusters, then set request = limit to keep this container in
|
||||||
|
143
test/e2e/dns.go
143
test/e2e/dns.go
@ -164,7 +164,18 @@ func createProbeCommand(namesToResolve []string, hostEntries []string, ptrLookup
|
|||||||
return probeCmd, fileNames
|
return probeCmd, fileNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createTargetedProbeCommand returns a command line that performs a DNS lookup for a specific record type
|
||||||
|
func createTargetedProbeCommand(nameToResolve string, lookup string, fileNamePrefix string) (string, string) {
|
||||||
|
fileName := fmt.Sprintf("%s_udp@%s", fileNamePrefix, nameToResolve)
|
||||||
|
probeCmd := fmt.Sprintf("dig +short +tries=12 +norecurse %s %s > /results/%s", nameToResolve, lookup, fileName)
|
||||||
|
return probeCmd, fileName
|
||||||
|
}
|
||||||
|
|
||||||
func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *client.Client) {
|
func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *client.Client) {
|
||||||
|
assertFilesContain(fileNames, fileDir, pod, client, false, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertFilesContain(fileNames []string, fileDir string, pod *api.Pod, client *client.Client, check bool, expected string) {
|
||||||
var failed []string
|
var failed []string
|
||||||
|
|
||||||
framework.ExpectNoError(wait.Poll(time.Second*2, time.Second*60, func() (bool, error) {
|
framework.ExpectNoError(wait.Poll(time.Second*2, time.Second*60, func() (bool, error) {
|
||||||
@ -173,9 +184,10 @@ func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
var contents []byte
|
||||||
for _, fileName := range fileNames {
|
for _, fileName := range fileNames {
|
||||||
if subResourceProxyAvailable {
|
if subResourceProxyAvailable {
|
||||||
_, err = client.Get().
|
contents, err = client.Get().
|
||||||
Namespace(pod.Namespace).
|
Namespace(pod.Namespace).
|
||||||
Resource("pods").
|
Resource("pods").
|
||||||
SubResource("proxy").
|
SubResource("proxy").
|
||||||
@ -183,7 +195,7 @@ func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *
|
|||||||
Suffix(fileDir, fileName).
|
Suffix(fileDir, fileName).
|
||||||
Do().Raw()
|
Do().Raw()
|
||||||
} else {
|
} else {
|
||||||
_, err = client.Get().
|
contents, err = client.Get().
|
||||||
Prefix("proxy").
|
Prefix("proxy").
|
||||||
Resource("pods").
|
Resource("pods").
|
||||||
Namespace(pod.Namespace).
|
Namespace(pod.Namespace).
|
||||||
@ -194,6 +206,9 @@ func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
framework.Logf("Unable to read %s from pod %s: %v", fileName, pod.Name, err)
|
framework.Logf("Unable to read %s from pod %s: %v", fileName, pod.Name, err)
|
||||||
failed = append(failed, fileName)
|
failed = append(failed, fileName)
|
||||||
|
} else if check && strings.TrimSpace(string(contents)) != expected {
|
||||||
|
framework.Logf("File %s from pod %s contains '%s' instead of '%s'", fileName, pod.Name, string(contents), expected)
|
||||||
|
failed = append(failed, fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(failed) == 0 {
|
if len(failed) == 0 {
|
||||||
@ -226,7 +241,7 @@ func validateDNSResults(f *framework.Framework, pod *api.Pod, fileNames []string
|
|||||||
framework.Failf("Failed to get pod %s: %v", pod.Name, err)
|
framework.Failf("Failed to get pod %s: %v", pod.Name, err)
|
||||||
}
|
}
|
||||||
// Try to find results for each expected name.
|
// Try to find results for each expected name.
|
||||||
By("looking for the results for each expected name from probiers")
|
By("looking for the results for each expected name from probers")
|
||||||
assertFilesExist(fileNames, "results", pod, f.Client)
|
assertFilesExist(fileNames, "results", pod, f.Client)
|
||||||
|
|
||||||
// TODO: probe from the host, too.
|
// TODO: probe from the host, too.
|
||||||
@ -234,6 +249,33 @@ func validateDNSResults(f *framework.Framework, pod *api.Pod, fileNames []string
|
|||||||
framework.Logf("DNS probes using %s succeeded\n", pod.Name)
|
framework.Logf("DNS probes using %s succeeded\n", pod.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateTargetedProbeOutput(f *framework.Framework, pod *api.Pod, fileNames []string, value string) {
|
||||||
|
|
||||||
|
By("submitting the pod to kubernetes")
|
||||||
|
podClient := f.Client.Pods(f.Namespace.Name)
|
||||||
|
defer func() {
|
||||||
|
By("deleting the pod")
|
||||||
|
defer GinkgoRecover()
|
||||||
|
podClient.Delete(pod.Name, api.NewDeleteOptions(0))
|
||||||
|
}()
|
||||||
|
if _, err := podClient.Create(pod); err != nil {
|
||||||
|
framework.Failf("Failed to create %s pod: %v", pod.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
framework.ExpectNoError(f.WaitForPodRunning(pod.Name))
|
||||||
|
|
||||||
|
By("retrieving the pod")
|
||||||
|
pod, err := podClient.Get(pod.Name)
|
||||||
|
if err != nil {
|
||||||
|
framework.Failf("Failed to get pod %s: %v", pod.Name, err)
|
||||||
|
}
|
||||||
|
// Try to find the expected value for each expected name.
|
||||||
|
By("looking for the results for each expected name from probers")
|
||||||
|
assertFilesContain(fileNames, "results", pod, f.Client, true, value)
|
||||||
|
|
||||||
|
framework.Logf("DNS probes using %s succeeded\n", pod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
func verifyDNSPodIsRunning(f *framework.Framework) {
|
func verifyDNSPodIsRunning(f *framework.Framework) {
|
||||||
systemClient := f.Client.Pods(api.NamespaceSystem)
|
systemClient := f.Client.Pods(api.NamespaceSystem)
|
||||||
By("Waiting for DNS Service to be Running")
|
By("Waiting for DNS Service to be Running")
|
||||||
@ -249,18 +291,23 @@ func verifyDNSPodIsRunning(f *framework.Framework) {
|
|||||||
framework.ExpectNoError(framework.WaitForPodRunningInNamespace(f.Client, &pod))
|
framework.ExpectNoError(framework.WaitForPodRunningInNamespace(f.Client, &pod))
|
||||||
}
|
}
|
||||||
|
|
||||||
func createServiceSpec(serviceName string, isHeadless bool, selector map[string]string) *api.Service {
|
func createServiceSpec(serviceName, externalName string, isHeadless bool, selector map[string]string) *api.Service {
|
||||||
headlessService := &api.Service{
|
headlessService := &api.Service{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Name: serviceName,
|
Name: serviceName,
|
||||||
},
|
},
|
||||||
Spec: api.ServiceSpec{
|
Spec: api.ServiceSpec{
|
||||||
Ports: []api.ServicePort{
|
|
||||||
{Port: 80, Name: "http", Protocol: "TCP"},
|
|
||||||
},
|
|
||||||
Selector: selector,
|
Selector: selector,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if externalName != "" {
|
||||||
|
headlessService.Spec.Type = api.ServiceTypeExternalName
|
||||||
|
headlessService.Spec.ExternalName = externalName
|
||||||
|
} else {
|
||||||
|
headlessService.Spec.Ports = []api.ServicePort{
|
||||||
|
{Port: 80, Name: "http", Protocol: "TCP"},
|
||||||
|
}
|
||||||
|
}
|
||||||
if isHeadless {
|
if isHeadless {
|
||||||
headlessService.Spec.ClusterIP = "None"
|
headlessService.Spec.ClusterIP = "None"
|
||||||
}
|
}
|
||||||
@ -295,8 +342,8 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
hostEntries := []string{hostFQDN, dnsTestPodHostName}
|
hostEntries := []string{hostFQDN, dnsTestPodHostName}
|
||||||
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostEntries, "", "wheezy", f.Namespace.Name)
|
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostEntries, "", "wheezy", f.Namespace.Name)
|
||||||
jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, hostEntries, "", "jessie", f.Namespace.Name)
|
jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, hostEntries, "", "jessie", f.Namespace.Name)
|
||||||
By("Running these commands on wheezy:" + wheezyProbeCmd + "\n")
|
By("Running these commands on wheezy: " + wheezyProbeCmd + "\n")
|
||||||
By("Running these commands on jessie:" + jessieProbeCmd + "\n")
|
By("Running these commands on jessie: " + jessieProbeCmd + "\n")
|
||||||
|
|
||||||
// Run a pod which probes DNS and exposes the results by HTTP.
|
// Run a pod which probes DNS and exposes the results by HTTP.
|
||||||
By("creating a pod to probe DNS")
|
By("creating a pod to probe DNS")
|
||||||
@ -310,7 +357,7 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
testServiceSelector := map[string]string{
|
testServiceSelector := map[string]string{
|
||||||
"dns-test": "true",
|
"dns-test": "true",
|
||||||
}
|
}
|
||||||
headlessService := createServiceSpec(dnsTestServiceName, true, testServiceSelector)
|
headlessService := createServiceSpec(dnsTestServiceName, "", true, testServiceSelector)
|
||||||
_, err := f.Client.Services(f.Namespace.Name).Create(headlessService)
|
_, err := f.Client.Services(f.Namespace.Name).Create(headlessService)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -319,7 +366,7 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
f.Client.Services(f.Namespace.Name).Delete(headlessService.Name)
|
f.Client.Services(f.Namespace.Name).Delete(headlessService.Name)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
regularService := createServiceSpec("test-service-2", false, testServiceSelector)
|
regularService := createServiceSpec("test-service-2", "", false, testServiceSelector)
|
||||||
regularService, err = f.Client.Services(f.Namespace.Name).Create(regularService)
|
regularService, err = f.Client.Services(f.Namespace.Name).Create(regularService)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -341,8 +388,8 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
|
|
||||||
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "wheezy", f.Namespace.Name)
|
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "wheezy", f.Namespace.Name)
|
||||||
jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "jessie", f.Namespace.Name)
|
jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "jessie", f.Namespace.Name)
|
||||||
By("Running these commands on wheezy:" + wheezyProbeCmd + "\n")
|
By("Running these commands on wheezy: " + wheezyProbeCmd + "\n")
|
||||||
By("Running these commands on jessie:" + jessieProbeCmd + "\n")
|
By("Running these commands on jessie: " + jessieProbeCmd + "\n")
|
||||||
|
|
||||||
// Run a pod which probes DNS and exposes the results by HTTP.
|
// Run a pod which probes DNS and exposes the results by HTTP.
|
||||||
By("creating a pod to probe DNS")
|
By("creating a pod to probe DNS")
|
||||||
@ -360,7 +407,7 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
}
|
}
|
||||||
serviceName := "dns-test-service-2"
|
serviceName := "dns-test-service-2"
|
||||||
podHostname := "dns-querier-2"
|
podHostname := "dns-querier-2"
|
||||||
headlessService := createServiceSpec(serviceName, true, testServiceSelector)
|
headlessService := createServiceSpec(serviceName, "", true, testServiceSelector)
|
||||||
_, err := f.Client.Services(f.Namespace.Name).Create(headlessService)
|
_, err := f.Client.Services(f.Namespace.Name).Create(headlessService)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -374,8 +421,8 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
namesToResolve := []string{hostFQDN}
|
namesToResolve := []string{hostFQDN}
|
||||||
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostNames, "", "wheezy", f.Namespace.Name)
|
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostNames, "", "wheezy", f.Namespace.Name)
|
||||||
jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, hostNames, "", "jessie", f.Namespace.Name)
|
jessieProbeCmd, jessieFileNames := createProbeCommand(namesToResolve, hostNames, "", "jessie", f.Namespace.Name)
|
||||||
By("Running these commands on wheezy:" + wheezyProbeCmd + "\n")
|
By("Running these commands on wheezy: " + wheezyProbeCmd + "\n")
|
||||||
By("Running these commands on jessie:" + jessieProbeCmd + "\n")
|
By("Running these commands on jessie: " + jessieProbeCmd + "\n")
|
||||||
|
|
||||||
// Run a pod which probes DNS and exposes the results by HTTP.
|
// Run a pod which probes DNS and exposes the results by HTTP.
|
||||||
By("creating a pod to probe DNS")
|
By("creating a pod to probe DNS")
|
||||||
@ -388,4 +435,68 @@ var _ = framework.KubeDescribe("DNS", func() {
|
|||||||
|
|
||||||
validateDNSResults(f, pod1, append(wheezyFileNames, jessieFileNames...))
|
validateDNSResults(f, pod1, append(wheezyFileNames, jessieFileNames...))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should provide DNS for ExternalName services", func() {
|
||||||
|
// Create a test ExternalName service.
|
||||||
|
By("Creating a test externalName service")
|
||||||
|
serviceName := "dns-test-service-3"
|
||||||
|
externalNameService := createServiceSpec(serviceName, "foo.example.com", false, nil)
|
||||||
|
_, err := f.Client.Services(f.Namespace.Name).Create(externalNameService)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
defer func() {
|
||||||
|
By("deleting the test externalName service")
|
||||||
|
defer GinkgoRecover()
|
||||||
|
f.Client.Services(f.Namespace.Name).Delete(externalNameService.Name)
|
||||||
|
}()
|
||||||
|
|
||||||
|
hostFQDN := fmt.Sprintf("%s.%s.svc.cluster.local", serviceName, f.Namespace.Name)
|
||||||
|
wheezyProbeCmd, wheezyFileName := createTargetedProbeCommand(hostFQDN, "CNAME", "wheezy")
|
||||||
|
jessieProbeCmd, jessieFileName := createTargetedProbeCommand(hostFQDN, "CNAME", "jessie")
|
||||||
|
By("Running these commands on wheezy: " + wheezyProbeCmd + "\n")
|
||||||
|
By("Running these commands on jessie: " + jessieProbeCmd + "\n")
|
||||||
|
|
||||||
|
// Run a pod which probes DNS and exposes the results by HTTP.
|
||||||
|
By("creating a pod to probe DNS")
|
||||||
|
pod1 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, true)
|
||||||
|
|
||||||
|
validateTargetedProbeOutput(f, pod1, []string{wheezyFileName, jessieFileName}, "foo.example.com.")
|
||||||
|
|
||||||
|
// Test changing the externalName field
|
||||||
|
By("changing the externalName to bar.example.com")
|
||||||
|
_, err = updateService(f.Client, f.Namespace.Name, serviceName, func(s *api.Service) {
|
||||||
|
s.Spec.ExternalName = "bar.example.com"
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
wheezyProbeCmd, wheezyFileName = createTargetedProbeCommand(hostFQDN, "CNAME", "wheezy")
|
||||||
|
jessieProbeCmd, jessieFileName = createTargetedProbeCommand(hostFQDN, "CNAME", "jessie")
|
||||||
|
By("Running these commands on wheezy: " + wheezyProbeCmd + "\n")
|
||||||
|
By("Running these commands on jessie: " + jessieProbeCmd + "\n")
|
||||||
|
|
||||||
|
// Run a pod which probes DNS and exposes the results by HTTP.
|
||||||
|
By("creating a second pod to probe DNS")
|
||||||
|
pod2 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, true)
|
||||||
|
|
||||||
|
validateTargetedProbeOutput(f, pod2, []string{wheezyFileName, jessieFileName}, "bar.example.com.")
|
||||||
|
|
||||||
|
// Test changing type from ExternalName to ClusterIP
|
||||||
|
By("changing the service to type=ClusterIP")
|
||||||
|
_, err = updateService(f.Client, f.Namespace.Name, serviceName, func(s *api.Service) {
|
||||||
|
s.Spec.Type = api.ServiceTypeClusterIP
|
||||||
|
s.Spec.ClusterIP = "127.1.2.3"
|
||||||
|
s.Spec.Ports = []api.ServicePort{
|
||||||
|
{Port: 80, Name: "http", Protocol: "TCP"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
wheezyProbeCmd, wheezyFileName = createTargetedProbeCommand(hostFQDN, "A", "wheezy")
|
||||||
|
jessieProbeCmd, jessieFileName = createTargetedProbeCommand(hostFQDN, "A", "jessie")
|
||||||
|
By("Running these commands on wheezy: " + wheezyProbeCmd + "\n")
|
||||||
|
By("Running these commands on jessie: " + jessieProbeCmd + "\n")
|
||||||
|
|
||||||
|
// Run a pod which probes DNS and exposes the results by HTTP.
|
||||||
|
By("creating a third pod to probe DNS")
|
||||||
|
pod3 := createDNSPod(f.Namespace.Name, wheezyProbeCmd, jessieProbeCmd, true)
|
||||||
|
|
||||||
|
validateTargetedProbeOutput(f, pod3, []string{wheezyFileName, jessieFileName}, "127.1.2.3")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -83,7 +83,7 @@ var _ = framework.KubeDescribe("PetSet [Slow] [Feature:PetSet]", func() {
|
|||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
By("creating service " + headlessSvcName + " in namespace " + ns)
|
By("creating service " + headlessSvcName + " in namespace " + ns)
|
||||||
headlessService := createServiceSpec(headlessSvcName, true, labels)
|
headlessService := createServiceSpec(headlessSvcName, "", true, labels)
|
||||||
_, err := c.Services(ns).Create(headlessService)
|
_, err := c.Services(ns).Create(headlessService)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user