pkg/controllers/member/serviceimport/controller.go (93 lines of code) (raw):

/* Copyright (c) Microsoft Corporation. Licensed under the MIT license. */ // Package serviceimport features the serviceimport controller deployed in member cluster to managed // internalserviceimport according to its corresponding serviceimport. package serviceimport import ( "context" "fmt" "time" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" ) const ( ServiceImportFinalizer = "networking.fleet.azure.com/serviceimport-cleanup" ) // Reconciler reconciles a InternalServceImport object. type Reconciler struct { MemberClusterID string // The namespace reserved for the current member cluster in the hub cluster. HubNamespace string HubClient client.Client MemberClient client.Client } //+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=serviceimports,verbs=get;list;watch;update;patch //+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=serviceimports/finalizers,verbs=get;update //+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=internalserviceimports,verbs=get;list;watch;create;update;patch;delete // Reconcile in member cluster creates hub cluster internal service import out of member cluster service import. func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // fetch serviceimport in member cluster serviceImport := &fleetnetv1alpha1.ServiceImport{} serviceImportRef := klog.KRef(req.Namespace, req.Name) startTime := time.Now() klog.V(2).InfoS("Reconciliation starts", "serviceImport", serviceImportRef) defer func() { latency := time.Since(startTime).Milliseconds() klog.V(2).InfoS("Reconciliation ends", "serviceImport", serviceImportRef, "latency", latency) }() if err := r.MemberClient.Get(ctx, req.NamespacedName, serviceImport); err != nil { if errors.IsNotFound(err) { klog.V(4).InfoS("Ignoring NotFound serviceImport", "serviceImport", serviceImportRef) return ctrl.Result{}, nil } klog.ErrorS(err, "Failed to get serviceImport", "serviceImport", serviceImportRef) return reconcile.Result{}, err } internalServiceImportName := formatInternalServiceImportName(serviceImport) internalServiceImport := &fleetnetv1alpha1.InternalServiceImport{ ObjectMeta: metav1.ObjectMeta{ Name: internalServiceImportName, Namespace: r.HubNamespace, }, } internalServiceImportRef := klog.KObj(internalServiceImport) // Examine DeletionTimestamp to determine if service import is under deletion. if serviceImport.ObjectMeta.DeletionTimestamp != nil { // When finalizer is not found, we can return early as the cleanup work should have been done. if !controllerutil.ContainsFinalizer(serviceImport, ServiceImportFinalizer) { return ctrl.Result{}, nil } // Delete service import dependency when the finalizer is expected then remove the finalizer from service import. if err := r.HubClient.Delete(ctx, internalServiceImport); err != nil { klog.ErrorS(err, "Failed to delete internalserviceimport as required by serviceimport finalizer", "InternalServiceImport", internalServiceImportRef, "ServiceImport", serviceImportRef, "finalizer", ServiceImportFinalizer) if !errors.IsNotFound(err) { return ctrl.Result{}, err } } controllerutil.RemoveFinalizer(serviceImport, ServiceImportFinalizer) if err := r.MemberClient.Update(ctx, serviceImport); err != nil { klog.ErrorS(err, "Failed to remove serviceimport finalizer", "ServiceImport", serviceImportRef, "finalizer", ServiceImportFinalizer) return ctrl.Result{}, err } // Stop reconciliation as the item is being deleted return ctrl.Result{}, nil } // Add finalizer when it's in service import when not being deleted if !controllerutil.ContainsFinalizer(serviceImport, ServiceImportFinalizer) { controllerutil.AddFinalizer(serviceImport, ServiceImportFinalizer) if err := r.MemberClient.Update(ctx, serviceImport); err != nil { klog.ErrorS(err, "Failed to add serviceimport finalizer", "ServiceImport", serviceImportRef, "finalizer", ServiceImportFinalizer) return ctrl.Result{}, err } } klog.V(2).InfoS("Create or update internal service import", "InternalServiceImport", internalServiceImportRef) if op, err := controllerutil.CreateOrUpdate(ctx, r.HubClient, internalServiceImport, func() error { if internalServiceImport.CreationTimestamp.IsZero() { // Set the ServiceReference only when the InternalServiceImport is created; most of the fields in // an ExportedObjectReference should be immutable. internalServiceImport.Spec.ServiceImportReference = fleetnetv1alpha1.FromMetaObjects(r.MemberClusterID, serviceImport.TypeMeta, serviceImport.ObjectMeta, serviceImport.CreationTimestamp) } // TO-DO: InternalServiceImport object is not an exported object and the ServiceImportReference (an // exportedObject field) will be removed; information updated here is not used. internalServiceImport.Spec.ServiceImportReference.UpdateFromMetaObject(serviceImport.ObjectMeta, serviceImport.CreationTimestamp) return nil }); err != nil { klog.ErrorS(err, "Failed to create or update InternalServiceImport from ServiceImport", "InternalServiceImport", internalServiceImportRef, "ServiceImport", serviceImportRef, "op", op) return ctrl.Result{}, err } return ctrl.Result{}, nil } func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&fleetnetv1alpha1.ServiceImport{}). Complete(r) } // formatInternalServiceImportName returns the unique name assigned to an service import func formatInternalServiceImportName(serviceImport *fleetnetv1alpha1.ServiceImport) string { return fmt.Sprintf("%s-%s", serviceImport.Namespace, serviceImport.Name) }