Skip to content

Commit 661423d

Browse files
authored
improve missing service handling for targetGroupBinding (#1888)
1 parent 2d84a75 commit 661423d

File tree

5 files changed

+133
-3
lines changed

5 files changed

+133
-3
lines changed

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func main() {
104104
sgReconciler := networking.NewDefaultSecurityGroupReconciler(sgManager, ctrl.Log)
105105
subnetResolver := networking.NewDefaultSubnetsResolver(cloud.EC2(), cloud.VpcID(), controllerCFG.ClusterName, ctrl.Log.WithName("subnets-resolver"))
106106
tgbResManager := targetgroupbinding.NewDefaultResourceManager(mgr.GetClient(), cloud.ELBV2(),
107-
podInfoRepo, podENIResolver, nodeENIResolver, sgManager, sgReconciler, cloud.VpcID(), controllerCFG.ClusterName, ctrl.Log)
107+
podInfoRepo, podENIResolver, nodeENIResolver, sgManager, sgReconciler, cloud.VpcID(), controllerCFG.ClusterName, mgr.GetEventRecorderFor("targetGroupBinding"), ctrl.Log)
108108
ingGroupReconciler := ingress.NewGroupReconciler(cloud, mgr.GetClient(), mgr.GetEventRecorderFor("ingress"),
109109
finalizerManager, sgManager, sgReconciler, subnetResolver,
110110
controllerCFG, ctrl.Log.WithName("controllers").WithName("ingress"))

pkg/backend/endpoint_resolver.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ package backend
22

33
import (
44
"context"
5+
"fmt"
56
"github.com/go-logr/logr"
67
"github.com/pkg/errors"
78
corev1 "k8s.io/api/core/v1"
9+
apierrors "k8s.io/apimachinery/pkg/api/errors"
810
"k8s.io/apimachinery/pkg/types"
911
"k8s.io/apimachinery/pkg/util/intstr"
1012
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
1113
"sigs.k8s.io/controller-runtime/pkg/client"
1214
)
1315

16+
var ErrNotFound = errors.New("backend not found")
17+
1418
// TODO: for pod endpoints, we currently rely on endpoints events, we might change to use pod events directly in the future.
1519
// under current implementation with pod readinessGate enabled, an unready endpoint but not match our inclusionCriteria won't be registered,
1620
// and it won't turn ready due to blocked by readinessGate, and no future endpoint events will trigger.
@@ -55,9 +59,13 @@ func (r *defaultEndpointResolver) ResolvePodEndpoints(ctx context.Context, svcKe
5559
if err != nil {
5660
return nil, false, err
5761
}
62+
5863
epsKey := k8s.NamespacedName(svc) // k8s Endpoints have same name as k8s Service
5964
eps := &corev1.Endpoints{}
6065
if err := r.k8sClient.Get(ctx, epsKey, eps); err != nil {
66+
if apierrors.IsNotFound(err) {
67+
return nil, false, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
68+
}
6169
return nil, false, err
6270
}
6371

@@ -149,11 +157,14 @@ func (r *defaultEndpointResolver) ResolveNodePortEndpoints(ctx context.Context,
149157
func (r *defaultEndpointResolver) findServiceAndServicePort(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString) (*corev1.Service, corev1.ServicePort, error) {
150158
svc := &corev1.Service{}
151159
if err := r.k8sClient.Get(ctx, svcKey, svc); err != nil {
160+
if apierrors.IsNotFound(err) {
161+
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
162+
}
152163
return nil, corev1.ServicePort{}, err
153164
}
154165
svcPort, err := k8s.LookupServicePort(svc, port)
155166
if err != nil {
156-
return nil, corev1.ServicePort{}, err
167+
return nil, corev1.ServicePort{}, fmt.Errorf("%w: %v", ErrNotFound, err.Error())
157168
}
158169

159170
return svc, svcPort, nil

pkg/backend/endpoint_resolver_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package backend
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"github.com/golang/mock/gomock"
78
"github.com/google/go-cmp/cmp"
89
"github.com/google/go-cmp/cmp/cmpopts"
@@ -131,6 +132,21 @@ func Test_defaultEndpointResolver_ResolvePodEndpoints(t *testing.T) {
131132
},
132133
},
133134
}
135+
svc1WithoutHTTPPort := &corev1.Service{
136+
ObjectMeta: metav1.ObjectMeta{
137+
Namespace: testNS,
138+
Name: "svc-1",
139+
},
140+
Spec: corev1.ServiceSpec{
141+
Type: corev1.ServiceTypeClusterIP,
142+
Ports: []corev1.ServicePort{
143+
{
144+
Name: "https",
145+
Port: 443,
146+
},
147+
},
148+
},
149+
}
134150
ep1A := &corev1.Endpoints{
135151
ObjectMeta: metav1.ObjectMeta{
136152
Namespace: testNS,
@@ -704,6 +720,56 @@ func Test_defaultEndpointResolver_ResolvePodEndpoints(t *testing.T) {
704720
},
705721
wantContainsPotentialReadyEndpoints: true,
706722
},
723+
{
724+
name: "service not found",
725+
env: env{
726+
services: []*corev1.Service{},
727+
endpointsList: []*corev1.Endpoints{},
728+
},
729+
fields: fields{
730+
podInfoRepoGetCalls: []podInfoRepoGetCall{},
731+
},
732+
args: args{
733+
svcKey: k8s.NamespacedName(svc1),
734+
port: intstr.FromString("http"),
735+
opts: nil,
736+
},
737+
want: []PodEndpoint{},
738+
wantContainsPotentialReadyEndpoints: false,
739+
wantErr: fmt.Errorf("%w: %v", ErrNotFound, "services \"svc-1\" not found"),
740+
},
741+
{
742+
name: "service port not found",
743+
env: env{
744+
services: []*corev1.Service{svc1WithoutHTTPPort},
745+
endpointsList: []*corev1.Endpoints{},
746+
},
747+
fields: fields{
748+
podInfoRepoGetCalls: []podInfoRepoGetCall{},
749+
},
750+
args: args{
751+
svcKey: k8s.NamespacedName(svc1),
752+
port: intstr.FromString("http"),
753+
opts: nil,
754+
},
755+
wantErr: fmt.Errorf("%w: %v", ErrNotFound, "unable to find port http on service test-ns/svc-1"),
756+
},
757+
{
758+
name: "endpoints not found",
759+
env: env{
760+
services: []*corev1.Service{svc1},
761+
endpointsList: []*corev1.Endpoints{},
762+
},
763+
fields: fields{
764+
podInfoRepoGetCalls: []podInfoRepoGetCall{},
765+
},
766+
args: args{
767+
svcKey: k8s.NamespacedName(svc1),
768+
port: intstr.FromString("http"),
769+
opts: nil,
770+
},
771+
wantErr: fmt.Errorf("%w: %v", ErrNotFound, "endpoints \"svc-1\" not found"),
772+
},
707773
}
708774

709775
for _, tt := range tests {
@@ -850,6 +916,21 @@ func Test_defaultEndpointResolver_ResolveNodePortEndpoints(t *testing.T) {
850916
},
851917
},
852918
}
919+
svc1WithoutHTTPPort := &corev1.Service{
920+
ObjectMeta: metav1.ObjectMeta{
921+
Namespace: testNS,
922+
Name: "svc-1",
923+
},
924+
Spec: corev1.ServiceSpec{
925+
Type: corev1.ServiceTypeClusterIP,
926+
Ports: []corev1.ServicePort{
927+
{
928+
Name: "https",
929+
Port: 443,
930+
},
931+
},
932+
},
933+
}
853934
svc2 := &corev1.Service{
854935
ObjectMeta: metav1.ObjectMeta{
855936
Namespace: testNS,
@@ -957,6 +1038,32 @@ func Test_defaultEndpointResolver_ResolveNodePortEndpoints(t *testing.T) {
9571038
},
9581039
wantErr: errors.New("service type must be either 'NodePort' or 'LoadBalancer': test-ns/svc-2"),
9591040
},
1041+
{
1042+
name: "service not found",
1043+
env: env{
1044+
nodes: []*corev1.Node{node1, node2, node3, node4},
1045+
services: []*corev1.Service{},
1046+
},
1047+
args: args{
1048+
svcKey: k8s.NamespacedName(svc1),
1049+
port: intstr.FromString("http"),
1050+
opts: []EndpointResolveOption{WithNodeSelector(labels.Everything())},
1051+
},
1052+
wantErr: fmt.Errorf("%w: %v", ErrNotFound, "services \"svc-1\" not found"),
1053+
},
1054+
{
1055+
name: "service port not found",
1056+
env: env{
1057+
nodes: []*corev1.Node{node1, node2, node3, node4},
1058+
services: []*corev1.Service{svc1WithoutHTTPPort},
1059+
},
1060+
args: args{
1061+
svcKey: k8s.NamespacedName(svc1),
1062+
port: intstr.FromString("http"),
1063+
opts: []EndpointResolveOption{WithNodeSelector(labels.Everything())},
1064+
},
1065+
wantErr: fmt.Errorf("%w: %v", ErrNotFound, "unable to find port http on service test-ns/svc-1"),
1066+
},
9601067
}
9611068
for _, tt := range tests {
9621069
t.Run(tt.name, func(t *testing.T) {

pkg/k8s/events.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ const (
2424
TargetGroupBindingEventReasonFailedRemoveFinalizer = "FailedRemoveFinalizer"
2525
TargetGroupBindingEventReasonFailedUpdateStatus = "FailedUpdateStatus"
2626
TargetGroupBindingEventReasonFailedCleanup = "FailedCleanup"
27+
TargetGroupBindingEventReasonBackendNotFound = "BackendNotFound"
2728
TargetGroupBindingEventReasonSuccessfullyReconciled = "SuccessfullyReconciled"
2829
)

pkg/targetgroupbinding/resource_manager.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"k8s.io/client-go/tools/record"
78
"time"
89

910
awssdk "github.com/aws/aws-sdk-go/aws"
@@ -38,7 +39,7 @@ type ResourceManager interface {
3839
func NewDefaultResourceManager(k8sClient client.Client, elbv2Client services.ELBV2,
3940
podInfoRepo k8s.PodInfoRepo, podENIResolver networking.PodENIInfoResolver, nodeENIResolver networking.NodeENIInfoResolver,
4041
sgManager networking.SecurityGroupManager, sgReconciler networking.SecurityGroupReconciler,
41-
vpcID string, clusterName string, logger logr.Logger) *defaultResourceManager {
42+
vpcID string, clusterName string, eventRecorder record.EventRecorder, logger logr.Logger) *defaultResourceManager {
4243
targetsManager := NewCachedTargetsManager(elbv2Client, logger)
4344
endpointResolver := backend.NewDefaultEndpointResolver(k8sClient, podInfoRepo, logger)
4445
networkingManager := NewDefaultNetworkingManager(k8sClient, podENIResolver, nodeENIResolver, sgManager, sgReconciler, vpcID, clusterName, logger)
@@ -47,6 +48,7 @@ func NewDefaultResourceManager(k8sClient client.Client, elbv2Client services.ELB
4748
targetsManager: targetsManager,
4849
endpointResolver: endpointResolver,
4950
networkingManager: networkingManager,
51+
eventRecorder: eventRecorder,
5052
logger: logger,
5153

5254
targetHealthRequeueDuration: defaultTargetHealthRequeueDuration,
@@ -61,6 +63,7 @@ type defaultResourceManager struct {
6163
targetsManager TargetsManager
6264
endpointResolver backend.EndpointResolver
6365
networkingManager NetworkingManager
66+
eventRecorder record.EventRecorder
6467
logger logr.Logger
6568

6669
targetHealthRequeueDuration time.Duration
@@ -95,6 +98,10 @@ func (m *defaultResourceManager) reconcileWithIPTargetType(ctx context.Context,
9598
}
9699
endpoints, containsPotentialReadyEndpoints, err := m.endpointResolver.ResolvePodEndpoints(ctx, svcKey, tgb.Spec.ServiceRef.Port, resolveOpts...)
97100
if err != nil {
101+
if errors.Is(err, backend.ErrNotFound) {
102+
m.eventRecorder.Event(tgb, corev1.EventTypeWarning, k8s.TargetGroupBindingEventReasonBackendNotFound, err.Error())
103+
return m.Cleanup(ctx, tgb)
104+
}
98105
return err
99106
}
100107

@@ -146,6 +153,10 @@ func (m *defaultResourceManager) reconcileWithInstanceTargetType(ctx context.Con
146153
resolveOpts := []backend.EndpointResolveOption{backend.WithNodeSelector(nodeSelector)}
147154
endpoints, err := m.endpointResolver.ResolveNodePortEndpoints(ctx, svcKey, tgb.Spec.ServiceRef.Port, resolveOpts...)
148155
if err != nil {
156+
if errors.Is(err, backend.ErrNotFound) {
157+
m.eventRecorder.Event(tgb, corev1.EventTypeWarning, k8s.TargetGroupBindingEventReasonBackendNotFound, err.Error())
158+
return m.Cleanup(ctx, tgb)
159+
}
149160
return err
150161
}
151162
tgARN := tgb.Spec.TargetGroupARN

0 commit comments

Comments
 (0)