github.phpd.cn/cilium/cilium@v1.6.12/pkg/identity/cache/cache.go (about) 1 // Copyright 2016-2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cache 16 17 import ( 18 "context" 19 "reflect" 20 21 "github.com/cilium/cilium/api/v1/models" 22 "github.com/cilium/cilium/pkg/allocator" 23 "github.com/cilium/cilium/pkg/identity" 24 "github.com/cilium/cilium/pkg/idpool" 25 "github.com/cilium/cilium/pkg/kvstore" 26 "github.com/cilium/cilium/pkg/labels" 27 "github.com/cilium/cilium/pkg/logging" 28 "github.com/cilium/cilium/pkg/logging/logfields" 29 ) 30 31 var ( 32 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "identity-cache") 33 ) 34 35 // IdentityCache is a cache of identity to labels mapping 36 type IdentityCache map[identity.NumericIdentity]labels.LabelArray 37 38 // IdentitiesModel is a wrapper so that we can implement the sort.Interface 39 // to sort the slice by ID 40 type IdentitiesModel []*models.Identity 41 42 // Less returns true if the element in index `i` is lower than the element 43 // in index `j` 44 func (s IdentitiesModel) Less(i, j int) bool { 45 return s[i].ID < s[j].ID 46 } 47 48 // GetIdentityCache returns a cache of all known identities 49 func GetIdentityCache() IdentityCache { 50 cache := IdentityCache{} 51 52 if IdentityAllocator != nil { 53 54 IdentityAllocator.ForeachCache(func(id idpool.ID, val allocator.AllocatorKey) { 55 if val != nil { 56 if gi, ok := val.(GlobalIdentity); ok { 57 cache[identity.NumericIdentity(id)] = gi.LabelArray 58 } else { 59 log.Warningf("Ignoring unknown identity type '%s': %+v", 60 reflect.TypeOf(val), val) 61 } 62 } 63 }) 64 } 65 66 for key, identity := range identity.ReservedIdentityCache { 67 cache[key] = identity.Labels.LabelArray() 68 } 69 70 if localIdentities != nil { 71 for _, identity := range localIdentities.GetIdentities() { 72 cache[identity.ID] = identity.Labels.LabelArray() 73 } 74 } 75 76 return cache 77 } 78 79 // GetIdentities returns all known identities 80 func GetIdentities() IdentitiesModel { 81 identities := IdentitiesModel{} 82 83 IdentityAllocator.ForeachCache(func(id idpool.ID, val allocator.AllocatorKey) { 84 if gi, ok := val.(GlobalIdentity); ok { 85 identity := identity.NewIdentityFromLabelArray(identity.NumericIdentity(id), gi.LabelArray) 86 identities = append(identities, identity.GetModel()) 87 } 88 89 }) 90 // append user reserved identities 91 for _, v := range identity.ReservedIdentityCache { 92 identities = append(identities, v.GetModel()) 93 } 94 95 for _, v := range localIdentities.GetIdentities() { 96 identities = append(identities, v.GetModel()) 97 } 98 99 return identities 100 } 101 102 type identityWatcher struct { 103 stopChan chan bool 104 } 105 106 // collectEvent records the 'event' as an added or deleted identity, 107 // and makes sure that any identity is present in only one of the sets 108 // (added or deleted). 109 func collectEvent(event allocator.AllocatorEvent, added, deleted IdentityCache) bool { 110 id := identity.NumericIdentity(event.ID) 111 // Only create events have the key 112 if event.Typ == kvstore.EventTypeCreate { 113 if gi, ok := event.Key.(GlobalIdentity); ok { 114 // Un-delete the added ID if previously 115 // 'deleted' so that collected events can be 116 // processed in any order. 117 if _, exists := deleted[id]; exists { 118 delete(deleted, id) 119 } 120 added[id] = gi.LabelArray 121 return true 122 } 123 log.Warningf("collectEvent: Ignoring unknown identity type '%s': %+v", 124 reflect.TypeOf(event.Key), event.Key) 125 return false 126 } 127 // Reverse an add when subsequently deleted 128 if _, exists := added[id]; exists { 129 delete(added, id) 130 } 131 // record the id deleted even if an add was reversed, as the 132 // id may also have previously existed, in which case the 133 // result is not no-op! 134 deleted[id] = labels.LabelArray{} 135 136 return true 137 } 138 139 // watch starts the identity watcher 140 func (w *identityWatcher) watch(owner IdentityAllocatorOwner, events allocator.AllocatorEventChan) { 141 w.stopChan = make(chan bool) 142 143 go func() { 144 for { 145 added := IdentityCache{} 146 deleted := IdentityCache{} 147 148 First: 149 for { 150 // Wait for one identity add or delete or stop 151 select { 152 case event, ok := <-events: 153 if !ok { 154 // 'events' was closed 155 return 156 } 157 // Collect first added and deleted labels 158 switch event.Typ { 159 case kvstore.EventTypeCreate, kvstore.EventTypeDelete: 160 if collectEvent(event, added, deleted) { 161 // First event collected 162 break First 163 } 164 default: 165 // Ignore modify events 166 } 167 case <-w.stopChan: 168 return 169 } 170 } 171 172 More: 173 for { 174 // see if there is more, but do not wait nor stop 175 select { 176 case event, ok := <-events: 177 if !ok { 178 // 'events' was closed 179 break More 180 } 181 // Collect more added and deleted labels 182 switch event.Typ { 183 case kvstore.EventTypeCreate, kvstore.EventTypeDelete: 184 collectEvent(event, added, deleted) 185 default: 186 // Ignore modify events 187 } 188 default: 189 // No more events available without blocking 190 break More 191 } 192 } 193 // Issue collected updates 194 owner.UpdateIdentities(added, deleted) // disjoint sets 195 } 196 }() 197 } 198 199 // stop stops the identity watcher 200 func (w *identityWatcher) stop() { 201 close(w.stopChan) 202 } 203 204 // LookupIdentity looks up the identity by its labels but does not create it. 205 // This function will first search through the local cache and fall back to 206 // querying the kvstore. 207 func LookupIdentity(lbls labels.Labels) *identity.Identity { 208 if reservedIdentity := LookupReservedIdentityByLabels(lbls); reservedIdentity != nil { 209 return reservedIdentity 210 } 211 212 if identity := localIdentities.lookup(lbls); identity != nil { 213 return identity 214 } 215 216 if IdentityAllocator == nil { 217 return nil 218 } 219 220 lblArray := lbls.LabelArray() 221 id, err := IdentityAllocator.Get(context.TODO(), GlobalIdentity{lblArray}) 222 if err != nil { 223 return nil 224 } 225 226 if id == idpool.NoID { 227 return nil 228 } 229 230 return identity.NewIdentityFromLabelArray(identity.NumericIdentity(id), lblArray) 231 } 232 233 // LookupReservedIdentityByLabels looks up a reserved identity by its labels and 234 // returns it if found. Returns nil if not found. 235 func LookupReservedIdentityByLabels(lbls labels.Labels) *identity.Identity { 236 if identity := identity.WellKnown.LookupByLabels(lbls); identity != nil { 237 return identity 238 } 239 240 for _, lbl := range lbls { 241 switch { 242 // If the set of labels contain a fixed identity then and exists in 243 // the map of reserved IDs then return the identity of that reserved ID. 244 case lbl.Key == labels.LabelKeyFixedIdentity: 245 id := identity.GetReservedID(lbl.Value) 246 if id != identity.IdentityUnknown && identity.IsUserReservedIdentity(id) { 247 return identity.LookupReservedIdentity(id) 248 } 249 // If a fixed identity was not found then we return nil to avoid 250 // falling to a reserved identity. 251 return nil 252 // If it doesn't contain a fixed-identity then make sure the set of 253 // labels only contains a single label and that label is of the reserved 254 // type. This is to prevent users from adding cilium-reserved labels 255 // into the workloads. 256 case lbl.Source == labels.LabelSourceReserved: 257 if len(lbls) != 1 { 258 return nil 259 } 260 id := identity.GetReservedID(lbl.Key) 261 if id != identity.IdentityUnknown && !identity.IsUserReservedIdentity(id) { 262 return identity.LookupReservedIdentity(id) 263 } 264 } 265 } 266 return nil 267 } 268 269 var unknownIdentity = identity.NewIdentity(identity.IdentityUnknown, labels.Labels{labels.IDNameUnknown: labels.NewLabel(labels.IDNameUnknown, "", labels.LabelSourceReserved)}) 270 271 // LookupIdentityByID returns the identity by ID. This function will first 272 // search through the local cache and fall back to querying the kvstore. 273 func LookupIdentityByID(id identity.NumericIdentity) *identity.Identity { 274 if id == identity.IdentityUnknown { 275 return unknownIdentity 276 } 277 278 if identity := identity.LookupReservedIdentity(id); identity != nil { 279 return identity 280 } 281 282 if IdentityAllocator == nil { 283 return nil 284 } 285 286 if identity := localIdentities.lookupByID(id); identity != nil { 287 return identity 288 } 289 290 allocatorKey, err := IdentityAllocator.GetByID(idpool.ID(id)) 291 if err != nil { 292 return nil 293 } 294 295 if gi, ok := allocatorKey.(GlobalIdentity); ok { 296 return identity.NewIdentityFromLabelArray(id, gi.LabelArray) 297 } 298 299 return nil 300 } 301 302 // AddUserDefinedNumericIdentitySet adds all key-value pairs from the given map 303 // to the map of user defined numeric identities and reserved identities. 304 // The key-value pairs should map a numeric identity to a valid label. 305 // Is not safe for concurrent use. 306 func AddUserDefinedNumericIdentitySet(m map[string]string) error { 307 // Validate first 308 for k := range m { 309 ni, err := identity.ParseNumericIdentity(k) 310 if err != nil { 311 return err 312 } 313 if !identity.IsUserReservedIdentity(ni) { 314 return identity.ErrNotUserIdentity 315 } 316 } 317 for k, lbl := range m { 318 ni, _ := identity.ParseNumericIdentity(k) 319 identity.AddUserDefinedNumericIdentity(ni, lbl) 320 identity.AddReservedIdentity(ni, lbl) 321 } 322 return nil 323 }