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  }