diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 0312d81be8..6a50c9ea53 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -3567,6 +3567,9 @@ func getEndpointsFromEndpointSlicesForSubselectedPods(targetPort int32, pods []* continue } for _, endpoint := range endpointSlice.Endpoints { + if !*endpoint.Conditions.Ready { + continue + } for _, address := range endpoint.Addresses { if pod.Status.PodIP == address { addr := ipv6SafeAddrPort(pod.Status.PodIP, targetPort) @@ -3718,6 +3721,9 @@ func (lbc *LoadBalancerController) getEndpointsForPortFromEndpointSlices(endpoin for _, endpointSlicePort := range endpointSlice.Ports { if *endpointSlicePort.Port == targetPort { for _, endpoint := range endpointSlice.Endpoints { + if !*endpoint.Conditions.Ready { + continue + } for _, endpointAddress := range endpoint.Addresses { address := ipv6SafeAddrPort(endpointAddress, *endpointSlicePort.Port) podEndpoint := podEndpoint{ diff --git a/internal/k8s/controller_test.go b/internal/k8s/controller_test.go index 4629e2f22f..b1b352edf8 100644 --- a/internal/k8s/controller_test.go +++ b/internal/k8s/controller_test.go @@ -484,6 +484,8 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsInOneEndpointSlice(t * Name: "foo", } + endpointReady := true + tests := []struct { desc string svc api_v1.Service @@ -526,11 +528,17 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsInOneEndpointSlice(t * Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, { Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -563,6 +571,7 @@ func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsInOnEndpointSlice(t Number: 8080, Name: "foo", } + endpointReady := true tests := []struct { desc string @@ -609,11 +618,17 @@ func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsInOnEndpointSlice(t Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, { Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -647,6 +662,8 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsAcrossTwoEndpointSlice Name: "foo", } + endpointReady := true + tests := []struct { desc string svc api_v1.Service @@ -695,14 +712,197 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsAcrossTwoEndpointSlice Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + { + Addresses: []string{ + "5.6.7.8", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + }, + }, + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + { + Addresses: []string{ + "10.0.0.1", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints, err := lbc.getEndpointsForPortFromEndpointSlices(test.svcEndpointSlices, backendServicePort, &test.svc) + if err != nil { + t.Fatal(err) + } + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("lbc.getEndpointsForPortFromEndpointSlices() got %v, want %v", + gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsInOnEndpointSliceOneEndpointNotReady(t *testing.T) { + endpointPort := int32(8080) + + lbc := LoadBalancerController{ + isNginxPlus: true, + } + + backendServicePort := networking.ServiceBackendPort{ + Number: 8080, + Name: "foo", + } + endpointReadyTrue := true + endpointReadyFalse := false + + tests := []struct { + desc string + svc api_v1.Service + svcEndpointSlices []discovery_v1.EndpointSlice + expectedEndpoints []podEndpoint + }{ + { + desc: "two different endpoints in one endpoint slice", + svc: api_v1.Service{ + TypeMeta: meta_v1.TypeMeta{}, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee-svc", + Namespace: "default", + }, + Spec: api_v1.ServiceSpec{ + Ports: []api_v1.ServicePort{ + { + Name: "foo", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + Status: api_v1.ServiceStatus{}, + }, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, }, { Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, }, }, }, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints, err := lbc.getEndpointsForPortFromEndpointSlices(test.svcEndpointSlices, backendServicePort, &test.svc) + if err != nil { + t.Fatal(err) + } + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("lbc.getEndpointsForPortFromEndpointSlices() got %v, want %v", + gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsAcrossTwoEndpointSlicesOneEndpointNotReady(t *testing.T) { + endpointPort := int32(8080) + + lbc := LoadBalancerController{ + isNginxPlus: true, + } + + backendServicePort := networking.ServiceBackendPort{ + Number: 8080, + Name: "foo", + } + + endpointReadyTrue := true + endpointReadyFalse := false + + tests := []struct { + desc string + svc api_v1.Service + svcEndpointSlices []discovery_v1.EndpointSlice + expectedEndpoints []podEndpoint + }{ + { + desc: "duplicate endpoints across two endpointslices", + svc: api_v1.Service{ + TypeMeta: meta_v1.TypeMeta{}, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee-svc", + Namespace: "default", + }, + Spec: api_v1.ServiceSpec{ + Ports: []api_v1.ServicePort{ + { + Name: "foo", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + Status: api_v1.ServiceStatus{}, + }, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ { Ports: []discovery_v1.EndpointPort{ { @@ -714,11 +914,26 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsAcrossTwoEndpointSlice Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, }, + }, + }, + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ { Addresses: []string{ "10.0.0.1", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, }, }, }, @@ -862,7 +1077,7 @@ func TestGetEndpointsFromEndpointSlices_ErrorsOnNoEndpointSlicesFound(t *testing func TestGetEndpointSlicesBySubselectedPods_FindOnePodInOneEndpointSlice(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -911,6 +1126,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInOneEndpointSlice(t *test Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -931,7 +1149,7 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInOneEndpointSlice(t *test func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDuplicateEndpoints(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -980,6 +1198,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDup Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -994,6 +1215,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDup Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1014,7 +1238,7 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDup func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInOneEndpointSlice(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -1084,11 +1308,17 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInOneEndpointSlice(t *tes Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, { Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1109,7 +1339,7 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInOneEndpointSlice(t *tes func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -1179,6 +1409,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *te Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1193,6 +1426,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *te Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1211,9 +1447,208 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *te } } -func TestGetEndpointSlicesBySubselectedPods_FindNoPods(t *testing.T) { +func TestGetEndpointSlicesBySubselectedPods_FindOnePodEndpointInOneEndpointSliceWithOneEndpointNotReady(t *testing.T) { endpointPort := int32(8080) + endpointReadyTrue := true + endpointReadyFalse := false + boolPointer := func(b bool) *bool { return &b } + tests := []struct { + desc string + targetPort int32 + svcEndpointSlices []discovery_v1.EndpointSlice + pods []*api_v1.Pod + expectedEndpoints []podEndpoint + }{ + { + desc: "find two pods in one endpointslice", + targetPort: 8080, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + MeshPodOwner: configs.MeshPodOwner{ + OwnerType: "deployment", + OwnerName: "deploy-1", + }, + }, + }, + pods: []*api_v1.Pod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "1.2.3.4", + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "5.6.7.8", + }, + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, + }, + { + Addresses: []string{ + "5.6.7.8", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints := getEndpointsFromEndpointSlicesForSubselectedPods(test.targetPort, test.pods, test.svcEndpointSlices) + + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("getEndpointsFromEndpointSlicesForSubselectedPods() = got %v, want %v", gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointSlicesBySubselectedPods_FindOnePodEndpointInTwoEndpointSlicesWithOneEndpointNotReady(t *testing.T) { + endpointPort := int32(8080) + endpointReadyTrue := true + endpointReadyFalse := false + boolPointer := func(b bool) *bool { return &b } + tests := []struct { + desc string + targetPort int32 + svcEndpointSlices []discovery_v1.EndpointSlice + pods []*api_v1.Pod + expectedEndpoints []podEndpoint + }{ + { + desc: "find two pods in two endpointslices", + targetPort: 8080, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + MeshPodOwner: configs.MeshPodOwner{ + OwnerType: "deployment", + OwnerName: "deploy-1", + }, + }, + }, + pods: []*api_v1.Pod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "1.2.3.4", + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "5.6.7.8", + }, + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, + }, + }, + }, + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "5.6.7.8", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, + }, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints := getEndpointsFromEndpointSlicesForSubselectedPods(test.targetPort, test.pods, test.svcEndpointSlices) + + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("getEndpointsFromEndpointSlicesForSubselectedPods() = got %v, want %v", gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointSlicesBySubselectedPods_FindNoPods(t *testing.T) { + endpointPort := int32(8080) + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -1254,6 +1689,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindNoPods(t *testing.T) { Addresses: []string{ "5.4.3.2", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, },