Skip to content

Commit c4ea350

Browse files
committed
Add "endpoints.kubernetes.io/managed-by" label to Endpoints
Add a label to allow us to recognize endpoint-controller-generated Endpoints in the future. (In particular, to allow us to recognize stale Endpoints whose Service gets deleted while the Endpoints controller is not running.) Unlike the corresponding EndpointSlice label, this is not defined as part of the public API, because we have no interest in getting other controllers to use it. (They should switch to creating EndpointSlices instead.)
1 parent 5870490 commit c4ea350

File tree

2 files changed

+66
-12
lines changed

2 files changed

+66
-12
lines changed

pkg/controller/endpoint/endpoints_controller.go

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"time"
2424

2525
v1 "k8s.io/api/core/v1"
26-
apiequality "k8s.io/apimachinery/pkg/api/equality"
2726
"k8s.io/apimachinery/pkg/api/errors"
2827
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2928
"k8s.io/apimachinery/pkg/conversion"
@@ -67,13 +66,19 @@ const (
6766
// endpoint resource and indicates that the number of endpoints have been truncated to
6867
// maxCapacity
6968
truncated = "truncated"
69+
70+
// labelManagedBy is a label for recognizing Endpoints managed by this controller.
71+
labelManagedBy = "endpoints.kubernetes.io/managed-by"
72+
73+
// controllerName is the name of this controller
74+
controllerName = "endpoint-controller"
7075
)
7176

7277
// NewEndpointController returns a new *Controller.
7378
func NewEndpointController(ctx context.Context, podInformer coreinformers.PodInformer, serviceInformer coreinformers.ServiceInformer,
7479
endpointsInformer coreinformers.EndpointsInformer, client clientset.Interface, endpointUpdatesBatchPeriod time.Duration) *Controller {
7580
broadcaster := record.NewBroadcaster(record.WithContext(ctx))
76-
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "endpoint-controller"})
81+
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: controllerName})
7782

