Skip to content

Commit a979b4a

Browse files
authored
feat(multicluster): propagate service labels to endpoints (#13583)
When Kubernetes creates endpoints for a service, it propagates the labels from the parent service to the endpoints, allowing these labels to be used for discovery. When using Linkerd mirrored endpoints, this ability was lost until now. This change simply mimics the vanilla Kubernetes behavior by propagating the remote service labels to mirror endpoints. https://github.com/kubernetes/kubernetes/blob/a716ea756d87f60900dbbb500fc27ae30f7bd384/pkg/controller/endpoint/endpoints_controller.go#L506 Signed-off-by: Maxime Brunet <[email protected]>
1 parent 6714331 commit a979b4a

File tree

4 files changed

+129
-103
lines changed

4 files changed

+129
-103
lines changed

multicluster/service-mirror/cluster_watcher.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,16 @@ func (rcsw *RemoteClusterServiceWatcher) getMirrorServiceLabels(remoteService *c
289289
return labels
290290
}
291291

292+
// Provides labels for mirror endpoint. Copies all labels from the exported
293+
// service to the mirror endpoint (except labels with the "SvcMirrorPrefix").
294+
func (rcsw *RemoteClusterServiceWatcher) getMirrorEndpointLabels(exportedService *corev1.Service) map[string]string {
295+
labels := rcsw.getCommonServiceLabels(exportedService)
296+
297+
labels[consts.RemoteClusterNameLabel] = rcsw.link.Spec.TargetClusterName
298+
299+
return labels
300+
}
301+
292302
// Provides labels for federated services. Copies all labels from the remote
293303
// service to the federated service (except labels with the "SvcMirrorPrefix").
294304
func (rcsw *RemoteClusterServiceWatcher) getFederatedServiceLabels(remoteService *corev1.Service) map[string]string {
@@ -1000,10 +1010,7 @@ func (rcsw *RemoteClusterServiceWatcher) createGatewayEndpoints(ctx context.Cont
10001010
ObjectMeta: metav1.ObjectMeta{
10011011
Name: localServiceName,
10021012
Namespace: exportedService.Namespace,
1003-
Labels: map[string]string{
1004-
consts.MirroredResourceLabel: "true",
1005-
consts.RemoteClusterNameLabel: rcsw.link.Spec.TargetClusterName,
1006-
},
1013+
Labels: rcsw.getMirrorEndpointLabels(exportedService),
10071014
Annotations: map[string]string{
10081015
consts.RemoteServiceFqName: fmt.Sprintf("%s.%s.svc.%s", exportedService.Name, exportedService.Namespace, rcsw.link.Spec.TargetClusterDomain),
10091016
},

multicluster/service-mirror/cluster_watcher_headless.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,7 @@ func (rcsw *RemoteClusterServiceWatcher) createHeadlessMirrorEndpoints(ctx conte
290290
ObjectMeta: metav1.ObjectMeta{
291291
Name: headlessMirrorServiceName,
292292
Namespace: exportedService.Namespace,
293-
Labels: map[string]string{
294-
consts.MirroredResourceLabel: "true",
295-
consts.RemoteClusterNameLabel: rcsw.link.Spec.TargetClusterName,
296-
},
293+
Labels: rcsw.getMirrorEndpointLabels(exportedService),
297294
Annotations: map[string]string{
298295
consts.RemoteServiceFqName: fmt.Sprintf("%s.%s.svc.%s", exportedService.Name, exportedService.Namespace, rcsw.link.Spec.TargetClusterDomain),
299296
},

multicluster/service-mirror/cluster_watcher_mirroring_test.go

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ type mirroringTestCase struct {
2626
}
2727

2828
func (tc *mirroringTestCase) run(t *testing.T) {
29+
t.Helper()
2930
t.Run(tc.description, func(t *testing.T) {
31+
t.Helper()
3032

3133
q := workqueue.NewTypedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any]())
3234
localAPI, err := tc.environment.runEnvironment(q)
@@ -52,7 +54,7 @@ func (tc *mirroringTestCase) run(t *testing.T) {
5254
}
5355

5456
if err := diffServices(expected, actual); err != nil {
55-
t.Fatal(err)
57+
t.Fatalf("service %s/%s: %v", expected.Namespace, expected.Name, err)
5658
}
5759
}
5860
}
@@ -68,7 +70,7 @@ func (tc *mirroringTestCase) run(t *testing.T) {
6870
}
6971

7072
if err := diffEndpoints(expected, actual); err != nil {
71-
t.Fatal(err)
73+
t.Fatalf("endpoint %s/%s: %v", expected.Namespace, expected.Name, err)
7274
}
7375
}
7476
}
@@ -99,6 +101,7 @@ func TestRemoteServiceCreatedMirroring(t *testing.T) {
99101
"service-one-remote",
100102
"ns1",
101103
"111",
104+
map[string]string{"lk": "lv"},
102105
[]corev1.ServicePort{
103106
{
104107
Name: "port1",
@@ -113,7 +116,7 @@ func TestRemoteServiceCreatedMirroring(t *testing.T) {
113116
}),
114117
},
115118
expectedLocalEndpoints: []*corev1.Endpoints{
116-
endpoints("service-one-remote", "ns1", "192.0.2.127", "gateway-identity", []corev1.EndpointPort{
119+
endpoints("service-one-remote", "ns1", map[string]string{"lk": "lv"}, "192.0.2.127", "gateway-identity", []corev1.EndpointPort{
117120
{
118121
Name: "port1",
119122
Port: 888,
@@ -135,6 +138,7 @@ func TestRemoteServiceCreatedMirroring(t *testing.T) {
135138
"service-one-remote",
136139
"ns2",
137140
"111",
141+
map[string]string{"lk": "lv"},
138142
[]corev1.ServicePort{
139143
{
140144
Name: "port1",
@@ -152,6 +156,7 @@ func TestRemoteServiceCreatedMirroring(t *testing.T) {
152156
"service-one-remote",
153157
"ns2",
154158
"112",
159+
map[string]string{"lk": "lv"},
155160
[]corev1.ServicePort{
156161
{
157162
Name: "port1",
@@ -167,7 +172,7 @@ func TestRemoteServiceCreatedMirroring(t *testing.T) {
167172
),
168173
},
169174
expectedLocalEndpoints: []*corev1.Endpoints{
170-
headlessMirrorEndpoints("service-one-remote", "ns2", "gateway-identity", []corev1.EndpointPort{
175+
headlessMirrorEndpoints("service-one-remote", "ns2", map[string]string{"lk": "lv"}, "gateway-identity", []corev1.EndpointPort{
171176
{
172177
Name: "port1",
173178
Port: 555,
@@ -182,6 +187,7 @@ func TestRemoteServiceCreatedMirroring(t *testing.T) {
182187
endpointMirrorEndpoints(
183188
"service-one-remote",
184189
"ns2",
190+
map[string]string{"lk": "lv"},
185191
"pod-0",
186192
"192.0.2.129",
187193
"gateway-identity",
@@ -388,7 +394,7 @@ func TestLocalNamespaceCreatedAfterServiceExport(t *testing.T) {
388394
remoteAPI, err := k8s.NewFakeAPI(
389395
asYaml(gateway("existing-gateway", "existing-namespace", "222", "192.0.2.127", "mc-gateway", 888, "gateway-identity", defaultProbePort, defaultProbePath, defaultProbePeriod)),
390396
asYaml(remoteService("service-one", "ns1", "111", map[string]string{consts.DefaultExportedServiceSelector: "true"}, []corev1.ServicePort{})),
391-
asYaml(endpoints("service-one", "ns1", "192.0.2.127", "gateway-identity", []corev1.EndpointPort{})),
397+
asYaml(endpoints("service-one", "ns1", nil, "192.0.2.127", "gateway-identity", []corev1.EndpointPort{})),
392398
)
393399
if err != nil {
394400
t.Fatal(err)
@@ -480,7 +486,7 @@ func TestServiceCreatedGatewayAlive(t *testing.T) {
480486
remoteAPI, err := k8s.NewFakeAPI(
481487
asYaml(gateway("gateway", "gateway-ns", "1", "192.0.0.1", "gateway", 888, "gateway-identity", defaultProbePort, defaultProbePath, defaultProbePeriod)),
482488
asYaml(remoteService("svc", "ns", "1", map[string]string{consts.DefaultExportedServiceSelector: "true"}, []corev1.ServicePort{})),
483-
asYaml(endpoints("svc", "ns", "192.0.0.1", "gateway-identity", []corev1.EndpointPort{})),
489+
asYaml(endpoints("svc", "ns", nil, "192.0.0.1", "gateway-identity", []corev1.EndpointPort{})),
484490
)
485491
if err != nil {
486492
t.Fatal(err)
@@ -631,7 +637,7 @@ func TestServiceCreatedGatewayDown(t *testing.T) {
631637
remoteAPI, err := k8s.NewFakeAPI(
632638
asYaml(gateway("gateway", "gateway-ns", "1", "192.0.0.1", "gateway", 888, "gateway-identity", defaultProbePort, defaultProbePath, defaultProbePeriod)),
633639
asYaml(remoteService("svc", "ns", "1", map[string]string{consts.DefaultExportedServiceSelector: "true"}, []corev1.ServicePort{})),
634-
asYaml(endpoints("svc", "ns", "192.0.0.1", "gateway-identity", []corev1.EndpointPort{})),
640+
asYaml(endpoints("svc", "ns", nil, "192.0.0.1", "gateway-identity", []corev1.EndpointPort{})),
635641
)
636642
if err != nil {
637643
t.Fatal(err)
@@ -747,7 +753,7 @@ func TestRemoteServiceUpdatedMirroring(t *testing.T) {
747753
description: "updates service ports on both service and endpoints",
748754
environment: updateServiceWithChangedPorts,
749755
expectedLocalServices: []*corev1.Service{
750-
mirrorService("test-service-remote", "test-namespace", "currentServiceResVersion",
756+
mirrorService("test-service-remote", "test-namespace", "currentServiceResVersion", nil,
751757
[]corev1.ServicePort{
752758
{
753759
Name: "port1",
@@ -763,7 +769,7 @@ func TestRemoteServiceUpdatedMirroring(t *testing.T) {
763769
},
764770

765771
expectedLocalEndpoints: []*corev1.Endpoints{
766-
endpoints("test-service-remote", "test-namespace", "192.0.2.127", "gateway-identity", []corev1.EndpointPort{
772+
endpoints("test-service-remote", "test-namespace", nil, "192.0.2.127", "gateway-identity", []corev1.EndpointPort{
767773
{
768774
Name: "port1",
769775
Port: 888,
@@ -847,7 +853,7 @@ func TestRemoteEndpointsUpdatedMirroring(t *testing.T) {
847853
description: "updates headless mirror service with new remote Endpoints hosts",
848854
environment: updateEndpointsWithChangedHosts,
849855
expectedLocalServices: []*corev1.Service{
850-
headlessMirrorService("service-two-remote", "eptest", "222", []corev1.ServicePort{
856+
headlessMirrorService("service-two-remote", "eptest", "222", nil, []corev1.ServicePort{
851857
{
852858
Name: "port1",
853859
Protocol: "TCP",
@@ -859,7 +865,7 @@ func TestRemoteEndpointsUpdatedMirroring(t *testing.T) {
859865
Port: 666,
860866
},
861867
}),
862-
endpointMirrorService("pod-0", "service-two-remote", "eptest", "333", []corev1.ServicePort{
868+
endpointMirrorService("pod-0", "service-two-remote", "eptest", "333", nil, []corev1.ServicePort{
863869
{
864870
Name: "port1",
865871
Protocol: "TCP",
@@ -871,7 +877,7 @@ func TestRemoteEndpointsUpdatedMirroring(t *testing.T) {
871877
Port: 666,
872878
},
873879
}),
874-
endpointMirrorService("pod-1", "service-two-remote", "eptest", "112", []corev1.ServicePort{
880+
endpointMirrorService("pod-1", "service-two-remote", "eptest", "112", nil, []corev1.ServicePort{
875881
{
876882
Name: "port1",
877883
Protocol: "TCP",
@@ -906,6 +912,7 @@ func TestRemoteEndpointsUpdatedMirroring(t *testing.T) {
906912
endpointMirrorEndpoints(
907913
"service-two-remote",
908914
"eptest",
915+
nil,
909916
"pod-0",
910917
"192.0.2.127",
911918
"gateway-identity",
@@ -924,6 +931,7 @@ func TestRemoteEndpointsUpdatedMirroring(t *testing.T) {
924931
endpointMirrorEndpoints(
925932
"service-two-remote",
926933
"eptest",
934+
nil,
927935
"pod-1",
928936
"192.0.2.127",
929937
"gateway-identity",
@@ -965,15 +973,15 @@ func TestGcOrphanedServicesMirroring(t *testing.T) {
965973
description: "deletes mirrored resources that are no longer present on the remote cluster",
966974
environment: gcTriggered,
967975
expectedLocalServices: []*corev1.Service{
968-
mirrorService("test-service-1-remote", "test-namespace", "", nil),
969-
headlessMirrorService("test-headless-service-remote", "test-namespace", "", nil),
970-
endpointMirrorService("pod-0", "test-headless-service-remote", "test-namespace", "", nil),
976+
mirrorService("test-service-1-remote", "test-namespace", "", nil, nil),
977+
headlessMirrorService("test-headless-service-remote", "test-namespace", "", nil, nil),
978+
endpointMirrorService("pod-0", "test-headless-service-remote", "test-namespace", "", nil, nil),
971979
},
972980

973981
expectedLocalEndpoints: []*corev1.Endpoints{
974-
endpoints("test-service-1-remote", "test-namespace", "", "", nil),
975-
headlessMirrorEndpoints("test-headless-service-remote", "test-namespace", "", nil),
976-
endpointMirrorEndpoints("test-headless-service-remote", "test-namespace", "pod-0", "", "", nil),
982+
endpoints("test-service-1-remote", "test-namespace", nil, "", "", nil),
983+
headlessMirrorEndpoints("test-headless-service-remote", "test-namespace", nil, "", nil),
984+
endpointMirrorEndpoints("test-headless-service-remote", "test-namespace", nil, "pod-0", "", "", nil),
977985
},
978986
},
979987
} {
@@ -1003,27 +1011,27 @@ func onAddOrUpdateTestCases(isAdd bool) []mirroringTestCase {
10031011
description: fmt.Sprintf("enqueue a RemoteServiceUpdated event if this is a service that we have already mirrored and its res version is different (%s)", testType),
10041012
environment: onAddOrUpdateRemoteServiceUpdated(isAdd),
10051013
expectedEventsInQueue: []interface{}{&RemoteExportedServiceUpdated{
1006-
localService: mirrorService("test-service-remote", "test-namespace", "pastResourceVersion", nil),
1007-
localEndpoints: endpoints("test-service-remote", "test-namespace", "0.0.0.0", "", nil),
1014+
localService: mirrorService("test-service-remote", "test-namespace", "pastResourceVersion", nil, nil),
1015+
localEndpoints: endpoints("test-service-remote", "test-namespace", nil, "0.0.0.0", "", nil),
10081016
remoteUpdate: remoteService("test-service", "test-namespace", "currentResVersion", map[string]string{
10091017
consts.DefaultExportedServiceSelector: "true",
10101018
}, nil),
10111019
}},
10121020
expectedLocalServices: []*corev1.Service{
1013-
mirrorService("test-service-remote", "test-namespace", "pastResourceVersion", nil),
1021+
mirrorService("test-service-remote", "test-namespace", "pastResourceVersion", nil, nil),
10141022
},
10151023
expectedLocalEndpoints: []*corev1.Endpoints{
1016-
endpoints("test-service-remote", "test-namespace", "0.0.0.0", "", nil),
1024+
endpoints("test-service-remote", "test-namespace", nil, "0.0.0.0", "", nil),
10171025
},
10181026
},
10191027
{
10201028
description: fmt.Sprintf("not enqueue any events as this update does not really tell us anything new (res version is the same...) (%s)", testType),
10211029
environment: onAddOrUpdateSameResVersion(isAdd),
10221030
expectedLocalServices: []*corev1.Service{
1023-
mirrorService("test-service-remote", "test-namespace", "currentResVersion", nil),
1031+
mirrorService("test-service-remote", "test-namespace", "currentResVersion", nil, nil),
10241032
},
10251033
expectedLocalEndpoints: []*corev1.Endpoints{
1026-
endpoints("test-service-remote", "test-namespace", "0.0.0.0", "", nil),
1034+
endpoints("test-service-remote", "test-namespace", nil, "0.0.0.0", "", nil),
10271035
},
10281036
},
10291037
{
@@ -1035,10 +1043,10 @@ func onAddOrUpdateTestCases(isAdd bool) []mirroringTestCase {
10351043
}},
10361044

10371045
expectedLocalServices: []*corev1.Service{
1038-
mirrorService("test-service-remote", "test-namespace", "currentResVersion", nil),
1046+
mirrorService("test-service-remote", "test-namespace", "currentResVersion", nil, nil),
10391047
},
10401048
expectedLocalEndpoints: []*corev1.Endpoints{
1041-
endpoints("test-service-remote", "test-namespace", "0.0.0.0", "", nil),
1049+
endpoints("test-service-remote", "test-namespace", nil, "0.0.0.0", "", nil),
10421050
},
10431051
},
10441052
}

0 commit comments

Comments
 (0)