github.com/cilium/cilium@v1.16.2/operator/watchers/cilium_endpoint.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package watchers
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"strconv"
    11  	"sync"
    12  
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/client-go/tools/cache"
    15  
    16  	cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    17  	k8sClient "github.com/cilium/cilium/pkg/k8s/client"
    18  	"github.com/cilium/cilium/pkg/k8s/informer"
    19  	"github.com/cilium/cilium/pkg/k8s/utils"
    20  )
    21  
    22  const identityIndex = "identity"
    23  
    24  var (
    25  	errNoCE  = errors.New("object is not a *cilium_api_v2.CiliumEndpoint")
    26  	indexers = cache.Indexers{
    27  		cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
    28  		identityIndex:        identityIndexFunc,
    29  	}
    30  
    31  	// CiliumEndpointStore contains all CiliumEndpoint present in k8s.
    32  	// Warning: The CiliumEndpoints stored in the cache are not intended to be
    33  	// used for Update operations in k8s as some of its fields were are not
    34  	// populated.
    35  	CiliumEndpointStore cache.Indexer
    36  
    37  	// CiliumEndpointsSynced is closed once the CiliumEndpointStore is synced
    38  	// with k8s.
    39  	CiliumEndpointsSynced = make(chan struct{})
    40  	// once is used to make sure CiliumEndpointsInit is only setup once.
    41  	once sync.Once
    42  )
    43  
    44  // identityIndexFunc index identities by ID.
    45  func identityIndexFunc(obj interface{}) ([]string, error) {
    46  	switch t := obj.(type) {
    47  	case *cilium_api_v2.CiliumEndpoint:
    48  		if t.Status.Identity != nil {
    49  			id := strconv.FormatInt(t.Status.Identity.ID, 10)
    50  			return []string{id}, nil
    51  		}
    52  		return []string{"0"}, nil
    53  	}
    54  	return nil, fmt.Errorf("%w - found %T", errNoCE, obj)
    55  }
    56  
    57  // CiliumEndpointsInit starts a CiliumEndpointWatcher
    58  func CiliumEndpointsInit(ctx context.Context, wg *sync.WaitGroup, clientset k8sClient.Clientset) {
    59  	once.Do(func() {
    60  		CiliumEndpointStore = cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, indexers)
    61  
    62  		ciliumEndpointInformer := informer.NewInformerWithStore(
    63  			utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumEndpointList](clientset.CiliumV2().CiliumEndpoints("")),
    64  			&cilium_api_v2.CiliumEndpoint{},
    65  			0,
    66  			cache.ResourceEventHandlerFuncs{},
    67  			transformToCiliumEndpoint,
    68  			CiliumEndpointStore,
    69  		)
    70  
    71  		wg.Add(1)
    72  		go func() {
    73  			defer wg.Done()
    74  			ciliumEndpointInformer.Run(ctx.Done())
    75  		}()
    76  
    77  		cache.WaitForCacheSync(ctx.Done(), ciliumEndpointInformer.HasSynced)
    78  		close(CiliumEndpointsSynced)
    79  	})
    80  }
    81  
    82  // transformToCiliumEndpoint transforms a CiliumEndpoint to a minimal CiliumEndpoint
    83  // containing only a minimal set of entities used to identity a CiliumEndpoint
    84  // Warning: The CiliumEndpoints created by the converter are not intended to be
    85  // used for Update operations in k8s. If the given obj can't be cast into either
    86  // CiliumEndpoint nor DeletedFinalStateUnknown, an error is returned.
    87  func transformToCiliumEndpoint(obj interface{}) (interface{}, error) {
    88  	switch concreteObj := obj.(type) {
    89  	case *cilium_api_v2.CiliumEndpoint:
    90  		p := &cilium_api_v2.CiliumEndpoint{
    91  			TypeMeta: concreteObj.TypeMeta,
    92  			ObjectMeta: metav1.ObjectMeta{
    93  				Name:            concreteObj.Name,
    94  				Namespace:       concreteObj.Namespace,
    95  				ResourceVersion: concreteObj.ResourceVersion,
    96  				OwnerReferences: concreteObj.OwnerReferences,
    97  				UID:             concreteObj.UID,
    98  			},
    99  			Status: cilium_api_v2.EndpointStatus{
   100  				Identity:   concreteObj.Status.Identity,
   101  				Networking: concreteObj.Status.Networking,
   102  				NamedPorts: concreteObj.Status.NamedPorts,
   103  				Encryption: concreteObj.Status.Encryption,
   104  			},
   105  		}
   106  		*concreteObj = cilium_api_v2.CiliumEndpoint{}
   107  		return p, nil
   108  	case cache.DeletedFinalStateUnknown:
   109  		ciliumEndpoint, ok := concreteObj.Obj.(*cilium_api_v2.CiliumEndpoint)
   110  		if !ok {
   111  			return nil, fmt.Errorf("unknown object type %T", concreteObj.Obj)
   112  		}
   113  		dfsu := cache.DeletedFinalStateUnknown{
   114  			Key: concreteObj.Key,
   115  			Obj: &cilium_api_v2.CiliumEndpoint{
   116  				TypeMeta: ciliumEndpoint.TypeMeta,
   117  				ObjectMeta: metav1.ObjectMeta{
   118  					Name:            ciliumEndpoint.Name,
   119  					Namespace:       ciliumEndpoint.Namespace,
   120  					ResourceVersion: ciliumEndpoint.ResourceVersion,
   121  					OwnerReferences: ciliumEndpoint.OwnerReferences,
   122  					UID:             ciliumEndpoint.UID,
   123  				},
   124  				Status: cilium_api_v2.EndpointStatus{
   125  					Identity:   ciliumEndpoint.Status.Identity,
   126  					Networking: ciliumEndpoint.Status.Networking,
   127  					NamedPorts: ciliumEndpoint.Status.NamedPorts,
   128  					Encryption: ciliumEndpoint.Status.Encryption,
   129  				},
   130  			},
   131  		}
   132  		// Small GC optimization
   133  		*ciliumEndpoint = cilium_api_v2.CiliumEndpoint{}
   134  		return dfsu, nil
   135  	default:
   136  		return nil, fmt.Errorf("unknown object type %T", concreteObj)
   137  	}
   138  }
   139  
   140  // HasCE returns true or false if the Cilium Endpoint store has the endpoint
   141  // with the given name.
   142  func HasCE(ns, name string) (*cilium_api_v2.CiliumEndpoint, bool, error) {
   143  	if CiliumEndpointStore == nil {
   144  		return nil, false, nil
   145  	}
   146  	cepKey := fmt.Sprintf("%s/%s", ns, name)
   147  	item, exists, err := CiliumEndpointStore.GetByKey(cepKey)
   148  	if err != nil {
   149  		return nil, false, err
   150  	}
   151  	if !exists {
   152  		return nil, false, nil
   153  	}
   154  	cep := item.(*cilium_api_v2.CiliumEndpoint)
   155  	return cep, exists, nil
   156  }