7883
e := &Controller{
7984
client: client,
@@ -460,19 +465,11 @@ func (e *Controller) syncService(ctx context.Context, key string) error {
460465
createEndpoints := len(currentEndpoints.ResourceVersion) == 0
461466

462467
// Compare the sorted subsets and labels
463-
// Remove the HeadlessService label from the endpoints if it exists,
464-
// as this won't be set on the service itself
465-
// and will cause a false negative in this diff check.
466-
// But first check if it has that label to avoid expensive copies.
467-
compareLabels := currentEndpoints.Labels
468-
if _, ok := currentEndpoints.Labels[v1.IsHeadlessService]; ok {
469-
compareLabels = utillabels.CloneAndRemoveLabel(currentEndpoints.Labels, v1.IsHeadlessService)
470-
}
471468
// When comparing the subsets, we ignore the difference in ResourceVersion of Pod to avoid unnecessary Endpoints
472469
// updates caused by Pod updates that we don't care, e.g. annotation update.
473470
if !createEndpoints &&
474471
endpointSubsetsEqualIgnoreResourceVersion(currentEndpoints.Subsets, subsets) &&
475-
apiequality.Semantic.DeepEqual(compareLabels, service.Labels) &&
472+
labelsCorrectForEndpoints(currentEndpoints.Labels, service.Labels) &&
476473
capacityAnnotationSetCorrectly(currentEndpoints.Annotations, currentEndpoints.Subsets) {
477474
logger.V(5).Info("endpoints are equal, skipping update", "service", klog.KObj(service))
478475
return nil
@@ -506,6 +503,7 @@ func (e *Controller) syncService(ctx context.Context, key string) error {
506503
} else {
507504
newEndpoints.Labels = utillabels.CloneAndRemoveLabel(newEndpoints.Labels, v1.IsHeadlessService)
508505
}
506+
newEndpoints.Labels[labelManagedBy] = controllerName
509507

510508
logger.V(4).Info("Update endpoints", "service", klog.KObj(service), "readyEndpoints", totalReadyEps, "notreadyEndpoints", totalNotReadyEps)
511509
var updatedEndpoints *v1.Endpoints
@@ -718,3 +716,24 @@ var semanticIgnoreResourceVersion = conversion.EqualitiesOrDie(
718716
func endpointSubsetsEqualIgnoreResourceVersion(subsets1, subsets2 []v1.EndpointSubset) bool {
719717
return semanticIgnoreResourceVersion.DeepEqual(subsets1, subsets2)
720718
}
719+
720+
// labelsCorrectForEndpoints tests that epLabels is correctly derived from svcLabels
721+
// (ignoring the v1.IsHeadlessService label).
722+
func labelsCorrectForEndpoints(epLabels, svcLabels map[string]string) bool {
723+
if epLabels[labelManagedBy] != controllerName {
724+
return false
725+
}
726+
727+
// Every label in epLabels except v1.IsHeadlessService and labelManagedBy should
728+
// correspond to a label in svcLabels, and svcLabels should not have any other
729+
// labels that aren't in epLabels.
730+
skipped := 0
731+
for k, v := range epLabels {
732+
if k == v1.IsHeadlessService || k == labelManagedBy {
733+
skipped++
734+
} else if sv, exists := svcLabels[k]; !exists || sv != v {
735+
return false
736+
}
737+
}
738+
return len(svcLabels) == len(epLabels)-skipped
739+
}

pkg/controller/endpoint/endpoints_controller_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ func TestSyncEndpointsExistingNilSubsets(t *testing.T) {
317317
Name: "foo",
318318
Namespace: ns,
319319
ResourceVersion: "1",
320+
Labels: map[string]string{
321+
labelManagedBy: controllerName,
322+
},
320323
},
321324
Subsets: nil,
322325
})
@@ -346,6 +349,9 @@ func TestSyncEndpointsExistingEmptySubsets(t *testing.T) {
346349
Name: "foo",
347350
Namespace: ns,
348351
ResourceVersion: "1",
352+
Labels: map[string]string{
353+
labelManagedBy: controllerName,
354+
},
349355
},
350356
Subsets: []v1.EndpointSubset{},
351357
})
@@ -376,6 +382,9 @@ func TestSyncEndpointsWithPodResourceVersionUpdateOnly(t *testing.T) {
376382
Name: "foo",
377383
Namespace: ns,
378384
ResourceVersion: "1",
385+
Labels: map[string]string{
386+
labelManagedBy: controllerName,
387+
},
379388
},
380389
Subsets: []v1.EndpointSubset{{
381390
Addresses: []v1.EndpointAddress{
@@ -501,6 +510,7 @@ func TestSyncEndpointsProtocolTCP(t *testing.T) {
501510
Namespace: ns,
502511
ResourceVersion: "1",
503512
Labels: map[string]string{
513+
labelManagedBy: controllerName,
504514
v1.IsHeadlessService: "",
505515
},
506516
},
@@ -524,6 +534,7 @@ func TestSyncEndpointsHeadlessServiceLabel(t *testing.T) {
524534
Namespace: ns,
525535
ResourceVersion: "1",
526536
Labels: map[string]string{
537+
labelManagedBy: controllerName,
527538
v1.IsHeadlessService: "",
528539
},
529540
},
@@ -652,6 +663,7 @@ func TestSyncEndpointsProtocolUDP(t *testing.T) {
652663
Namespace: ns,
653664
ResourceVersion: "1",
654665
Labels: map[string]string{
666+
labelManagedBy: controllerName,
655667
v1.IsHeadlessService: "",
656668
},
657669
},
@@ -701,6 +713,7 @@ func TestSyncEndpointsProtocolSCTP(t *testing.T) {
701713
Namespace: ns,
702714
ResourceVersion: "1",
703715
Labels: map[string]string{
716+
labelManagedBy: controllerName,
704717
v1.IsHeadlessService: "",
705718
},
706719
},
@@ -746,6 +759,7 @@ func TestSyncEndpointsItemsEmptySelectorSelectsAll(t *testing.T) {
746759
Namespace: ns,
747760
ResourceVersion: "1",
748761
Labels: map[string]string{
762+
labelManagedBy: controllerName,
749763
v1.IsHeadlessService: "",
750764
},
751765
},
@@ -792,6 +806,7 @@ func TestSyncEndpointsItemsEmptySelectorSelectsAllNotReady(t *testing.T) {
792806
Namespace: ns,
793807
ResourceVersion: "1",
794808
Labels: map[string]string{
809+
labelManagedBy: controllerName,
795810
v1.IsHeadlessService: "",
796811
},
797812
},
@@ -838,6 +853,7 @@ func TestSyncEndpointsItemsEmptySelectorSelectsAllMixed(t *testing.T) {
838853
Namespace: ns,
839854
ResourceVersion: "1",
840855
Labels: map[string]string{
856+
labelManagedBy: controllerName,
841857
v1.IsHeadlessService: "",
842858
},
843859
},
@@ -861,6 +877,9 @@ func TestSyncEndpointsItemsPreexisting(t *testing.T) {
861877
Name: "foo",
862878
Namespace: ns,
863879
ResourceVersion: "1",
880+
Labels: map[string]string{
881+
labelManagedBy: controllerName,
882+
},
864883
},
865884
Subsets: []v1.EndpointSubset{{
866885
Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
@@ -887,6 +906,7 @@ func TestSyncEndpointsItemsPreexisting(t *testing.T) {
887906
Namespace: ns,
888907
ResourceVersion: "1",
889908
Labels: map[string]string{
909+
labelManagedBy: controllerName,
890910
v1.IsHeadlessService: "",
891911
},
892912
},
@@ -909,6 +929,9 @@ func TestSyncEndpointsItemsPreexistingIdentical(t *testing.T) {
909929
ResourceVersion: "1",
910930
Name: "foo",
911931
Namespace: ns,
932+
Labels: map[string]string{
933+
labelManagedBy: controllerName,
934+
},
912935
},
913936
Subsets: []v1.EndpointSubset{{
914937
Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
@@ -972,6 +995,7 @@ func TestSyncEndpointsItems(t *testing.T) {
972995
ResourceVersion: "",
973996
Name: "foo",
974997
Labels: map[string]string{
998+
labelManagedBy: controllerName,
975999
v1.IsHeadlessService: "",
9761000
},
9771001
},
@@ -1022,6 +1046,7 @@ func TestSyncEndpointsItemsWithLabels(t *testing.T) {
10221046
}}
10231047

10241048
serviceLabels[v1.IsHeadlessService] = ""
1049+
serviceLabels[labelManagedBy] = controllerName
10251050
data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
10261051
ObjectMeta: metav1.ObjectMeta{
10271052
ResourceVersion: "",
@@ -1074,6 +1099,7 @@ func TestSyncEndpointsItemsPreexistingLabelsChange(t *testing.T) {
10741099
}
10751100

10761101
serviceLabels[v1.IsHeadlessService] = ""
1102+
serviceLabels[labelManagedBy] = controllerName
10771103
data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
10781104
ObjectMeta: metav1.ObjectMeta{
10791105
Name: "foo",
@@ -1183,6 +1209,7 @@ func TestSyncEndpointsHeadlessService(t *testing.T) {
11831209
Namespace: ns,
11841210
ResourceVersion: "1",
11851211
Labels: map[string]string{
1212+
labelManagedBy: controllerName,
11861213
"a": "b",
11871214
v1.IsHeadlessService: "",
11881215
},
@@ -1212,7 +1239,8 @@ func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseFail
12121239
Namespace: ns,
12131240
ResourceVersion: "1",
12141241
Labels: map[string]string{
1215-
"foo": "bar",
1242+
labelManagedBy: controllerName,
1243+
"foo": "bar",
12161244
},
12171245
},
12181246
Subsets: []v1.EndpointSubset{},
@@ -1236,6 +1264,7 @@ func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseFail
12361264
Namespace: ns,
12371265
ResourceVersion: "1",
12381266
Labels: map[string]string{
1267+
labelManagedBy: controllerName,
12391268
v1.IsHeadlessService: "",
12401269
},
12411270
},
@@ -1281,6 +1310,7 @@ func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseSucc
12811310
Namespace: ns,
12821311
ResourceVersion: "1",
12831312
Labels: map[string]string{
1313+
labelManagedBy: controllerName,
12841314
v1.IsHeadlessService: "",
12851315
},
12861316
},
@@ -1327,6 +1357,7 @@ func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyOnFailureAndPhase
13271357
Namespace: ns,
13281358
ResourceVersion: "1",
13291359
Labels: map[string]string{
1360+
labelManagedBy: controllerName,
13301361
v1.IsHeadlessService: "",
13311362
},
13321363
},
@@ -1361,6 +1392,7 @@ func TestSyncEndpointsHeadlessWithoutPort(t *testing.T) {
13611392
ObjectMeta: metav1.ObjectMeta{
13621393
Name: "foo",
13631394
Labels: map[string]string{
1395+
labelManagedBy: controllerName,
13641396
v1.IsHeadlessService: "",
13651397
},
13661398
},
@@ -1580,6 +1612,7 @@ func TestLastTriggerChangeTimeAnnotation(t *testing.T) {
15801612
v1.EndpointsLastChangeTriggerTime: triggerTimeString,
15811613
},
15821614
Labels: map[string]string{
1615+
labelManagedBy: controllerName,
15831616
v1.IsHeadlessService: "",
15841617
},
15851618
},
@@ -1636,6 +1669,7 @@ func TestLastTriggerChangeTimeAnnotation_AnnotationOverridden(t *testing.T) {
16361669
v1.EndpointsLastChangeTriggerTime: triggerTimeString,
16371670
},
16381671
Labels: map[string]string{
1672+
labelManagedBy: controllerName,
16391673
v1.IsHeadlessService: "",
16401674
},
16411675
},
@@ -1690,6 +1724,7 @@ func TestLastTriggerChangeTimeAnnotation_AnnotationCleared(t *testing.T) {
16901724
Namespace: ns,
16911725
ResourceVersion: "1",
16921726
Labels: map[string]string{
1727+
labelManagedBy: controllerName,
16931728
v1.IsHeadlessService: "",
16941729
}, // Annotation not set anymore.
16951730
},

0 commit comments

Comments
 (0)