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  }