github.com/cilium/cilium@v1.16.2/operator/pkg/ciliumenvoyconfig/ciliumenvoyconfig_reconcile.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ciliumenvoyconfig
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strconv"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	corev1 "k8s.io/api/core/v1"
    13  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    14  	"k8s.io/apimachinery/pkg/types"
    15  	ctrl "sigs.k8s.io/controller-runtime"
    16  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    17  
    18  	ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    19  	"github.com/cilium/cilium/pkg/logging/logfields"
    20  )
    21  
    22  const (
    23  	ciliumEnvoyLBPrefix = "cilium-envoy-lb"
    24  )
    25  
    26  func (r *ciliumEnvoyConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    27  	scopedLog := r.logger.WithFields(logrus.Fields{
    28  		logfields.Controller: "ciliumenvoyconfig",
    29  		logfields.Resource:   req.NamespacedName,
    30  	})
    31  	scopedLog.Info("Starting reconciliation")
    32  
    33  	svc := &corev1.Service{}
    34  	if err := r.client.Get(ctx, req.NamespacedName, svc); err != nil {
    35  		if k8serrors.IsNotFound(err) {
    36  			scopedLog.WithError(err).Debug("Unable to get service - either deleted or not yet available")
    37  			return ctrl.Result{}, nil
    38  		}
    39  
    40  		return ctrl.Result{}, err
    41  	}
    42  
    43  	if isLBProtocolAnnotationEnabled(svc) || hasAnyPort(svc, r.ports) {
    44  		if err := r.createOrUpdateEnvoyConfig(ctx, svc); err != nil {
    45  			return ctrl.Result{}, err
    46  		}
    47  	} else {
    48  		if err := r.deleteEnvoyConfig(ctx, svc); err != nil {
    49  			return ctrl.Result{}, err
    50  		}
    51  	}
    52  
    53  	scopedLog.Info("Successfully reconciled")
    54  	return ctrl.Result{}, nil
    55  }
    56  
    57  func hasAnyPort(svc *corev1.Service, ports []string) bool {
    58  	for _, p := range ports {
    59  		for _, port := range svc.Spec.Ports {
    60  			if p == getServiceFrontendPort(port) {
    61  				return true
    62  			}
    63  		}
    64  	}
    65  	return false
    66  }
    67  
    68  func getServiceFrontendPort(port corev1.ServicePort) string {
    69  	if port.Port != 0 {
    70  		return strconv.Itoa(int(port.Port))
    71  	}
    72  	if port.NodePort != 0 {
    73  		return strconv.Itoa(int(port.NodePort))
    74  	}
    75  	return port.Name
    76  }
    77  
    78  func (r *ciliumEnvoyConfigReconciler) createOrUpdateEnvoyConfig(ctx context.Context, svc *corev1.Service) error {
    79  	desired, err := r.getEnvoyConfigForService(svc)
    80  	if err != nil {
    81  		return fmt.Errorf("failed to get CiliumEnvoyConfig for service: %w", err)
    82  	}
    83  
    84  	if err := controllerutil.SetControllerReference(svc, desired, r.client.Scheme()); err != nil {
    85  		return fmt.Errorf("failed to set owner reference: %w", err)
    86  	}
    87  
    88  	exists := true
    89  	existing := ciliumv2.CiliumEnvoyConfig{}
    90  	if err := r.client.Get(ctx, types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name}, &existing); err != nil {
    91  		if !k8serrors.IsNotFound(err) {
    92  			return fmt.Errorf(" failed to lookup CiliumEnvoyConfig: %w", err)
    93  		}
    94  		exists = false
    95  	}
    96  
    97  	scopedLog := r.logger.WithField(logfields.ServiceKey, getName(svc))
    98  	if exists {
    99  		if desired.DeepEqual(&existing) {
   100  			r.logger.WithField(logfields.CiliumEnvoyConfigName, fmt.Sprintf("%s/%s", desired.Namespace, desired.Name)).Debug("No change for existing CiliumEnvoyConfig")
   101  			return nil
   102  		}
   103  
   104  		// Update existing CEC
   105  		updated := existing.DeepCopy()
   106  		updated.Spec = desired.Spec
   107  
   108  		scopedLog.Debug("Updating CiliumEnvoyConfig")
   109  		if err := r.client.Update(ctx, updated); err != nil {
   110  			return fmt.Errorf("failed to update CiliumEnvoyConfig for service: %w", err)
   111  		}
   112  
   113  		scopedLog.Debug("Updated CiliumEnvoyConfig for service")
   114  		return nil
   115  	}
   116  
   117  	scopedLog.Debug("Creating CiliumEnvoyConfig")
   118  	if err := r.client.Create(ctx, desired); err != nil {
   119  		return fmt.Errorf("failed to create CiliumEnvoyConfig for service: %w", err)
   120  	}
   121  
   122  	scopedLog.Debug("Created CiliumEnvoyConfig for service")
   123  	return nil
   124  }
   125  
   126  func (r *ciliumEnvoyConfigReconciler) deleteEnvoyConfig(ctx context.Context, svc *corev1.Service) error {
   127  	existing := ciliumv2.CiliumEnvoyConfig{}
   128  	if err := r.client.Get(ctx, types.NamespacedName{Namespace: svc.Namespace, Name: fmt.Sprintf("%s-%s", ciliumEnvoyLBPrefix, svc.Name)}, &existing); err != nil {
   129  		if !k8serrors.IsNotFound(err) {
   130  			return fmt.Errorf("failed to lookup CiliumEnvoyConfig: %w", err)
   131  		}
   132  		return nil
   133  	}
   134  
   135  	r.logger.Debug("Deleting CiliumEnvoyConfig")
   136  	if err := r.client.Delete(ctx, &existing); err != nil {
   137  		return fmt.Errorf("failed to delete CiliumEnvoyConfig for service: %w", err)
   138  	}
   139  
   140  	return nil
   141  }