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:
Kubernetes Submit Queue 2016-08-25 17:23:01 -07:00 committed by GitHub
commit 36a6aee27f
5 changed files with 131 additions and 20 deletions

View File

@ -44,7 +44,7 @@ spec:
spec:
containers:
- name: kubedns
image: gcr.io/google_containers/kubedns-amd64:1.6
image: gcr.io/google_containers/kubedns-amd64:1.7
resources:
# TODO: Set memory limits when we've profiled the container for large
# clusters, then set request = limit to keep this container in

View File

@ -44,7 +44,7 @@ spec:
spec:
containers:
- name: kubedns
image: gcr.io/google_containers/kubedns-amd64:1.6
image: gcr.io/google_containers/kubedns-amd64:1.7
resources:
# TODO: Set memory limits when we've profiled the container for large
# clusters, then set request = limit to keep this container in

View File

@ -44,7 +44,7 @@ spec:
spec:
containers:
- name: kubedns
image: gcr.io/google_containers/kubedns-amd64:1.6
image: gcr.io/google_containers/kubedns-amd64:1.7
resources:
# TODO: Set memory limits when we've profiled the container for large
# clusters, then set request = limit to keep this container in

View File

@ -164,7 +164,18 @@ func createProbeCommand(namesToResolve []string, hostEntries []string, ptrLookup
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) {
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
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 {
return false, err
}
var contents []byte
for _, fileName := range fileNames {
if subResourceProxyAvailable {
_, err = client.Get().
contents, err = client.Get().
Namespace(pod.Namespace).
Resource("pods").
SubResource("proxy").
@ -183,7 +195,7 @@ func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *
Suffix(fileDir, fileName).
Do().Raw()
} else {
_, err = client.Get().
contents, err = client.Get().
Prefix("proxy").
Resource("pods").
Namespace(pod.Namespace).
@ -194,6 +206,9 @@ func assertFilesExist(fileNames []string, fileDir string, pod *api.Pod, client *
if err != nil {
framework.Logf("Unable to read %s from pod %s: %v", fileName, pod.Name, err)
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 {
@ -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)
}
// 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)
// 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)
}
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) {
systemClient := f.Client.Pods(api.NamespaceSystem)
By("Waiting for DNS Service to be Running")
@ -249,18 +291,23 @@ func verifyDNSPodIsRunning(f *framework.Framework) {
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{
ObjectMeta: api.ObjectMeta{
Name: serviceName,
},
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{Port: 80, Name: "http", Protocol: "TCP"},
},
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 {
headlessService.Spec.ClusterIP = "None"
}
@ -295,8 +342,8 @@ var _ = framework.KubeDescribe("DNS", func() {
hostEntries := []string{hostFQDN, dnsTestPodHostName}
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostEntries, "", "wheezy", 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 jessie:" + jessieProbeCmd + "\n")
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")
@ -310,7 +357,7 @@ var _ = framework.KubeDescribe("DNS", func() {
testServiceSelector := map[string]string{
"dns-test": "true",
}
headlessService := createServiceSpec(dnsTestServiceName, true, testServiceSelector)
headlessService := createServiceSpec(dnsTestServiceName, "", true, testServiceSelector)
_, err := f.Client.Services(f.Namespace.Name).Create(headlessService)
Expect(err).NotTo(HaveOccurred())
defer func() {
@ -319,7 +366,7 @@ var _ = framework.KubeDescribe("DNS", func() {
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)
Expect(err).NotTo(HaveOccurred())
defer func() {
@ -341,8 +388,8 @@ var _ = framework.KubeDescribe("DNS", func() {
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, nil, regularService.Spec.ClusterIP, "wheezy", 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 jessie:" + jessieProbeCmd + "\n")
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")
@ -360,7 +407,7 @@ var _ = framework.KubeDescribe("DNS", func() {
}
serviceName := "dns-test-service-2"
podHostname := "dns-querier-2"
headlessService := createServiceSpec(serviceName, true, testServiceSelector)
headlessService := createServiceSpec(serviceName, "", true, testServiceSelector)
_, err := f.Client.Services(f.Namespace.Name).Create(headlessService)
Expect(err).NotTo(HaveOccurred())
defer func() {
@ -374,8 +421,8 @@ var _ = framework.KubeDescribe("DNS", func() {
namesToResolve := []string{hostFQDN}
wheezyProbeCmd, wheezyFileNames := createProbeCommand(namesToResolve, hostNames, "", "wheezy", 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 jessie:" + jessieProbeCmd + "\n")
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")
@ -388,4 +435,68 @@ var _ = framework.KubeDescribe("DNS", func() {
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")
})
})

View File

@ -83,7 +83,7 @@ var _ = framework.KubeDescribe("PetSet [Slow] [Feature:PetSet]", func() {
BeforeEach(func() {
By("creating service " + headlessSvcName + " in namespace " + ns)
headlessService := createServiceSpec(headlessSvcName, true, labels)
headlessService := createServiceSpec(headlessSvcName, "", true, labels)
_, err := c.Services(ns).Create(headlessService)
Expect(err).NotTo(HaveOccurred())
})