github.com/cilium/cilium@v1.16.2/pkg/identity/cache/cache.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package cache
     5  
     6  import (
     7  	"context"
     8  	"reflect"
     9  
    10  	"github.com/cilium/cilium/api/v1/models"
    11  	"github.com/cilium/cilium/pkg/allocator"
    12  	"github.com/cilium/cilium/pkg/identity"
    13  	"github.com/cilium/cilium/pkg/identity/key"
    14  	identitymodel "github.com/cilium/cilium/pkg/identity/model"
    15  	"github.com/cilium/cilium/pkg/idpool"
    16  	"github.com/cilium/cilium/pkg/labels"
    17  	"github.com/cilium/cilium/pkg/logging"
    18  	"github.com/cilium/cilium/pkg/logging/logfields"
    19  )
    20  
    21  var (
    22  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "identity-cache")
    23  )
    24  
    25  // IdentitiesModel is a wrapper so that we can implement the sort.Interface
    26  // to sort the slice by ID
    27  type IdentitiesModel []*models.Identity
    28  
    29  // Less returns true if the element in index `i` is lower than the element
    30  // in index `j`
    31  func (s IdentitiesModel) Less(i, j int) bool {
    32  	return s[i].ID < s[j].ID
    33  }
    34  
    35  // FromIdentityCache populates the provided model from an identity cache.
    36  func (s IdentitiesModel) FromIdentityCache(cache identity.IdentityMap) IdentitiesModel {
    37  	for id, lbls := range cache {
    38  		s = append(s, identitymodel.CreateModel(&identity.Identity{
    39  			ID:     id,
    40  			Labels: lbls.Labels(),
    41  		}))
    42  	}
    43  	return s
    44  }
    45  
    46  // GetIdentityCache returns a cache of all known identities
    47  func (m *CachingIdentityAllocator) GetIdentityCache() identity.IdentityMap {
    48  	log.Debug("getting identity cache for identity allocator manager")
    49  	cache := identity.IdentityMap{}
    50  
    51  	if m.isGlobalIdentityAllocatorInitialized() {
    52  		m.IdentityAllocator.ForeachCache(func(id idpool.ID, val allocator.AllocatorKey) {
    53  			if val != nil {
    54  				if gi, ok := val.(*key.GlobalIdentity); ok {
    55  					cache[identity.NumericIdentity(id)] = gi.LabelArray
    56  				} else {
    57  					log.Warningf("Ignoring unknown identity type '%s': %+v",
    58  						reflect.TypeOf(val), val)
    59  				}
    60  			}
    61  		})
    62  	}
    63  
    64  	identity.IterateReservedIdentities(func(ni identity.NumericIdentity, id *identity.Identity) {
    65  		cache[ni] = id.Labels.LabelArray()
    66  	})
    67  
    68  	for _, identity := range m.localIdentities.GetIdentities() {
    69  		cache[identity.ID] = identity.Labels.LabelArray()
    70  	}
    71  	for _, identity := range m.localNodeIdentities.GetIdentities() {
    72  		cache[identity.ID] = identity.Labels.LabelArray()
    73  	}
    74  
    75  	return cache
    76  }
    77  
    78  // GetIdentities returns all known identities
    79  func (m *CachingIdentityAllocator) GetIdentities() IdentitiesModel {
    80  	identities := IdentitiesModel{}
    81  
    82  	if m.isGlobalIdentityAllocatorInitialized() {
    83  		m.IdentityAllocator.ForeachCache(func(id idpool.ID, val allocator.AllocatorKey) {
    84  			if gi, ok := val.(*key.GlobalIdentity); ok {
    85  				identity := identity.NewIdentityFromLabelArray(identity.NumericIdentity(id), gi.LabelArray)
    86  				identities = append(identities, identitymodel.CreateModel(identity))
    87  			}
    88  
    89  		})
    90  	}
    91  	identity.IterateReservedIdentities(func(ni identity.NumericIdentity, id *identity.Identity) {
    92  		identities = append(identities, identitymodel.CreateModel(id))
    93  	})
    94  
    95  	for _, v := range m.localIdentities.GetIdentities() {
    96  		identities = append(identities, identitymodel.CreateModel(v))
    97  	}
    98  	for _, v := range m.localNodeIdentities.GetIdentities() {
    99  		identities = append(identities, identitymodel.CreateModel(v))
   100  	}
   101  
   102  	return identities
   103  }
   104  
   105  type identityWatcher struct {
   106  	owner IdentityAllocatorOwner
   107  }
   108  
   109  // collectEvent records the 'event' as an added or deleted identity,
   110  // and makes sure that any identity is present in only one of the sets
   111  // (added or deleted).
   112  func collectEvent(event allocator.AllocatorEvent, added, deleted identity.IdentityMap) bool {
   113  	id := identity.NumericIdentity(event.ID)
   114  	// Only create events have the key
   115  	if event.Typ == allocator.AllocatorChangeUpsert {
   116  		if gi, ok := event.Key.(*key.GlobalIdentity); ok {
   117  			// Un-delete the added ID if previously
   118  			// 'deleted' so that collected events can be
   119  			// processed in any order.
   120  			delete(deleted, id)
   121  			added[id] = gi.LabelArray
   122  			return true
   123  		}
   124  		log.Warningf("collectEvent: Ignoring unknown identity type '%s': %+v",
   125  			reflect.TypeOf(event.Key), event.Key)
   126  		return false
   127  	}
   128  	// Reverse an add when subsequently deleted
   129  	delete(added, id)
   130  	// record the id deleted even if an add was reversed, as the
   131  	// id may also have previously existed, in which case the
   132  	// result is not no-op!
   133  	deleted[id] = labels.LabelArray{}
   134  
   135  	return true
   136  }
   137  
   138  // watch starts the identity watcher
   139  func (w *identityWatcher) watch(events allocator.AllocatorEventRecvChan) {
   140  
   141  	go func() {
   142  		for {
   143  			added := identity.IdentityMap{}
   144  			deleted := identity.IdentityMap{}
   145  		First:
   146  			for {
   147  				event, ok := <-events
   148  				// Wait for one identity add or delete or stop
   149  				if !ok {
   150  					// 'events' was closed
   151  					return
   152  				}
   153  				// Collect first added and deleted labels
   154  				switch event.Typ {
   155  				case allocator.AllocatorChangeUpsert, allocator.AllocatorChangeDelete:
   156  					if collectEvent(event, added, deleted) {
   157  						// First event collected
   158  						break First
   159  					}
   160  				}
   161  			}
   162  
   163  		More:
   164  			for {
   165  				// see if there is more, but do not wait nor stop
   166  				select {
   167  				case event, ok := <-events:
   168  					if !ok {
   169  						// 'events' was closed
   170  						break More
   171  					}
   172  					// Collect more added and deleted labels
   173  					switch event.Typ {
   174  					case allocator.AllocatorChangeUpsert, allocator.AllocatorChangeDelete:
   175  						collectEvent(event, added, deleted)
   176  					}
   177  				default:
   178  					// No more events available without blocking
   179  					break More
   180  				}
   181  			}
   182  			// Issue collected updates
   183  			w.owner.UpdateIdentities(added, deleted) // disjoint sets
   184  		}
   185  	}()
   186  }
   187  
   188  // isGlobalIdentityAllocatorInitialized returns true if m.IdentityAllocator is not nil.
   189  // Note: This does not mean that the identities have been synchronized,
   190  // see WaitForInitialGlobalIdentities to wait for a fully populated cache.
   191  func (m *CachingIdentityAllocator) isGlobalIdentityAllocatorInitialized() bool {
   192  	select {
   193  	case <-m.globalIdentityAllocatorInitialized:
   194  		return m.IdentityAllocator != nil
   195  	default:
   196  		return false
   197  	}
   198  }
   199  
   200  // LookupIdentity looks up the identity by its labels but does not create it.
   201  // This function will first search through the local cache, then the caches for
   202  // remote kvstores and finally fall back to the main kvstore.
   203  // May return nil for lookups if the allocator has not yet been synchronized.
   204  func (m *CachingIdentityAllocator) LookupIdentity(ctx context.Context, lbls labels.Labels) *identity.Identity {
   205  	if reservedIdentity := identity.LookupReservedIdentityByLabels(lbls); reservedIdentity != nil {
   206  		return reservedIdentity
   207  	}
   208  
   209  	switch identity.ScopeForLabels(lbls) {
   210  	case identity.IdentityScopeLocal:
   211  		return m.localIdentities.lookup(lbls)
   212  	case identity.IdentityScopeRemoteNode:
   213  		return m.localNodeIdentities.lookup(lbls)
   214  	}
   215  
   216  	if !m.isGlobalIdentityAllocatorInitialized() {
   217  		return nil
   218  	}
   219  
   220  	lblArray := lbls.LabelArray()
   221  	id, err := m.IdentityAllocator.GetIncludeRemoteCaches(ctx, &key.GlobalIdentity{LabelArray: lblArray})
   222  	if err != nil {
   223  		return nil
   224  	}
   225  	if id > identity.MaxNumericIdentity {
   226  		return nil
   227  	}
   228  
   229  	if id == idpool.NoID {
   230  		return nil
   231  	}
   232  
   233  	return identity.NewIdentityFromLabelArray(identity.NumericIdentity(id), lblArray)
   234  }
   235  
   236  var unknownIdentity = identity.NewIdentity(identity.IdentityUnknown, labels.Labels{labels.IDNameUnknown: labels.NewLabel(labels.IDNameUnknown, "", labels.LabelSourceReserved)})
   237  
   238  // LookupIdentityByID returns the identity by ID. This function will first
   239  // search through the local cache, then the caches for remote kvstores and
   240  // finally fall back to the main kvstore
   241  // May return nil for lookups if the allocator has not yet been synchronized.
   242  func (m *CachingIdentityAllocator) LookupIdentityByID(ctx context.Context, id identity.NumericIdentity) *identity.Identity {
   243  	if id == identity.IdentityUnknown {
   244  		return unknownIdentity
   245  	}
   246  
   247  	if identity := identity.LookupReservedIdentity(id); identity != nil {
   248  		return identity
   249  	}
   250  
   251  	switch id.Scope() {
   252  	case identity.IdentityScopeLocal:
   253  		return m.localIdentities.lookupByID(id)
   254  	case identity.IdentityScopeRemoteNode:
   255  		return m.localNodeIdentities.lookupByID(id)
   256  	}
   257  
   258  	if !m.isGlobalIdentityAllocatorInitialized() {
   259  		return nil
   260  	}
   261  
   262  	allocatorKey, err := m.IdentityAllocator.GetByIDIncludeRemoteCaches(ctx, idpool.ID(id))
   263  	if err != nil {
   264  		return nil
   265  	}
   266  
   267  	if gi, ok := allocatorKey.(*key.GlobalIdentity); ok {
   268  		return identity.NewIdentityFromLabelArray(id, gi.LabelArray)
   269  	}
   270  
   271  	return nil
   272  }