diff --git a/cmd/main.go b/cmd/main.go index c032e63..b7bfb18 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -332,16 +332,14 @@ func main() { os.Exit(1) } if err = (&controller.HTTPRouteReconciler{ - Client: mgr.GetClient(), - Netbird: nbClient, - ClusterDNS: clusterDNS, + Client: mgr.GetClient(), + Netbird: nbClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "HTTPRoute") os.Exit(1) } if err = (&controller.TCPRouteReconciler{ - Client: mgr.GetClient(), - ClusterDNS: clusterDNS, + Client: mgr.GetClient(), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "TCPRoute") os.Exit(1) diff --git a/examples/gateway-api/gateway.yaml b/examples/gateway-api/gateway.yaml index 3097fd0..4e224be 100644 --- a/examples/gateway-api/gateway.yaml +++ b/examples/gateway-api/gateway.yaml @@ -1,9 +1,25 @@ -apiVersion: netbird.io/v1 -kind: NBRoutingPeer +apiVersion: netbird.io/v1alpha1 +kind: NetworkRouter metadata: name: netbird namespace: netbird -spec: {} +spec: + dnsZoneRef: + name: cluster.local +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: netbird-public +spec: + controllerName: "gateway.netbird.io/controller" +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: netbird-private +spec: + controllerName: "gateway.netbird.io/controller" --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway @@ -13,7 +29,7 @@ metadata: spec: gatewayClassName: netbird-private listeners: - - protocol: gateway.netbird.io/NBRoutingPeer + - protocol: gateway.netbird.io/NetworkRouter name: netbird port: 1 --- @@ -25,6 +41,6 @@ metadata: spec: gatewayClassName: netbird-public listeners: - - protocol: gateway.netbird.io/NBRoutingPeer + - protocol: gateway.netbird.io/NetworkRouter name: netbird port: 1 diff --git a/internal/controller/gateway_controller.go b/internal/controller/gateway_controller.go index 2945d63..b1bf54f 100644 --- a/internal/controller/gateway_controller.go +++ b/internal/controller/gateway_controller.go @@ -19,11 +19,10 @@ package controller import ( "context" "fmt" - "slices" "time" - netbird "github.com/netbirdio/netbird/shared/management/client/rest" - corev1 "k8s.io/api/core/v1" + "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/patch" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -32,24 +31,21 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" "github.com/netbirdio/kubernetes-operator/internal/gatewayutil" ) -const ( - GatewayFinalizer = "gateway.netbird.io/gateway" -) - type GatewayReconciler struct { client.Client } func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - gw := gatewayv1.Gateway{} - err := r.Get(ctx, req.NamespacedName, &gw) + gw := &gatewayv1.Gateway{} + err := r.Get(ctx, req.NamespacedName, gw) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } + sp := patch.NewSerialPatcher(gw, r.Client) // Check if referenced class belongs to this controller. gwc := &gatewayv1.GatewayClass{} @@ -69,11 +65,11 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct // Handle resource deletion. if !gw.DeletionTimestamp.IsZero() { - return r.reconcileDelete(ctx, gw) + return r.reconcileDelete(ctx, sp, gw) } // Verify Gateway configuration. - routingPeerName, err := gatewayutil.GetRoutingPeerName(gw.Spec.Listeners) + routingPeerName, err := gatewayutil.GetNetworkRouterName(gw.Spec.Listeners) if err != nil { cond := metav1.Condition{ Type: string(gatewayv1.GatewayConditionAccepted), @@ -81,56 +77,42 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Reason: string(gatewayv1.GatewayReasonInvalidParameters), Message: err.Error(), } - if meta.SetStatusCondition(&gw.Status.Conditions, cond) { - err = r.Status().Update(ctx, &gw) - if err != nil { - return ctrl.Result{}, err - } + meta.SetStatusCondition(&gw.Status.Conditions, cond) + err = sp.Patch(ctx, gw) + if err != nil { + return ctrl.Result{}, err } return ctrl.Result{}, nil } - cond := metav1.Condition{ Type: string(gatewayv1.GatewayConditionAccepted), Status: metav1.ConditionTrue, Reason: string(gatewayv1.GatewayReasonAccepted), } - if meta.SetStatusCondition(&gw.Status.Conditions, cond) { - err = r.Status().Update(ctx, &gw) - if err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - if controllerutil.AddFinalizer(&gw, GatewayFinalizer) { - err = r.Client.Update(ctx, &gw) - if err != nil { - return ctrl.Result{}, err - } - } - - // Ensure routing peer is ready. - // TODO (phillebaba): Should watch routing peer instead of retrying when not found. - nbrp, err := gatewayutil.GetGatewayRoutingPeer(ctx, r.Client, gw) + meta.SetStatusCondition(&gw.Status.Conditions, cond) + controllerutil.AddFinalizer(gw, nbv1alpha1.NetbirdFinalizer) + err = sp.Patch(ctx, gw) if err != nil { return ctrl.Result{}, err } - idx := slices.IndexFunc(nbrp.Status.Conditions, func(cond netbirdiov1.NBCondition) bool { - return cond.Type == netbirdiov1.NBSetupKeyReady - }) - if idx == -1 || nbrp.Status.Conditions[idx].Status != corev1.ConditionStatus(metav1.ConditionTrue) { + + // Ensure routing peer is ready. + netRouter, err := gatewayutil.GetGatewayNetworkRouter(ctx, r.Client, gw) + if err != nil { + return ctrl.Result{}, err + } + if !conditions.Has(netRouter, nbv1alpha1.ReadyCondition) { + // TODO (phillebaba): Should watch routing peer instead of retrying when not found. cond := metav1.Condition{ Type: string(gatewayv1.GatewayConditionProgrammed), Status: metav1.ConditionFalse, Reason: string(gatewayv1.GatewayReasonProgrammed), Message: fmt.Sprintf("NBRoutingPeer %s is not ready", routingPeerName), } - if meta.SetStatusCondition(&gw.Status.Conditions, cond) { - err = r.Status().Update(ctx, &gw) - if err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil + meta.SetStatusCondition(&gw.Status.Conditions, cond) + err = sp.Patch(ctx, gw) + if err != nil { + return ctrl.Result{}, err } return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } @@ -141,18 +123,15 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Status: metav1.ConditionTrue, Reason: string(gatewayv1.GatewayReasonProgrammed), } - if meta.SetStatusCondition(&gw.Status.Conditions, cond) { - err = r.Status().Update(ctx, &gw) - if err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil + meta.SetStatusCondition(&gw.Status.Conditions, cond) + err = sp.Patch(ctx, gw) + if err != nil { + return ctrl.Result{}, err } - return ctrl.Result{}, nil } -func (r *GatewayReconciler) reconcileDelete(ctx context.Context, gw gatewayv1.Gateway) (ctrl.Result, error) { +func (r *GatewayReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, gw *gatewayv1.Gateway) (ctrl.Result, error) { var httpRouteList gatewayv1.HTTPRouteList err := r.Client.List(ctx, &httpRouteList) if err != nil { @@ -179,16 +158,14 @@ func (r *GatewayReconciler) reconcileDelete(ctx context.Context, gw gatewayv1.Ga } } - if controllerutil.RemoveFinalizer(&gw, GatewayFinalizer) { - err := r.Client.Update(ctx, &gw) - if err != nil && !netbird.IsNotFound(err) { - return ctrl.Result{}, err - } + controllerutil.RemoveFinalizer(gw, nbv1alpha1.NetbirdFinalizer) + err = sp.Patch(ctx, gw) + if err != nil { + return ctrl.Result{}, err } return ctrl.Result{}, nil } -// SetupWithManager sets up the controller with the Manager. func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&gatewayv1.Gateway{}). diff --git a/internal/controller/gatewayclass_controller.go b/internal/controller/gatewayclass_controller.go index 1831fe1..5ea0687 100644 --- a/internal/controller/gatewayclass_controller.go +++ b/internal/controller/gatewayclass_controller.go @@ -4,6 +4,8 @@ import ( "context" "time" + "github.com/fluxcd/pkg/runtime/patch" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" @@ -13,7 +15,6 @@ import ( ) const ( - GatewayClassFinalizer = "gateway.netbird.io/gatewayclass" GatewayControllerName = "gateway.netbird.io/controller" ) @@ -22,11 +23,12 @@ type GatewayClassReconciler struct { } func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - gwc := gatewayv1.GatewayClass{} - err := r.Client.Get(ctx, req.NamespacedName, &gwc) + gwc := &gatewayv1.GatewayClass{} + err := r.Client.Get(ctx, req.NamespacedName, gwc) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } + sp := patch.NewSerialPatcher(gwc, r.Client) // Controller name does not match. if gwc.Spec.ControllerName != GatewayControllerName { @@ -35,7 +37,7 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request // Gateway class is being deleted. if !gwc.GetDeletionTimestamp().IsZero() { - return r.reconcileDelete(ctx, gwc) + return r.reconcileDelete(ctx, sp, gwc) } // Validate configuration. @@ -55,24 +57,16 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request Reason: string(gatewayv1.GatewayClassReasonInvalidParameters), Message: message, } - if meta.SetStatusCondition(&gwc.Status.Conditions, cond) { - err = r.Client.Status().Update(ctx, &gwc) - if err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{}, nil - } - } - - // Add finalizer to validate deletion. - if controllerutil.AddFinalizer(&gwc, GatewayClassFinalizer) { - err = r.Client.Update(ctx, &gwc) + meta.SetStatusCondition(&gwc.Status.Conditions, cond) + err := sp.Patch(ctx, gwc) if err != nil { return ctrl.Result{}, err } + return ctrl.Result{}, nil } // Set condition to accepted. + controllerutil.AddFinalizer(gwc, nbv1alpha1.NetbirdFinalizer) cond := metav1.Condition{ Type: string(gatewayv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, @@ -80,15 +74,14 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request Message: "Reconciled by Netbird Operator.", } meta.SetStatusCondition(&gwc.Status.Conditions, cond) - err = r.Client.Status().Update(ctx, &gwc) + err = sp.Patch(ctx, gwc) if err != nil { return ctrl.Result{}, err } - return ctrl.Result{}, nil } -func (r *GatewayClassReconciler) reconcileDelete(ctx context.Context, gwc gatewayv1.GatewayClass) (ctrl.Result, error) { +func (r *GatewayClassReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, gwc *gatewayv1.GatewayClass) (ctrl.Result, error) { var gatewayList gatewayv1.GatewayList err := r.Client.List(ctx, &gatewayList) if err != nil { @@ -99,11 +92,11 @@ func (r *GatewayClassReconciler) reconcileDelete(ctx context.Context, gwc gatewa return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } } - if controllerutil.RemoveFinalizer(&gwc, GatewayClassFinalizer) { - err = r.Client.Update(ctx, &gwc) - if err != nil { - return ctrl.Result{}, err - } + + controllerutil.RemoveFinalizer(gwc, nbv1alpha1.NetbirdFinalizer) + err = sp.Patch(ctx, gwc) + if err != nil { + return ctrl.Result{}, err } return ctrl.Result{}, nil } diff --git a/internal/controller/httproute_controller.go b/internal/controller/httproute_controller.go index 5b8e403..4b88104 100644 --- a/internal/controller/httproute_controller.go +++ b/internal/controller/httproute_controller.go @@ -2,9 +2,10 @@ package controller import ( "context" - "fmt" "time" + "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/patch" netbird "github.com/netbirdio/netbird/shared/management/client/rest" "github.com/netbirdio/netbird/shared/management/http/api" corev1 "k8s.io/api/core/v1" @@ -16,9 +17,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" "github.com/netbirdio/kubernetes-operator/internal/gatewayutil" + "github.com/netbirdio/kubernetes-operator/internal/ssautil" "github.com/netbirdio/kubernetes-operator/internal/util" + nbv1alpha1ac "github.com/netbirdio/kubernetes-operator/pkg/applyconfigurations/api/v1alpha1" ) const ( @@ -28,22 +31,22 @@ const ( type HTTPRouteReconciler struct { client.Client - Netbird *netbird.Client - ClusterDNS string + Netbird *netbird.Client } // nolint:gocyclo func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := ctrl.Log.WithName("HTTPRoute").WithValues("namespace", req.Namespace, "name", req.Name) - hr := gatewayv1.HTTPRoute{} - err := r.Get(ctx, req.NamespacedName, &hr) + hr := &gatewayv1.HTTPRoute{} + err := r.Get(ctx, req.NamespacedName, hr) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } + sp := patch.NewSerialPatcher(hr, r.Client) if !hr.DeletionTimestamp.IsZero() { - return r.reconcileDelete(ctx, hr) + return r.reconcileDelete(ctx, sp, hr) } for _, parent := range hr.Spec.ParentRefs { @@ -58,16 +61,15 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( logger.Info("gateway is not ready", "name", gw.ObjectMeta.Name) continue } - nbrp, err := gatewayutil.GetGatewayRoutingPeer(ctx, r.Client, *gw) + netRouter, err := gatewayutil.GetGatewayNetworkRouter(ctx, r.Client, gw) if err != nil { return ctrl.Result{}, err } - if controllerutil.AddFinalizer(&hr, HTTPRouteFinalizer) { - err = r.Client.Update(ctx, &hr) - if err != nil { - return ctrl.Result{}, err - } + controllerutil.AddFinalizer(hr, nbv1alpha1.NetbirdFinalizer) + err = sp.Patch(ctx, hr) + if err != nil { + return ctrl.Result{}, err } // Create network resources. @@ -85,29 +87,23 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } for _, svc := range svcIdx { - nbResource := netbirdiov1.NBResource{ - ObjectMeta: metav1.ObjectMeta{ - Name: svc.Name, - Namespace: svc.Namespace, - }, + controllerRef, err := ssautil.ControllerReference(&svc, r.Scheme()) + if err != nil { + return ctrl.Result{}, err } - _, err := controllerutil.CreateOrUpdate(ctx, r.Client, &nbResource, func() error { - err = controllerutil.SetControllerReference(&svc, &nbResource, r.Scheme(), controllerutil.WithBlockOwnerDeletion(false)) - if err != nil { - return err - } - err = controllerutil.SetOwnerReference(&hr, &nbResource, r.Scheme()) - if err != nil { - return err - } - nbResource.Spec = netbirdiov1.NBResourceSpec{ - Name: svc.Name, - NetworkID: *nbrp.Status.NetworkID, - Address: fmt.Sprintf("%s.%s.%s", svc.Name, svc.Namespace, r.ClusterDNS), - Groups: []string{}, - } - return nil - }) + controllerRef = controllerRef.WithBlockOwnerDeletion(false) + ownerRef, err := ssautil.OwnerReference(hr, r.Scheme()) + if err != nil { + return ctrl.Result{}, err + } + netResourceAC := nbv1alpha1ac.NetworkResource(svc.Name, svc.Namespace). + WithOwnerReferences(controllerRef, ownerRef). + WithSpec( + nbv1alpha1ac.NetworkResourceSpec(). + WithNetworkRouterRef(nbv1alpha1ac.CrossNamespaceReference().WithName(netRouter.Name).WithNamespace(netRouter.Namespace)). + WithServiceRef(corev1.LocalObjectReference{Name: svc.Name}), + ) + err = r.Client.Apply(ctx, netResourceAC) if err != nil { return ctrl.Result{}, err } @@ -115,29 +111,26 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( targets := []api.ServiceTarget{} for _, svc := range svcIdx { - var nbResource netbirdiov1.NBResource - err := r.Client.Get(ctx, client.ObjectKeyFromObject(&svc), &nbResource) + netResource := &nbv1alpha1.NetworkResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: svc.Name, + Namespace: svc.Namespace, + }, + } + err := r.Client.Get(ctx, client.ObjectKeyFromObject(netResource), netResource) if err != nil { return ctrl.Result{}, err } - ready := func() bool { - for _, cond := range nbResource.Status.Conditions { - if cond.Type == netbirdiov1.NBSetupKeyReady && cond.Status == corev1.ConditionTrue { - return true - } - } - return false - }() - if !ready { + if !conditions.Has(netResource, nbv1alpha1.ReadyCondition) { return ctrl.Result{RequeueAfter: 1 * time.Second}, nil } target := api.ServiceTarget{ Enabled: true, Path: nil, - TargetId: *nbResource.Status.NetworkResourceID, - Protocol: "http", - TargetType: "domain", + TargetId: netResource.Status.ResourceID, + Protocol: api.ServiceTargetProtocolHttp, + TargetType: api.ServiceTargetTargetTypeHost, } targets = append(targets, target) } @@ -148,10 +141,11 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } for _, hostname := range hr.Spec.Hostnames { - proxyReq := api.PostApiReverseProxiesServicesJSONRequestBody{ + proxyReq := api.ServiceRequest{ Domain: string(hostname), Enabled: true, Name: string(hostname), + Mode: util.Ptr(api.ServiceRequestModeHttp), PassHostHeader: util.Ptr(false), RewriteRedirects: util.Ptr(false), Targets: &targets, @@ -182,7 +176,7 @@ func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } -func (r *HTTPRouteReconciler) reconcileDelete(ctx context.Context, hr gatewayv1.HTTPRoute) (ctrl.Result, error) { +func (r *HTTPRouteReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, hr *gatewayv1.HTTPRoute) (ctrl.Result, error) { // Index all proxy services. proxyServices, err := r.Netbird.ReverseProxyServices.List(ctx) if err != nil { @@ -219,24 +213,29 @@ func (r *HTTPRouteReconciler) reconcileDelete(ctx context.Context, hr gatewayv1. } } for _, svc := range svcIdx { - var nbResource netbirdiov1.NBResource - err = r.Client.Get(ctx, client.ObjectKeyFromObject(&svc), &nbResource) + netResource := &nbv1alpha1.NetworkResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: svc.Name, + Namespace: svc.Namespace, + }, + } + err = r.Client.Get(ctx, client.ObjectKeyFromObject(netResource), netResource) if err != nil { return ctrl.Result{}, err } - err = controllerutil.RemoveOwnerReference(&hr, &nbResource, r.Scheme()) + err = controllerutil.RemoveOwnerReference(hr, netResource, r.Scheme()) if err != nil { return ctrl.Result{}, err } - if len(nbResource.OwnerReferences) > 1 { - err = r.Client.Update(ctx, &nbResource) + if len(netResource.OwnerReferences) > 1 { + err = r.Client.Update(ctx, netResource) if err != nil { return ctrl.Result{}, err } } else { // TODO: Precondition that nothing has changed. - err := r.Client.Delete(ctx, &nbResource) + err := r.Client.Delete(ctx, netResource) if err != nil { return ctrl.Result{}, err } @@ -256,11 +255,10 @@ func (r *HTTPRouteReconciler) reconcileDelete(ctx context.Context, hr gatewayv1. } } - if controllerutil.RemoveFinalizer(&hr, HTTPRouteFinalizer) { - err := r.Client.Update(ctx, &hr) - if err != nil { - return ctrl.Result{}, err - } + controllerutil.RemoveFinalizer(hr, nbv1alpha1.NetbirdFinalizer) + err = sp.Patch(ctx, hr) + if err != nil { + return ctrl.Result{}, err } return ctrl.Result{}, nil } diff --git a/internal/controller/networkrouter_controller.go b/internal/controller/networkrouter_controller.go index 0954dad..366e12e 100644 --- a/internal/controller/networkrouter_controller.go +++ b/internal/controller/networkrouter_controller.go @@ -55,7 +55,7 @@ func (r *NetworkRouterReconciler) Reconcile(ctx context.Context, req ctrl.Reques return r.reconcileDelete(ctx, sp, netRouter) } - ownerRef, err := ssautil.OwnerReference(netRouter, r.Scheme()) + ownerRef, err := ssautil.ControllerReference(netRouter, r.Scheme()) if err != nil { return ctrl.Result{}, err } diff --git a/internal/controller/setupkey_controller.go b/internal/controller/setupkey_controller.go index c259e84..2db38b4 100644 --- a/internal/controller/setupkey_controller.go +++ b/internal/controller/setupkey_controller.go @@ -41,7 +41,7 @@ func (r *SetupKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - owner, err := ssautil.OwnerReference(setupKey, r.Client.Scheme()) + owner, err := ssautil.ControllerReference(setupKey, r.Client.Scheme()) if err != nil { return ctrl.Result{}, err } diff --git a/internal/controller/tcproute_controller.go b/internal/controller/tcproute_controller.go index ac7579e..16b9f8f 100644 --- a/internal/controller/tcproute_controller.go +++ b/internal/controller/tcproute_controller.go @@ -2,7 +2,6 @@ package controller import ( "context" - "fmt" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -14,32 +13,30 @@ import ( gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + "github.com/fluxcd/pkg/runtime/patch" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" "github.com/netbirdio/kubernetes-operator/internal/gatewayutil" -) - -const ( - TCPRouteFinalizer = "gateway.netbird.io/tcproute" + "github.com/netbirdio/kubernetes-operator/internal/ssautil" + nbv1alpha1ac "github.com/netbirdio/kubernetes-operator/pkg/applyconfigurations/api/v1alpha1" ) type TCPRouteReconciler struct { client.Client - - ClusterDNS string } // nolint:gocyclo func (r *TCPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := ctrl.Log.WithName("TCPRoute").WithValues("namespace", req.Namespace, "name", req.Name) - tr := gatewayv1alpha2.TCPRoute{} - err := r.Get(ctx, req.NamespacedName, &tr) + tr := &gatewayv1alpha2.TCPRoute{} + err := r.Get(ctx, req.NamespacedName, tr) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } + sp := patch.NewSerialPatcher(tr, r.Client) if !tr.DeletionTimestamp.IsZero() { - return r.reconcileDelete(ctx, tr) + return r.reconcileDelete(ctx, sp, tr) } for _, parent := range tr.Spec.ParentRefs { @@ -54,16 +51,15 @@ func (r *TCPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c logger.Info("gateway is not ready", "name", gw.ObjectMeta.Name) continue } - nbrp, err := gatewayutil.GetGatewayRoutingPeer(ctx, r.Client, *gw) + netRouter, err := gatewayutil.GetGatewayNetworkRouter(ctx, r.Client, gw) if err != nil { return ctrl.Result{}, err } - if controllerutil.AddFinalizer(&tr, TCPRouteFinalizer) { - err = r.Client.Update(ctx, &tr) - if err != nil { - return ctrl.Result{}, err - } + controllerutil.AddFinalizer(tr, nbv1alpha1.NetbirdFinalizer) + err = sp.Patch(ctx, tr) + if err != nil { + return ctrl.Result{}, err } // Create network resources. @@ -81,29 +77,23 @@ func (r *TCPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } for _, svc := range svcIdx { - nbResource := netbirdiov1.NBResource{ - ObjectMeta: metav1.ObjectMeta{ - Name: svc.Name, - Namespace: svc.Namespace, - }, + controllerRef, err := ssautil.ControllerReference(&svc, r.Scheme()) + if err != nil { + return ctrl.Result{}, err } - _, err := controllerutil.CreateOrUpdate(ctx, r.Client, &nbResource, func() error { - err = controllerutil.SetControllerReference(&svc, &nbResource, r.Scheme(), controllerutil.WithBlockOwnerDeletion(false)) - if err != nil { - return err - } - err = controllerutil.SetOwnerReference(&tr, &nbResource, r.Scheme()) - if err != nil { - return err - } - nbResource.Spec = netbirdiov1.NBResourceSpec{ - Name: svc.Name, - NetworkID: *nbrp.Status.NetworkID, - Address: fmt.Sprintf("%s.%s.%s", svc.Name, svc.Namespace, r.ClusterDNS), - Groups: []string{}, - } - return nil - }) + controllerRef = controllerRef.WithBlockOwnerDeletion(false) + ownerRef, err := ssautil.OwnerReference(tr, r.Scheme()) + if err != nil { + return ctrl.Result{}, err + } + netResourceAC := nbv1alpha1ac.NetworkResource(svc.Name, svc.Namespace). + WithOwnerReferences(controllerRef, ownerRef). + WithSpec( + nbv1alpha1ac.NetworkResourceSpec(). + WithNetworkRouterRef(nbv1alpha1ac.CrossNamespaceReference().WithName(netRouter.Name).WithNamespace(netRouter.Namespace)). + WithServiceRef(corev1.LocalObjectReference{Name: svc.Name}), + ) + err = r.Client.Apply(ctx, netResourceAC) if err != nil { return ctrl.Result{}, err } @@ -112,7 +102,7 @@ func (r *TCPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return ctrl.Result{}, nil } -func (r *TCPRouteReconciler) reconcileDelete(ctx context.Context, tr gatewayv1alpha2.TCPRoute) (ctrl.Result, error) { +func (r *TCPRouteReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, tr *gatewayv1alpha2.TCPRoute) (ctrl.Result, error) { for _, parent := range tr.Spec.ParentRefs { gw, err := gatewayutil.GetParentGateway(ctx, r.Client, parent, tr.Namespace, GatewayControllerName) if err != nil { @@ -139,24 +129,29 @@ func (r *TCPRouteReconciler) reconcileDelete(ctx context.Context, tr gatewayv1al } } for _, svc := range svcIdx { - var nbResource netbirdiov1.NBResource - err = r.Client.Get(ctx, client.ObjectKeyFromObject(&svc), &nbResource) + netResource := &nbv1alpha1.NetworkResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: svc.Name, + Namespace: svc.Namespace, + }, + } + err = r.Client.Get(ctx, client.ObjectKeyFromObject(netResource), netResource) if err != nil { return ctrl.Result{}, err } - err = controllerutil.RemoveOwnerReference(&tr, &nbResource, r.Scheme()) + err = controllerutil.RemoveOwnerReference(tr, netResource, r.Scheme()) if err != nil { return ctrl.Result{}, err } - if len(nbResource.OwnerReferences) > 1 { - err = r.Client.Update(ctx, &nbResource) + if len(netResource.OwnerReferences) > 1 { + err = r.Client.Update(ctx, netResource) if err != nil { return ctrl.Result{}, err } } else { // TODO: Precondition that nothing has changed. - err := r.Client.Delete(ctx, &nbResource) + err := r.Client.Delete(ctx, netResource) if err != nil { return ctrl.Result{}, err } @@ -164,11 +159,10 @@ func (r *TCPRouteReconciler) reconcileDelete(ctx context.Context, tr gatewayv1al } } - if controllerutil.RemoveFinalizer(&tr, TCPRouteFinalizer) { - err := r.Client.Update(ctx, &tr) - if err != nil { - return ctrl.Result{}, err - } + controllerutil.RemoveFinalizer(tr, nbv1alpha1.NetbirdFinalizer) + err := sp.Patch(ctx, tr) + if err != nil { + return ctrl.Result{}, err } return ctrl.Result{}, nil } diff --git a/internal/gatewayutil/gatewayutil.go b/internal/gatewayutil/gatewayutil.go index 061c7b2..3eb1175 100644 --- a/internal/gatewayutil/gatewayutil.go +++ b/internal/gatewayutil/gatewayutil.go @@ -10,7 +10,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" gwv1 "sigs.k8s.io/gateway-api/apis/v1" - netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1" + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" ) func GetParentGateway(ctx context.Context, k8sClient client.Client, parent gwv1.ParentReference, namespace, controllerName string) (*gwv1.Gateway, error) { @@ -36,29 +36,29 @@ func GetParentGateway(ctx context.Context, k8sClient client.Client, parent gwv1. return gw, nil } -func GetGatewayRoutingPeer(ctx context.Context, k8sClient client.Client, gw gwv1.Gateway) (*netbirdiov1.NBRoutingPeer, error) { - routingPeerName, err := GetRoutingPeerName(gw.Spec.Listeners) +func GetGatewayNetworkRouter(ctx context.Context, k8sClient client.Client, gw *gwv1.Gateway) (*nbv1alpha1.NetworkRouter, error) { + netRouterName, err := GetNetworkRouterName(gw.Spec.Listeners) if err != nil { return nil, err } - nbrp := &netbirdiov1.NBRoutingPeer{} - err = k8sClient.Get(ctx, types.NamespacedName{Namespace: gw.Namespace, Name: routingPeerName}, nbrp) + netRouter := &nbv1alpha1.NetworkRouter{} + err = k8sClient.Get(ctx, types.NamespacedName{Namespace: gw.Namespace, Name: netRouterName}, netRouter) if err != nil { return nil, err } - return nbrp, nil + return netRouter, nil } -func GetRoutingPeerName(listeners []gwv1.Listener) (string, error) { +func GetNetworkRouterName(listeners []gwv1.Listener) (string, error) { if len(listeners) > 1 { return "", errors.New("netbird Gateway only supports a single listener") } group, kind, ok := strings.Cut(string(listeners[0].Protocol), "/") if !ok { - return "", fmt.Errorf("invalid protocol %s, expected gateway.netbird.io/NBRoutingPeer", listeners[0].Protocol) + return "", fmt.Errorf("invalid protocol %s, expected gateway.netbird.io/NetworkRouter", listeners[0].Protocol) } - if group != "gateway.netbird.io" || kind != "NBRoutingPeer" { - return "", fmt.Errorf("invalid group %s and kind %s, expected gateway.netbird.io/NBRoutingPeer", group, kind) + if group != "gateway.netbird.io" || kind != "NetworkRouter" { + return "", fmt.Errorf("invalid group %s and kind %s, expected gateway.netbird.io/NetworkRouter", group, kind) } return string(listeners[0].Name), nil } diff --git a/internal/ssautil/ssautil.go b/internal/ssautil/ssautil.go index 18289ae..cbdcf41 100644 --- a/internal/ssautil/ssautil.go +++ b/internal/ssautil/ssautil.go @@ -7,7 +7,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/apiutil" ) -func OwnerReference(owner client.Object, scheme *runtime.Scheme) (*metav1ac.OwnerReferenceApplyConfiguration, error) { +func ControllerReference(owner client.Object, scheme *runtime.Scheme) (*metav1ac.OwnerReferenceApplyConfiguration, error) { gvk, err := apiutil.GVKForObject(owner, scheme) if err != nil { return nil, err @@ -20,3 +20,17 @@ func OwnerReference(owner client.Object, scheme *runtime.Scheme) (*metav1ac.Owne WithController(true). WithBlockOwnerDeletion(true), nil } + +func OwnerReference(owner client.Object, scheme *runtime.Scheme) (*metav1ac.OwnerReferenceApplyConfiguration, error) { + gvk, err := apiutil.GVKForObject(owner, scheme) + if err != nil { + return nil, err + } + return metav1ac.OwnerReference(). + WithAPIVersion(gvk.GroupVersion().String()). + WithKind(gvk.Kind). + WithName(owner.GetName()). + WithUID(owner.GetUID()). + WithController(false). + WithBlockOwnerDeletion(false), nil +}