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 }