github.com/cilium/cilium@v1.16.2/operator/pkg/gateway-api/tlsroute_reconcile.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package gateway_api 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/google/go-cmp/cmp/cmpopts" 12 "github.com/sirupsen/logrus" 13 k8serrors "k8s.io/apimachinery/pkg/api/errors" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 ctrl "sigs.k8s.io/controller-runtime" 16 gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" 17 gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 18 gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" 19 20 controllerruntime "github.com/cilium/cilium/operator/pkg/controller-runtime" 21 "github.com/cilium/cilium/operator/pkg/gateway-api/routechecks" 22 "github.com/cilium/cilium/pkg/logging/logfields" 23 ) 24 25 // Reconcile is part of the main kubernetes reconciliation loop which aims to 26 // move the current state of the cluster closer to the desired state. 27 // 28 // For more details, check Reconcile and its Result here: 29 // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.2/pkg/reconcile 30 func (r *tlsRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 31 scopedLog := log.WithContext(ctx).WithFields(logrus.Fields{ 32 logfields.Controller: "tlsRoute", 33 logfields.Resource: req.NamespacedName, 34 }) 35 scopedLog.Info("Reconciling TLSRoute") 36 37 // Fetch the TLSRoute instance 38 original := &gatewayv1alpha2.TLSRoute{} 39 if err := r.Client.Get(ctx, req.NamespacedName, original); err != nil { 40 if k8serrors.IsNotFound(err) { 41 return controllerruntime.Success() 42 } 43 scopedLog.WithError(err).Error("Unable to fetch TLSRoute") 44 return controllerruntime.Fail(err) 45 } 46 47 // Ignore deleted TLSRoute, this can happen when foregroundDeletion is enabled 48 if original.GetDeletionTimestamp() != nil { 49 return controllerruntime.Success() 50 } 51 52 tr := original.DeepCopy() 53 54 // check if this cert is allowed to be used by this gateway 55 grants := &gatewayv1beta1.ReferenceGrantList{} 56 if err := r.Client.List(ctx, grants); err != nil { 57 return r.handleReconcileErrorWithStatus(ctx, fmt.Errorf("failed to retrieve reference grants: %w", err), original, tr) 58 } 59 60 // input for the validators 61 i := &routechecks.TLSRouteInput{ 62 Ctx: ctx, 63 Logger: scopedLog.WithField(logfields.Resource, tr), 64 Client: r.Client, 65 Grants: grants, 66 TLSRoute: tr, 67 } 68 69 // gateway validators 70 for _, parent := range tr.Spec.ParentRefs { 71 72 // set acceptance to okay, this wil be overwritten in checks if needed 73 i.SetParentCondition(parent, metav1.Condition{ 74 Type: string(gatewayv1.RouteConditionAccepted), 75 Status: metav1.ConditionTrue, 76 Reason: string(gatewayv1.RouteReasonAccepted), 77 Message: "Accepted TLSRoute", 78 }) 79 80 // set status to okay, this wil be overwritten in checks if needed 81 i.SetAllParentCondition(metav1.Condition{ 82 Type: string(gatewayv1.RouteConditionResolvedRefs), 83 Status: metav1.ConditionTrue, 84 Reason: string(gatewayv1.RouteReasonResolvedRefs), 85 Message: "Service reference is valid", 86 }) 87 88 // run the actual validators 89 for _, fn := range []routechecks.CheckParentFunc{ 90 routechecks.CheckGatewayAllowedForNamespace, 91 routechecks.CheckGatewayRouteKindAllowed, 92 routechecks.CheckGatewayMatchingPorts, 93 routechecks.CheckGatewayMatchingHostnames, 94 routechecks.CheckGatewayMatchingSection, 95 } { 96 continueCheck, err := fn(i, parent) 97 if err != nil { 98 return r.handleReconcileErrorWithStatus(ctx, fmt.Errorf("failed to apply route check: %w", err), original, tr) 99 } 100 101 if !continueCheck { 102 break 103 } 104 } 105 } 106 107 // backend validators 108 109 for _, fn := range []routechecks.CheckRuleFunc{ 110 routechecks.CheckAgainstCrossNamespaceBackendReferences, 111 routechecks.CheckBackend, 112 routechecks.CheckHasServiceImportSupport, 113 routechecks.CheckBackendIsExistingService, 114 } { 115 continueCheck, err := fn(i) 116 if err != nil { 117 return r.handleReconcileErrorWithStatus(ctx, fmt.Errorf("failed to apply Gateway check: %w", err), original, tr) 118 } 119 120 if !continueCheck { 121 break 122 } 123 } 124 125 if err := r.updateStatus(ctx, original, tr); err != nil { 126 return ctrl.Result{}, fmt.Errorf("failed to update TLSRoute status: %w", err) 127 } 128 129 scopedLog.Info("Successfully reconciled TLSRoute") 130 return controllerruntime.Success() 131 } 132 133 func (r *tlsRouteReconciler) updateStatus(ctx context.Context, original *gatewayv1alpha2.TLSRoute, new *gatewayv1alpha2.TLSRoute) error { 134 oldStatus := original.Status.DeepCopy() 135 newStatus := new.Status.DeepCopy() 136 137 opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") 138 if cmp.Equal(oldStatus, newStatus, opts) { 139 return nil 140 } 141 return r.Client.Status().Update(ctx, new) 142 } 143 144 func (r *tlsRouteReconciler) handleReconcileErrorWithStatus(ctx context.Context, reconcileErr error, original *gatewayv1alpha2.TLSRoute, modified *gatewayv1alpha2.TLSRoute) (ctrl.Result, error) { 145 if err := r.updateStatus(ctx, original, modified); err != nil { 146 return controllerruntime.Fail(fmt.Errorf("failed to update TLSRoute status while handling the reconcile error: %w: %w", reconcileErr, err)) 147 } 148 149 return controllerruntime.Fail(reconcileErr) 150 }