in pkg/controllers/multiclusterservice/controller.go [191:279]
func (r *Reconciler) handleUpdate(ctx context.Context, mcs *fleetnetv1alpha1.MultiClusterService) (ctrl.Result, error) {
mcsKObj := klog.KObj(mcs)
currentServiceImportName := r.serviceImportFromLabel(mcs)
desiredServiceImportName := types.NamespacedName{Namespace: mcs.Namespace, Name: mcs.Spec.ServiceImport.Name}
if currentServiceImportName != nil && currentServiceImportName.Name != desiredServiceImportName.Name {
if err := r.deleteServiceImport(ctx, currentServiceImportName); err != nil {
klog.ErrorS(err, "Failed to remove service import of mcs", "multiClusterService", mcsKObj, "serviceImport", klog.KRef(currentServiceImportName.Namespace, currentServiceImportName.Name))
if !errors.IsNotFound(err) {
return ctrl.Result{}, err
}
}
}
// update mcs service import label first to prevent the controller abort before we create the resource
if err := r.updateMultiClusterLabel(ctx, mcs, multiClusterServiceLabelServiceImport, desiredServiceImportName.Name); err != nil {
return ctrl.Result{}, err
}
serviceImport := &fleetnetv1alpha1.ServiceImport{
ObjectMeta: metav1.ObjectMeta{
Namespace: desiredServiceImportName.Namespace,
Name: desiredServiceImportName.Name,
},
}
// CreateOrUpdate will
// 1) Create a serviceImport if not exists.
// OR 2) Update a serviceImport if the desired state does not match with current state.
// OR 3) Get a serviceImport when ServiceImport status change triggers the MCS reconcile.
if op, err := controllerutil.CreateOrUpdate(ctx, r.Client, serviceImport, func() error {
return r.ensureServiceImport(serviceImport, mcs)
}); err != nil {
serviceImportKObj := klog.KObj(serviceImport)
// If the service import is already owned by another MultiClusterService, serviceImport update or creation will fail.
if err := r.Client.Get(ctx, desiredServiceImportName, serviceImport); err == nil && isServiceImportOwnedByOthers(mcs, serviceImport) { // check if NO error
// reset the current serviceImport to empty as input so that internal func will update mcs status based on the serviceImport status
// it won't change the serviceImport in the API server
// TODO could be improved by moving into the mutate func and creating a customized error
serviceImport.Status = fleetnetv1alpha1.ServiceImportStatus{}
if err := r.handleInvalidServiceImport(ctx, mcs, serviceImport); err != nil {
klog.ErrorS(err, "Failed to update status of mcs as serviceImport has been owned by other mcs", "multiClusterService", mcsKObj, "serviceImport", serviceImportKObj, "owner", serviceImport.OwnerReferences)
return ctrl.Result{}, err
}
// have to requeue the request to see if the service import is deleted by owner or not
klog.V(3).InfoS("ServiceImport has been owned by other mcs and requeue the request", "multiClusterService", mcsKObj, "serviceImport", serviceImportKObj)
return ctrl.Result{RequeueAfter: mcsRetryInterval}, nil
}
klog.ErrorS(err, "Failed to create or update service import of mcs", "multiClusterService", mcsKObj, "serviceImport", serviceImportKObj, "op", op)
return ctrl.Result{}, err
}
if len(serviceImport.Status.Clusters) == 0 {
// Since there is no services exported in the clusters, delete derived service if exists.
// When service import is still in the processing state and there is no derived service attached to the MCS,
// it will do nothing.
return ctrl.Result{}, r.handleInvalidServiceImport(ctx, mcs, serviceImport)
}
r.Recorder.Eventf(mcs, corev1.EventTypeNormal, "FoundValidService", "Found valid service %s and importing", serviceImport.Name)
serviceName := r.derivedServiceFromLabel(mcs)
if serviceName == nil {
serviceName = r.generateDerivedServiceName(mcs)
klog.V(4).InfoS("Generated derived service name", "multiClusterService", mcsKObj, "service", serviceName)
}
// update mcs service label first to prevent the controller abort before we create the resource
if err := r.updateMultiClusterLabel(ctx, mcs, objectmeta.MultiClusterServiceLabelDerivedService, serviceName.Name); err != nil {
return ctrl.Result{}, err
}
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: serviceName.Namespace,
Name: serviceName.Name,
},
}
// CreateOrUpdate will
// 1) Create a service if not exists.
// OR 2) Update a service if the desired state does not match with current state.
// OR 3) Get a service when Service status change triggers the MCS reconcile.
if op, err := controllerutil.CreateOrUpdate(ctx, r.Client, service, func() error {
return r.ensureDerivedService(mcs, serviceImport, service)
}); err != nil {
klog.ErrorS(err, "Failed to create or update derived service of mcs", "multiClusterService", mcsKObj, "service", klog.KObj(service), "op", op)
return ctrl.Result{}, err
}
if err := r.updateMultiClusterServiceStatus(ctx, mcs, serviceImport, service); err != nil {
return ctrl.Result{}, err
}
r.Recorder.Eventf(mcs, corev1.EventTypeNormal, "SuccessfulUpdateStatus", "Imported %s service and updated %s status", serviceImport.Name, mcs.Name)
return ctrl.Result{}, nil
}