func()

in pkg/controllers/hub/trafficmanagerbackend/controller.go [251:341]


func (r *Reconciler) handleUpdate(ctx context.Context, backend *fleetnetv1beta1.TrafficManagerBackend) (ctrl.Result, error) {
	backendKObj := klog.KObj(backend)
	profile, err := r.validateTrafficManagerProfile(ctx, backend)
	if err != nil || profile == nil {
		// We don't need to requeue the invalid Profile (err == nil and profile == nil) because when the profile becomes
		// valid, the controller will be re-triggered again.
		// The controller will retry when err is not nil.
		return ctrl.Result{}, err
	}
	profileKObj := klog.KObj(profile)
	klog.V(2).InfoS("Found the valid trafficManagerProfile", "trafficManagerBackend", backendKObj, "trafficManagerProfile", profileKObj)

	atmProfile, err := r.validateAzureTrafficManagerProfile(ctx, backend, profile)
	if err != nil || atmProfile == nil {
		// We don't need to requeue the invalid Azure Traffic Manager profile (err == nil and atmProfile == nil) as when
		// the profile becomes valid, the controller will be re-triggered again.
		// The controller will retry when err is not nil.
		return ctrl.Result{}, err
	}
	klog.V(2).InfoS("Found the valid Azure Traffic Manager Profile", "resourceGroup", profile.Spec.ResourceGroup, "trafficManagerBackend", backendKObj, "trafficManagerProfile", profileKObj, "atmProfileName", atmProfile.Name)

	serviceImport, err := r.validateServiceImportAndCleanupEndpointsIfInvalid(ctx, profile.Spec.ResourceGroup, backend, atmProfile)
	if err != nil || serviceImport == nil {
		// We don't need to requeue the invalid serviceImport (err == nil and serviceImport == nil) as when the serviceImport
		// becomes valid, the controller will be re-triggered again.
		// The controller will retry when err is not nil.
		return ctrl.Result{}, err
	}

	klog.V(2).InfoS("Found the serviceImport", "trafficManagerBackend", backendKObj, "serviceImport", klog.KObj(serviceImport), "clusters", serviceImport.Status.Clusters)

	if *backend.Spec.Weight == 0 {
		klog.V(2).InfoS("Weight is 0, deleting all the endpoints", "trafficManagerBackend", backendKObj)
		if err := r.cleanupEndpoints(ctx, profile.Spec.ResourceGroup, backend, atmProfile); err != nil {
			return ctrl.Result{}, err
		}
		setTrueCondition(backend, nil)
		return ctrl.Result{}, r.updateTrafficManagerBackendStatus(ctx, backend)
	}

	desiredEndpointsMaps, invalidServicesMaps, err := r.validateAndProcessServiceImportForBackend(ctx, backend, serviceImport)
	if err != nil || (desiredEndpointsMaps == nil && invalidServicesMaps == nil) {
		// We don't need to requeue not found internalServiceExport(err == nil and desiredEndpointsMaps == nil && invalidServicesMaps == nil)
		// as when the serviceImport is updated, the controller will be re-triggered again.
		// The controller will retry when err is not nil.
		return ctrl.Result{}, err
	}
	klog.V(2).InfoS("Found the exported services behind the serviceImport", "trafficManagerBackend", backendKObj, "serviceImport", klog.KObj(serviceImport), "numberOfDesiredEndpoints", len(desiredEndpointsMaps), "numberOfInvalidServices", len(invalidServicesMaps))

	// register finalizer only before creating atm endpoints
	// So that when a user specifies an invalid resource group of the profile, the controller will fail to create the endpoint because of the 403 error.
	// Otherwise, the deletion will be stuck because of the 403 error and the finalizer cannot be removed.
	if !controllerutil.ContainsFinalizer(backend, objectmeta.TrafficManagerBackendFinalizer) {
		controllerutil.AddFinalizer(backend, objectmeta.TrafficManagerBackendFinalizer)
		if err := r.Update(ctx, backend); err != nil {
			klog.ErrorS(err, "Failed to add finalizer to trafficManagerBackend", "trafficManagerBackend", backend)
			return ctrl.Result{}, controller.NewUpdateIgnoreConflictError(err)
		}
	}

	acceptedEndpoints, badEndpointsErr, err := r.updateTrafficManagerEndpointsAndUpdateStatusIfUnknown(ctx, profile.Spec.ResourceGroup, backend, atmProfile, desiredEndpointsMaps)
	if err != nil {
		return ctrl.Result{}, err
	}
	if len(invalidServicesMaps) == 0 && len(badEndpointsErr) == 0 {
		setTrueCondition(backend, acceptedEndpoints)
	} else {
		var invalidEndpointErrMessage string
		if len(badEndpointsErr) > 0 {
			invalidEndpointErrMessage = fmt.Sprintf("%v endpoint(s) failed to be created/updated in the Azure Traffic Manager, for example, %v; ", len(badEndpointsErr), badEndpointsErr[0])
		}
		if len(invalidServicesMaps) > 0 {
			for clusterID, invalidServiceErr := range invalidServicesMaps {
				invalidEndpointErrMessage = invalidEndpointErrMessage + fmt.Sprintf("%v service(s) exported from clusters cannot be exposed as the Azure Traffic Manager, for example, service exported from %v is invalid: %v", len(invalidServicesMaps), clusterID, invalidServiceErr)
				// Here we only populate the message with the first invalid exported service.
				// Note, the loop of the invalidServicesMaps is not deterministic.
				break
			}
		}
		setFalseCondition(backend, acceptedEndpoints, invalidEndpointErrMessage)
	}
	klog.V(2).InfoS("Updated Traffic Manager endpoints for the serviceImport and updating the condition", "trafficManagerBackend", backendKObj, "status", backend.Status)
	if err := r.updateTrafficManagerBackendStatus(ctx, backend); err != nil {
		return ctrl.Result{}, err
	}

	// If there are any failed endpoints, we need to requeue the request to retry.
	// For any invalidService, we don't need to requeue the request as the controller will be re-triggered when the
	// serviceImport or internalServiceExport is updated.
	return ctrl.Result{}, errors.Join(badEndpointsErr...)
}