github.com/cilium/cilium@v1.16.2/operator/pkg/ciliumidentity/cache.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ciliumidentity 5 6 import ( 7 "errors" 8 "strconv" 9 10 "github.com/cilium/cilium/pkg/identity/key" 11 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 12 "github.com/cilium/cilium/pkg/lock" 13 ) 14 15 // SecIDs is used to handle duplicate CIDs. Operator itself will not generate 16 // duplicate CIDs. This is required when migrating to Operator managing CIDs. 17 // Operator is compatible with Agents simultaneously managing CIDs. 18 type SecIDs struct { 19 selectedID string 20 ids map[string]struct{} 21 } 22 23 type CIDState struct { 24 // Maps CID name to a GlobalIdentity which holds labels. 25 idToLabels map[string]*key.GlobalIdentity 26 // Maps label string generated from GlobalIdentity.GetKey() to CID name. 27 labelsToID map[string]*SecIDs 28 mu lock.RWMutex 29 } 30 31 func NewCIDState() *CIDState { 32 cidState := &CIDState{ 33 idToLabels: make(map[string]*key.GlobalIdentity), 34 labelsToID: make(map[string]*SecIDs), 35 } 36 37 return cidState 38 } 39 40 func (c *CIDState) Upsert(id string, k *key.GlobalIdentity) { 41 if len(id) == 0 || k == nil { 42 return 43 } 44 45 c.mu.Lock() 46 defer c.mu.Unlock() 47 48 if _, exists := c.idToLabels[id]; exists { 49 return 50 } 51 52 c.idToLabels[id] = k 53 54 keyStr := k.GetKey() 55 secIDs, exists := c.labelsToID[keyStr] 56 if !exists { 57 c.labelsToID[keyStr] = &SecIDs{ 58 selectedID: id, 59 ids: map[string]struct{}{id: {}}, 60 } 61 return 62 } 63 64 secIDs.ids[id] = struct{}{} 65 } 66 67 func (c *CIDState) Remove(id string) { 68 if len(id) == 0 { 69 return 70 } 71 72 c.mu.Lock() 73 defer c.mu.Unlock() 74 75 k, exists := c.idToLabels[id] 76 if !exists { 77 return 78 } 79 80 delete(c.idToLabels, id) 81 82 keyStr := k.GetKey() 83 secIDs := c.labelsToID[keyStr] 84 85 delete(secIDs.ids, id) 86 if len(secIDs.ids) == 0 { 87 delete(c.labelsToID, keyStr) 88 return 89 } 90 91 // After removing id, we need to set another one in selectedID by taking it 92 // from the duplicates. 93 if secIDs.selectedID == id { 94 for nextID := range secIDs.ids { 95 secIDs.selectedID = nextID 96 break 97 } 98 } 99 } 100 101 func (c *CIDState) LookupByID(id string) (*key.GlobalIdentity, bool) { 102 c.mu.RLock() 103 defer c.mu.RUnlock() 104 105 k, exists := c.idToLabels[id] 106 return k, exists 107 } 108 109 func (c *CIDState) LookupByKey(k *key.GlobalIdentity) (string, bool) { 110 if k == nil { 111 return "", false 112 } 113 114 c.mu.RLock() 115 defer c.mu.RUnlock() 116 117 secIDs, exists := c.labelsToID[k.GetKey()] 118 if !exists { 119 return "", false 120 } 121 122 return secIDs.selectedID, true 123 } 124 125 type CIDUsageInPods struct { 126 podToCID map[string]string 127 cidUsageCount map[string]int 128 129 mu lock.RWMutex 130 } 131 132 func NewCIDUsageInPods() *CIDUsageInPods { 133 return &CIDUsageInPods{ 134 podToCID: make(map[string]string), 135 cidUsageCount: make(map[string]int), 136 } 137 } 138 139 // AssignCIDToPod updates the pod to CID map and increments the CID usage. 140 // It also decrements the previous CID usage and returns the CID name of 141 // previously set CID and its usage count after decrementing the CID usage. 142 // The return values are used to track when old CIDs are no longer used. 143 func (c *CIDUsageInPods) AssignCIDToPod(podName, cidName string) (string, int) { 144 c.mu.Lock() 145 defer c.mu.Unlock() 146 147 var prevCIDUsageCount int 148 prevCIDName, exists := c.podToCID[podName] 149 if exists { 150 if cidName == prevCIDName { 151 return cidName, c.cidUsageCount[cidName] 152 } 153 154 prevCIDUsageCount = c.decrementUsage(prevCIDName) 155 } 156 157 c.podToCID[podName] = cidName 158 c.cidUsageCount[cidName]++ 159 160 return prevCIDName, prevCIDUsageCount 161 } 162 163 // RemovePod removes the pod from the pod to CID map, decrements the CID usage 164 // and returns the CID name and its usage count after decrementing the usage. 165 // The return values are used to track when old CIDs are no longer used. 166 func (c *CIDUsageInPods) RemovePod(podName string) (string, int, error) { 167 c.mu.Lock() 168 defer c.mu.Unlock() 169 170 cidName, exists := c.podToCID[podName] 171 if !exists { 172 return "", 0, errors.New("cilium identity not found in pods usage cache") 173 } 174 count := c.decrementUsage(cidName) 175 delete(c.podToCID, podName) 176 177 return cidName, count, nil 178 } 179 180 func (c *CIDUsageInPods) CIDUsedByPod(podName string) (string, bool) { 181 if len(podName) == 0 { 182 return "", false 183 } 184 185 c.mu.RLock() 186 defer c.mu.RUnlock() 187 188 cidName, exists := c.podToCID[podName] 189 return cidName, exists 190 } 191 192 func (c *CIDUsageInPods) CIDUsageCount(cidName string) int { 193 if len(cidName) == 0 { 194 return 0 195 } 196 197 c.mu.RLock() 198 defer c.mu.RUnlock() 199 200 return c.cidUsageCount[cidName] 201 } 202 203 // decrementUsage reduces the usage count for a CID and removes it from the map 204 // if the count is 0. Must be used only after acquiring the write lock. 205 func (c *CIDUsageInPods) decrementUsage(cidName string) int { 206 c.cidUsageCount[cidName]-- 207 208 count := c.cidUsageCount[cidName] 209 if count == 0 { 210 delete(c.cidUsageCount, cidName) 211 } 212 213 return count 214 } 215 216 type CIDUsageInCES struct { 217 cidUsageCount map[int64]int 218 prevCIDsUsedInCES map[string][]int64 219 220 mu lock.RWMutex 221 } 222 223 func NewCIDUsageInCES() *CIDUsageInCES { 224 return &CIDUsageInCES{ 225 cidUsageCount: make(map[int64]int), 226 prevCIDsUsedInCES: make(map[string][]int64), 227 } 228 } 229 230 // ProcessCESUpsert updates the CID usage in CES based on the provided CES. 231 // When the CES is new, it will just add all used CIDs. When CES is updated, it 232 // uses previous CID usage for the same CES, that it tracks, to accordingly 233 // reduce CID usage in CES. 234 func (c *CIDUsageInCES) ProcessCESUpsert(cesName string, endpoints []v2alpha1.CoreCiliumEndpoint) []int64 { 235 if cesName == "" { 236 return nil 237 } 238 239 var cidsWithNoCESUsage []int64 240 newUsedCIDs := make([]int64, len(endpoints)) 241 242 c.mu.Lock() 243 defer c.mu.Unlock() 244 245 for i, cep := range endpoints { 246 c.cidUsageCount[cep.IdentityID]++ 247 newUsedCIDs[i] = cep.IdentityID 248 } 249 250 for _, cid := range c.prevCIDsUsedInCES[cesName] { 251 count := c.decrementUsage(cid) 252 if count == 0 { 253 cidsWithNoCESUsage = append(cidsWithNoCESUsage, cid) 254 } 255 } 256 257 c.prevCIDsUsedInCES[cesName] = newUsedCIDs 258 259 return cidsWithNoCESUsage 260 } 261 262 // ProcessCESDelete reduces the CID usage in CES, based on the provided CES. 263 func (c *CIDUsageInCES) ProcessCESDelete(cesName string, endpoints []v2alpha1.CoreCiliumEndpoint) []int64 { 264 if cesName == "" { 265 return nil 266 } 267 268 var cidsWithNoCESUsage []int64 269 270 c.mu.Lock() 271 defer c.mu.Unlock() 272 273 for _, cep := range endpoints { 274 count := c.decrementUsage(cep.IdentityID) 275 if count == 0 { 276 cidsWithNoCESUsage = append(cidsWithNoCESUsage, cep.IdentityID) 277 } 278 } 279 280 delete(c.prevCIDsUsedInCES, cesName) 281 282 return cidsWithNoCESUsage 283 } 284 285 func (c *CIDUsageInCES) CIDUsageCount(cidName string) int { 286 if len(cidName) == 0 { 287 return 0 288 } 289 290 cidNum, err := strconv.Atoi(cidName) 291 if err != nil { 292 return 0 293 } 294 295 c.mu.RLock() 296 defer c.mu.RUnlock() 297 298 return c.cidUsageCount[int64(cidNum)] 299 } 300 301 // decrementUsage reduces the usage count for a CID and removes it from the map 302 // if the count is 0. Must be used only after acquiring the write lock. 303 func (c *CIDUsageInCES) decrementUsage(cidName int64) int { 304 c.cidUsageCount[cidName]-- 305 count := c.cidUsageCount[cidName] 306 307 if count == 0 { 308 delete(c.cidUsageCount, cidName) 309 } 310 311 return count 312 }