mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Validate endpoints in e2e tests by pod UID and port
Before this patch endpoints were validated by container IP and port. Depending on the endpoint controller logic neither of the two must match for a valid endpoint (e.g. in a Mesos setup). This patch checks that the endpoint targetRef points to the right pod by UID, instead of comparing IPs. A later patch will make sure the compared port is the actual container port, not the host port. /xref mesosphere/kubernetes-mesos#365
This commit is contained in:
parent
fbb5ce6636
commit
8848e26154
@ -348,12 +348,15 @@ var _ = Describe("Kubectl client", func() {
|
|||||||
endpoints, err := c.Endpoints(ns).Get(name)
|
endpoints, err := c.Endpoints(ns).Get(name)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
ipToPort := getPortsByIp(endpoints.Subsets)
|
uidToPort := getPortsByPodUID(endpoints.Subsets)
|
||||||
if len(ipToPort) != 1 {
|
if len(uidToPort) == 0 {
|
||||||
Logf("No IP found, retrying")
|
Logf("No endpoint found, retrying")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, port := range ipToPort {
|
if len(uidToPort) > 1 {
|
||||||
|
Fail("To many endpoints found")
|
||||||
|
}
|
||||||
|
for _, port := range uidToPort {
|
||||||
if port[0] != redisPort {
|
if port[0] != redisPort {
|
||||||
Failf("Wrong endpoint port: %d", port[0])
|
Failf("Wrong endpoint port: %d", port[0])
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/client"
|
"k8s.io/kubernetes/pkg/client"
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
|
"k8s.io/kubernetes/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
"k8s.io/kubernetes/pkg/util/wait"
|
"k8s.io/kubernetes/pkg/util/wait"
|
||||||
)
|
)
|
||||||
@ -101,7 +102,7 @@ var _ = Describe("Services", func() {
|
|||||||
_, err := c.Services(ns).Create(service)
|
_, err := c.Services(ns).Create(service)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{})
|
||||||
|
|
||||||
var names []string
|
var names []string
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -115,25 +116,25 @@ var _ = Describe("Services", func() {
|
|||||||
addEndpointPodOrFail(c, ns, name1, labels, []api.ContainerPort{{ContainerPort: 80}})
|
addEndpointPodOrFail(c, ns, name1, labels, []api.ContainerPort{{ContainerPort: 80}})
|
||||||
names = append(names, name1)
|
names = append(names, name1)
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{name1: {80}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{name1: {80}})
|
||||||
|
|
||||||
name2 := "test2"
|
name2 := "test2"
|
||||||
addEndpointPodOrFail(c, ns, name2, labels, []api.ContainerPort{{ContainerPort: 80}})
|
addEndpointPodOrFail(c, ns, name2, labels, []api.ContainerPort{{ContainerPort: 80}})
|
||||||
names = append(names, name2)
|
names = append(names, name2)
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{name1: {80}, name2: {80}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{name1: {80}, name2: {80}})
|
||||||
|
|
||||||
err = c.Pods(ns).Delete(name1, nil)
|
err = c.Pods(ns).Delete(name1, nil)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
names = []string{name2}
|
names = []string{name2}
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{name2: {80}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{name2: {80}})
|
||||||
|
|
||||||
err = c.Pods(ns).Delete(name2, nil)
|
err = c.Pods(ns).Delete(name2, nil)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
names = []string{}
|
names = []string{}
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{})
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should serve multiport endpoints from pods", func() {
|
It("should serve multiport endpoints from pods", func() {
|
||||||
@ -175,7 +176,7 @@ var _ = Describe("Services", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
port1 := 100
|
port1 := 100
|
||||||
port2 := 101
|
port2 := 101
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{})
|
||||||
|
|
||||||
var names []string
|
var names []string
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -201,35 +202,35 @@ var _ = Describe("Services", func() {
|
|||||||
podname1 := "podname1"
|
podname1 := "podname1"
|
||||||
addEndpointPodOrFail(c, ns, podname1, labels, containerPorts1)
|
addEndpointPodOrFail(c, ns, podname1, labels, containerPorts1)
|
||||||
names = append(names, podname1)
|
names = append(names, podname1)
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{podname1: {port1}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{podname1: {port1}})
|
||||||
|
|
||||||
podname2 := "podname2"
|
podname2 := "podname2"
|
||||||
addEndpointPodOrFail(c, ns, podname2, labels, containerPorts2)
|
addEndpointPodOrFail(c, ns, podname2, labels, containerPorts2)
|
||||||
names = append(names, podname2)
|
names = append(names, podname2)
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{podname1: {port1}, podname2: {port2}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{podname1: {port1}, podname2: {port2}})
|
||||||
|
|
||||||
podname3 := "podname3"
|
podname3 := "podname3"
|
||||||
addEndpointPodOrFail(c, ns, podname3, labels, append(containerPorts1, containerPorts2...))
|
addEndpointPodOrFail(c, ns, podname3, labels, append(containerPorts1, containerPorts2...))
|
||||||
names = append(names, podname3)
|
names = append(names, podname3)
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{podname1: {port1}, podname2: {port2}, podname3: {port1, port2}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{podname1: {port1}, podname2: {port2}, podname3: {port1, port2}})
|
||||||
|
|
||||||
err = c.Pods(ns).Delete(podname1, nil)
|
err = c.Pods(ns).Delete(podname1, nil)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
names = []string{podname2, podname3}
|
names = []string{podname2, podname3}
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{podname2: {port2}, podname3: {port1, port2}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{podname2: {port2}, podname3: {port1, port2}})
|
||||||
|
|
||||||
err = c.Pods(ns).Delete(podname2, nil)
|
err = c.Pods(ns).Delete(podname2, nil)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
names = []string{podname3}
|
names = []string{podname3}
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{podname3: {port1, port2}})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{podname3: {port1, port2}})
|
||||||
|
|
||||||
err = c.Pods(ns).Delete(podname3, nil)
|
err = c.Pods(ns).Delete(podname3, nil)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
names = []string{}
|
names = []string{}
|
||||||
|
|
||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{})
|
validateEndpointsOrFail(c, ns, serviceName, PortsByPodName{})
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should be able to up and down services", func() {
|
It("should be able to up and down services", func() {
|
||||||
@ -1022,60 +1023,63 @@ func validateUniqueOrFail(s []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPortsByIp(subsets []api.EndpointSubset) map[string][]int {
|
func getPortsByPodUID(subsets []api.EndpointSubset) PortsByPodUID {
|
||||||
m := make(map[string][]int)
|
m := PortsByPodUID{}
|
||||||
for _, ss := range subsets {
|
for _, ss := range subsets {
|
||||||
for _, port := range ss.Ports {
|
for _, port := range ss.Ports {
|
||||||
for _, addr := range ss.Addresses {
|
for _, addr := range ss.Addresses {
|
||||||
Logf("Found IP %v and port %v", addr.IP, port.Port)
|
Logf("Found pod %v and port %v", addr.TargetRef.UID, port.Port)
|
||||||
if _, ok := m[addr.IP]; !ok {
|
if _, ok := m[addr.TargetRef.UID]; !ok {
|
||||||
m[addr.IP] = make([]int, 0)
|
m[addr.TargetRef.UID] = make([]int, 0)
|
||||||
}
|
}
|
||||||
m[addr.IP] = append(m[addr.IP], port.Port)
|
m[addr.TargetRef.UID] = append(m[addr.TargetRef.UID], port.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func translatePodNameToIpOrFail(c *client.Client, ns string, expectedEndpoints map[string][]int) map[string][]int {
|
type PortsByPodName map[string][]int
|
||||||
portsByIp := make(map[string][]int)
|
type PortsByPodUID map[types.UID][]int
|
||||||
|
|
||||||
|
func translatePodNameToUIDOrFail(c *client.Client, ns string, expectedEndpoints PortsByPodName) PortsByPodUID {
|
||||||
|
portsByUID := make(PortsByPodUID)
|
||||||
|
|
||||||
for name, portList := range expectedEndpoints {
|
for name, portList := range expectedEndpoints {
|
||||||
pod, err := c.Pods(ns).Get(name)
|
pod, err := c.Pods(ns).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Failf("failed to get pod %s, that's pretty weird. validation failed: %s", name, err)
|
Failf("failed to get pod %s, that's pretty weird. validation failed: %s", name, err)
|
||||||
}
|
}
|
||||||
portsByIp[pod.Status.PodIP] = portList
|
portsByUID[pod.ObjectMeta.UID] = portList
|
||||||
By(fmt.Sprintf(""))
|
By(fmt.Sprintf(""))
|
||||||
}
|
}
|
||||||
By(fmt.Sprintf("successfully translated pod names to ips: %v -> %v on namespace %s", expectedEndpoints, portsByIp, ns))
|
By(fmt.Sprintf("successfully translated pod names to UIDs: %v -> %v on namespace %s", expectedEndpoints, portsByUID, ns))
|
||||||
return portsByIp
|
return portsByUID
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePortsOrFail(endpoints map[string][]int, expectedEndpoints map[string][]int) {
|
func validatePortsOrFail(endpoints PortsByPodUID, expectedEndpoints PortsByPodUID) {
|
||||||
if len(endpoints) != len(expectedEndpoints) {
|
if len(endpoints) != len(expectedEndpoints) {
|
||||||
// should not happen because we check this condition before
|
// should not happen because we check this condition before
|
||||||
Failf("invalid number of endpoints got %v, expected %v", endpoints, expectedEndpoints)
|
Failf("invalid number of endpoints got %v, expected %v", endpoints, expectedEndpoints)
|
||||||
}
|
}
|
||||||
for ip := range expectedEndpoints {
|
for podUID := range expectedEndpoints {
|
||||||
if _, ok := endpoints[ip]; !ok {
|
if _, ok := endpoints[podUID]; !ok {
|
||||||
Failf("endpoint %v not found", ip)
|
Failf("endpoint %v not found", podUID)
|
||||||
}
|
}
|
||||||
if len(endpoints[ip]) != len(expectedEndpoints[ip]) {
|
if len(endpoints[podUID]) != len(expectedEndpoints[podUID]) {
|
||||||
Failf("invalid list of ports for ip %v. Got %v, expected %v", ip, endpoints[ip], expectedEndpoints[ip])
|
Failf("invalid list of ports for uid %v. Got %v, expected %v", podUID, endpoints[podUID], expectedEndpoints[podUID])
|
||||||
}
|
}
|
||||||
sort.Ints(endpoints[ip])
|
sort.Ints(endpoints[podUID])
|
||||||
sort.Ints(expectedEndpoints[ip])
|
sort.Ints(expectedEndpoints[podUID])
|
||||||
for index := range endpoints[ip] {
|
for index := range endpoints[podUID] {
|
||||||
if endpoints[ip][index] != expectedEndpoints[ip][index] {
|
if endpoints[podUID][index] != expectedEndpoints[podUID][index] {
|
||||||
Failf("invalid list of ports for ip %v. Got %v, expected %v", ip, endpoints[ip], expectedEndpoints[ip])
|
Failf("invalid list of ports for uid %v. Got %v, expected %v", podUID, endpoints[podUID], expectedEndpoints[podUID])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateEndpointsOrFail(c *client.Client, namespace, serviceName string, expectedEndpoints map[string][]int) {
|
func validateEndpointsOrFail(c *client.Client, namespace, serviceName string, expectedEndpoints PortsByPodName) {
|
||||||
By(fmt.Sprintf("Waiting up to %v for service %s in namespace %s to expose endpoints %v", serviceStartTimeout, serviceName, namespace, expectedEndpoints))
|
By(fmt.Sprintf("Waiting up to %v for service %s in namespace %s to expose endpoints %v", serviceStartTimeout, serviceName, namespace, expectedEndpoints))
|
||||||
for start := time.Now(); time.Since(start) < serviceStartTimeout; time.Sleep(5 * time.Second) {
|
for start := time.Now(); time.Since(start) < serviceStartTimeout; time.Sleep(5 * time.Second) {
|
||||||
endpoints, err := c.Endpoints(namespace).Get(serviceName)
|
endpoints, err := c.Endpoints(namespace).Get(serviceName)
|
||||||
@ -1085,16 +1089,17 @@ func validateEndpointsOrFail(c *client.Client, namespace, serviceName string, ex
|
|||||||
}
|
}
|
||||||
Logf("Found endpoints %v", endpoints)
|
Logf("Found endpoints %v", endpoints)
|
||||||
|
|
||||||
portsByIp := getPortsByIp(endpoints.Subsets)
|
portsByPodUID := getPortsByPodUID(endpoints.Subsets)
|
||||||
Logf("Found ports by ip %v", portsByIp)
|
Logf("Found port by pod UID %v", portsByPodUID)
|
||||||
|
|
||||||
if len(portsByIp) == len(expectedEndpoints) {
|
expectedPortsByPodUID := translatePodNameToUIDOrFail(c, namespace, expectedEndpoints)
|
||||||
expectedPortsByIp := translatePodNameToIpOrFail(c, namespace, expectedEndpoints)
|
if len(portsByPodUID) == len(expectedEndpoints) {
|
||||||
validatePortsOrFail(portsByIp, expectedPortsByIp)
|
validatePortsOrFail(portsByPodUID, expectedPortsByPodUID)
|
||||||
By(fmt.Sprintf("Successfully validated that service %s in namespace %s exposes endpoints %v (%v elapsed)", serviceName, namespace, expectedEndpoints, time.Since(start)))
|
By(fmt.Sprintf("Successfully validated that service %s in namespace %s exposes endpoints %v (%v elapsed)", serviceName, namespace, expectedEndpoints, time.Since(start)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Logf("Unexpected number of endpoints: found %v, expected %v (%v elapsed, ignoring for 5s)", portsByIp, expectedEndpoints, time.Since(start))
|
|
||||||
|
Logf("Unexpected number of endpoints: found %v, expected %v (%v elapsed, ignoring for 5s)", portsByPodUID, expectedEndpoints, time.Since(start))
|
||||||
}
|
}
|
||||||
Failf("Timed out waiting for service %s in namespace %s to expose endpoints %v (%v elapsed)", serviceName, namespace, expectedEndpoints, serviceStartTimeout)
|
Failf("Timed out waiting for service %s in namespace %s to expose endpoints %v (%v elapsed)", serviceName, namespace, expectedEndpoints, serviceStartTimeout)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user