in pkg/controllers/member/endpointslice/controller.go [62:201]
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
endpointSliceRef := klog.KRef(req.Namespace, req.Name)
startTime := time.Now()
klog.V(2).InfoS("Reconciliation starts", "endpointSlice", endpointSliceRef)
defer func() {
latency := time.Since(startTime).Milliseconds()
klog.V(2).InfoS("Reconciliation ends", "endpointSlice", endpointSliceRef, "latency", latency)
}()
// Retrieve the EndpointSlice object.
var endpointSlice discoveryv1.EndpointSlice
if err := r.MemberClient.Get(ctx, req.NamespacedName, &endpointSlice); err != nil {
// Skip the reconciliation if the EndpointSlice does not exist; this should only happen when an EndpointSlice
// is deleted right before the controller gets a chance to reconcile it. If the EndpointSlice has never
// been exported to the fleet, no action is required on this controller's end; on the other hand, if the
// EndpointSlice has been exported before, this may result in an EndpointSlice being left over on the
// hub cluster, and it is up to another controller, EndpointSliceExport controller, to pick up the leftover
// and clean it out.
if errors.IsNotFound(err) {
klog.V(4).InfoS("Ignoring NotFound endpointSlice", "endpointSlice", endpointSliceRef)
return ctrl.Result{}, nil
}
klog.ErrorS(err, "Failed to get endpoint slice", "endpointSlice", endpointSliceRef)
return ctrl.Result{}, err
}
// Check if the EndpointSlice should be skipped for reconciliation or unexported.
skipOrUnexportOp, err := r.shouldSkipOrUnexportEndpointSlice(ctx, &endpointSlice)
if err != nil {
// An unexpected error occurs.
klog.ErrorS(err,
"Failed to determine whether an endpoint slice should be skipped for reconciliation or unexported",
"endpointSlice", endpointSliceRef)
return ctrl.Result{}, err
}
switch skipOrUnexportOp {
case shouldSkipEndpointSliceOp:
// Skip reconciling the EndpointSlice.
klog.V(4).InfoS("Endpoint slice should be skipped for reconciliation", "endpointSlice", endpointSliceRef)
return ctrl.Result{}, nil
case shouldUnexportEndpointSliceOp:
// Unexport the EndpointSlice.
klog.V(4).InfoS("Endpoint slice should be unexported", "endpointSlice", endpointSliceRef)
if err := r.unexportEndpointSlice(ctx, &endpointSlice); err != nil {
klog.ErrorS(err, "Failed to unexport the endpoint slice", "endpointSlice", endpointSliceRef)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// Retrieve the unique name assigned; if none has been assigned, or the one assigned is not valid, possibly due
// to user tampering with the annotation, assign a new unique name.
fleetUniqueName, ok := endpointSlice.Annotations[objectmeta.ExportedObjectAnnotationUniqueName]
if !ok || !isUniqueNameValid(fleetUniqueName) {
klog.V(2).InfoS("The endpoint slice does not have a unique name assigned or the one assigned is not valid; a new one will be assigned",
"endpointSlice", endpointSliceRef)
var err error
// Unique name annotation must be added before an EndpointSlice is exported.
fleetUniqueName, err = r.assignUniqueNameAsAnnotation(ctx, &endpointSlice)
if err != nil {
klog.ErrorS(err, "Failed to assign unique name as an annotation", "endpointSlice", endpointSliceRef)
return ctrl.Result{}, err
}
}
// Retrieve the last seen generation and the last seen timestamp; these two values are used for metric collection.
// If the two values are not present or not valid, annotate EndpointSlice with new values.
//
// Note that the two values are not tamperproof.
exportedSince, err := r.collectAndVerifyLastSeenGenerationAndTimestamp(ctx, &endpointSlice, startTime)
if err != nil {
klog.Warning("Failed to annotate last seen generation and timestamp", "endpointSlice", endpointSliceRef)
}
// Create an EndpointSliceExport in the hub cluster if the EndpointSlice has never been exported; otherwise
// update the corresponding EndpointSliceExport.
extractedEndpoints := extractEndpointsFromEndpointSlice(&endpointSlice)
endpointSliceExport := fleetnetv1alpha1.EndpointSliceExport{
ObjectMeta: metav1.ObjectMeta{
Namespace: r.HubNamespace,
Name: fleetUniqueName,
},
}
klog.V(2).InfoS("Endpoint slice will be exported",
"endpointSlice", endpointSliceRef,
"endpointSliceExport", klog.KObj(&endpointSliceExport))
createOrUpdateOp, err := controllerutil.CreateOrUpdate(ctx, r.HubClient, &endpointSliceExport, func() error {
// Set up an EndpointSliceReference and only when an EndpointSliceExport is first created; this is because
// most fields in EndpointSliceReference should be immutable after creation.
if endpointSliceExport.CreationTimestamp.IsZero() {
endpointSliceReference := fleetnetv1alpha1.FromMetaObjects(r.MemberClusterID,
endpointSlice.TypeMeta, endpointSlice.ObjectMeta, metav1.NewTime(exportedSince))
endpointSliceExport.Spec.EndpointSliceReference = endpointSliceReference
}
// Return an error if an attempt is made to update an EndpointSliceExport that references a different
// EndpointSlice from the one that is being reconciled. This usually happens when one unique name is assigned
// to multiple EndpointSliceExports, either by chance or through direct manipulation.
if !isEndpointSliceExportLinkedWithEndpointSlice(&endpointSliceExport, &endpointSlice) {
return errors.NewAlreadyExists(
schema.GroupResource{Group: fleetnetv1alpha1.GroupVersion.Group, Resource: "EndpointSliceExport"},
fleetUniqueName,
)
}
endpointSliceExport.Spec.AddressType = discoveryv1.AddressTypeIPv4
endpointSliceExport.Spec.Endpoints = extractedEndpoints
endpointSliceExport.Spec.Ports = endpointSlice.Ports
endpointSliceExport.Spec.OwnerServiceReference = fleetnetv1alpha1.OwnerServiceReference{
// The owner Service is guaranteed to reside in the same namespace as the EndpointSlice to export.
Namespace: endpointSlice.Namespace,
Name: endpointSlice.Labels[discoveryv1.LabelServiceName],
NamespacedName: fmt.Sprintf("%s/%s", endpointSlice.Namespace, endpointSlice.Labels[discoveryv1.LabelServiceName]),
}
endpointSliceExport.Spec.EndpointSliceReference.UpdateFromMetaObject(endpointSlice.ObjectMeta, metav1.NewTime(exportedSince))
return nil
})
switch {
case errors.IsAlreadyExists(err):
// Remove the unique name annotation; a new one will be assigned in future reciliation attempts.
klog.V(2).InfoS("The unique name assigned to the endpoint slice has been used; it will be removed", "endpointSlice", endpointSliceRef)
delete(endpointSlice.Annotations, objectmeta.ExportedObjectAnnotationUniqueName)
if err := r.MemberClient.Update(ctx, &endpointSlice); err != nil {
klog.ErrorS(err, "Failed to remove endpointslice unique name annotation", "endpointSlice", endpointSliceRef)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
case err != nil:
klog.ErrorS(err,
"Failed to create/update endpointslice export",
"endpointSlice", endpointSliceRef,
"endpointSliceExport", klog.KObj(&endpointSliceExport),
"op", createOrUpdateOp)
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}