@@ -64,6 +64,9 @@ import (
64
64
const ServiceExportControllerName = "service-export-controller"
65
65
66
66
// ServiceExportController is to sync ServiceExport and report EndpointSlices of exported service to control-plane.
67
+ // It creates Informers for clusters where ServiceExport resources have been propagated,
68
+ // registers EventHandlers for ServiceExport and EndpointSlice in those clusters,
69
+ // and then asynchronously processes ServiceExport and EndpointSlice events.
67
70
type ServiceExportController struct {
68
71
client.Client
69
72
EventRecorder record.EventRecorder
@@ -112,6 +115,7 @@ func (c *ServiceExportController) Reconcile(ctx context.Context, req controllerr
112
115
return controllerruntime.Result {}, nil
113
116
}
114
117
118
+ // Controller should only handle ServiceExport Works, reduces noise and prevents unnecessary processing.
115
119
if ! util .IsWorkContains (work .Spec .Workload .Manifests , serviceExportGVK ) {
116
120
return controllerruntime.Result {}, nil
117
121
}
@@ -139,7 +143,9 @@ func (c *ServiceExportController) Reconcile(ctx context.Context, req controllerr
139
143
140
144
// SetupWithManager creates a controller and register to controller manager.
141
145
func (c * ServiceExportController ) SetupWithManager (mgr controllerruntime.Manager ) error {
142
- return controllerruntime .NewControllerManagedBy (mgr ).Named (ServiceExportControllerName ).For (& workv1alpha1.Work {}, builder .WithPredicates (c .PredicateFunc )).
146
+ return controllerruntime .NewControllerManagedBy (mgr ).
147
+ Named (ServiceExportControllerName ).
148
+ For (& workv1alpha1.Work {}, builder .WithPredicates (c .PredicateFunc )).
143
149
WithOptions (controller.Options {
144
150
RateLimiter : ratelimiterflag.DefaultControllerRateLimiter [controllerruntime.Request ](c .RateLimiterOptions ),
145
151
}).
@@ -156,6 +162,8 @@ func (c *ServiceExportController) RunWorkQueue() {
156
162
c .worker = util .NewAsyncWorker (workerOptions )
157
163
c .worker .Run (c .Context , c .WorkerNumber )
158
164
165
+ // TODO(@XiShanYongYe-Chang): Need to re-examine whether this call is necessary.
166
+ // If it is necessary, clarify the reason; otherwise, delete this call.
159
167
go c .enqueueReportedEpsServiceExport ()
160
168
}
161
169
@@ -241,8 +249,8 @@ func (c *ServiceExportController) buildResourceInformers(cluster *clusterv1alpha
241
249
return nil
242
250
}
243
251
244
- // registerInformersAndStart builds informer manager for cluster if it doesn't exist, then constructs informers for gvr
245
- // and start it.
252
+ // registerInformersAndStart builds informer manager for cluster if it doesn't exist, then
253
+ // constructs informers for gvr and start it.
246
254
func (c * ServiceExportController ) registerInformersAndStart (cluster * clusterv1alpha1.Cluster ) error {
247
255
singleClusterInformerManager := c .InformerManager .GetSingleClusterManager (cluster .Name )
248
256
if singleClusterInformerManager == nil {
@@ -273,6 +281,7 @@ func (c *ServiceExportController) registerInformersAndStart(cluster *clusterv1al
273
281
c .InformerManager .Start (cluster .Name )
274
282
275
283
if err := func () error {
284
+ // this call is necessary; otherwise, `IsInformerSynced` will always return false.
276
285
synced := c .InformerManager .WaitForCacheSyncWithTimeout (cluster .Name , c .ClusterCacheSyncTimeout .Duration )
277
286
if synced == nil {
278
287
return fmt .Errorf ("no informerFactory for cluster %s exist" , cluster .Name )
@@ -362,8 +371,9 @@ func (c *ServiceExportController) handleServiceExportEvent(ctx context.Context,
362
371
363
372
// Even though the EndpointSlice will be synced when dealing with EndpointSlice events, thus the 'report' here may
364
373
// be redundant, but it helps to avoid a corner case:
365
- // If skip report here, after ServiceExport deletion and re-creation, if no EndpointSlice changes, we didn't get a
366
- // change to sync.
374
+ // When ServiceExport is created after Service in member cluster, and EndpointSlice events have been processed,
375
+ // if we don't handle the ServiceExport event, the EndpointSlice will not be collected to the control plane,
376
+ // unless a new EndpointSlice event is received.
367
377
if err = c .reportEndpointSliceWithServiceExportCreate (ctx , serviceExportKey ); err != nil {
368
378
klog .ErrorS (err , "Failed to handle ServiceExport event" , "namespace" , serviceExportKey .Namespace , "name" , serviceExportKey .Name )
369
379
return err
@@ -393,6 +403,8 @@ func (c *ServiceExportController) handleEndpointSliceEvent(ctx context.Context,
393
403
}
394
404
395
405
// reportEndpointSliceWithServiceExportCreate reports the referencing service's EndpointSlice.
406
+ // The informer for EndpointSlice is created dynamically in Reconcile() when ServiceExport Works are detected.
407
+ // If informer isn't synced yet, we return error to trigger retry.
396
408
func (c * ServiceExportController ) reportEndpointSliceWithServiceExportCreate (ctx context.Context , serviceExportKey keys.FederatedKey ) error {
397
409
var (
398
410
endpointSliceObjects []runtime.Object
@@ -402,6 +414,8 @@ func (c *ServiceExportController) reportEndpointSliceWithServiceExportCreate(ctx
402
414
403
415
singleClusterManager := c .InformerManager .GetSingleClusterManager (serviceExportKey .Cluster )
404
416
if singleClusterManager == nil {
417
+ // No informer manager for this cluster yet - this shouldn't happen
418
+ // if Reconcile() has run, but handle gracefully
405
419
return nil
406
420
}
407
421
@@ -433,6 +447,7 @@ func (c *ServiceExportController) reportEndpointSliceWithServiceExportCreate(ctx
433
447
return utilerrors .NewAggregate (errs )
434
448
}
435
449
450
+ // removeOrphanWork cleans up Work resources for EndpointSlices that no longer exist.
436
451
func (c * ServiceExportController ) removeOrphanWork (ctx context.Context , endpointSliceObjects []runtime.Object , serviceExportKey keys.FederatedKey ) error {
437
452
willReportWorks := sets .NewString ()
438
453
for index := range endpointSliceObjects {
@@ -481,6 +496,8 @@ func (c *ServiceExportController) removeOrphanWork(ctx context.Context, endpoint
481
496
}
482
497
483
498
// reportEndpointSliceWithEndpointSliceCreateOrUpdate reports the EndpointSlice when referencing service has been exported.
499
+ // The informer for ServiceExport is created dynamically in Reconcile() when ServiceExport Works are detected.
500
+ // If informer isn't synced yet, we return error to trigger retry.
484
501
func (c * ServiceExportController ) reportEndpointSliceWithEndpointSliceCreateOrUpdate (ctx context.Context , clusterName string , endpointSlice * unstructured.Unstructured ) error {
485
502
relatedServiceName := endpointSlice .GetLabels ()[discoveryv1 .LabelServiceName ]
486
503
0 commit comments