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 }