github.phpd.cn/cilium/cilium@v1.6.12/pkg/allocator/allocator.go (about) 1 // Copyright 2016-2020 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 allocator 16 17 import ( 18 "context" 19 "fmt" 20 "time" 21 22 "github.com/cilium/cilium/pkg/backoff" 23 "github.com/cilium/cilium/pkg/idpool" 24 "github.com/cilium/cilium/pkg/kvstore" 25 "github.com/cilium/cilium/pkg/lock" 26 "github.com/cilium/cilium/pkg/logging" 27 "github.com/cilium/cilium/pkg/logging/logfields" 28 "github.com/cilium/cilium/pkg/option" 29 "github.com/cilium/cilium/pkg/uuid" 30 "github.com/pkg/errors" 31 32 "github.com/sirupsen/logrus" 33 ) 34 35 var ( 36 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "allocator") 37 38 // ErrIdentityNonExistent is returned if the pod security identity does not exist 39 ErrIdentityNonExistent = errors.New("identity does not exist") 40 ) 41 42 const ( 43 // maxAllocAttempts is the number of attempted allocation requests 44 // performed before failing. 45 maxAllocAttempts = 16 46 47 // listTimeout is the time to wait for the initial list operation to 48 // succeed when creating a new allocator 49 listTimeout = 3 * time.Minute 50 ) 51 52 // Allocator is a distributed ID allocator backed by a KVstore. It maps 53 // arbitrary keys to identifiers. Multiple users on different cluster nodes can 54 // in parallel request the ID for keys and are guaranteed to retrieve the same 55 // ID for an identical key. 56 // 57 // While the details of how keys are stored is delegated to Backend 58 // implementations, some expectations exist. See pkg/kvstore/allocator for 59 // details about the kvstore implementation. 60 // 61 // A node takes a reference to an identity when it is in-use on that node, and 62 // the identity remains in-use if there is any node refernce to it. When an 63 // identity no longer has any node references, it may be garbage collected. No 64 // guarantees are made at that point and the numeric identity may be reused. 65 // Note that the numeric IDs are selected locally and verified with the Backend. 66 // 67 // Lookup ID by key: 68 // 1. Return ID from local cache updated by watcher (no Backend interactions) 69 // 2. Do ListPrefix() on slave key excluding node suffix, return the first 70 // result that matches the exact prefix. 71 // 72 // Lookup key by ID: 73 // 1. Return key from local cache updated by watcher (no Backend interactions) 74 // 2. Do Get() on master key, return result 75 // 76 // Allocate: 77 // 1. Check local key cache, increment, and return if key is already in use 78 // locally (no Backend interactions) 79 // 2. Check local cache updated by watcher, if... 80 // 81 // ... match found: 82 // 2.1 Create a new slave key. This operation is potentially racy as the master 83 // key can be removed in the meantime. 84 // etcd: Create is made conditional on existence of master key 85 // consul: locking 86 // 87 // ... match not found: 88 // 2.1 Select new unused id from local cache 89 // 2.2 Create a new master key with the condition that it may not exist 90 // 2.3 Create a new slave key 91 // 92 // 1.1. If found, increment and return (no Backend interactions) 93 // 2. Lookup ID by key in local cache or via first slave key found in Backend 94 // 95 // Release: 96 // 1. Reduce local reference count until last use (no Backend interactions) 97 // 2. Delete slave key (basePath/value/key1/node1) 98 // This automatically guarantees that when the last node has released the 99 // key, the key is no longer found by Get() 100 // 3. If the node goes down, all slave keys of that node are removed after 101 // the TTL expires (auto release). 102 type Allocator struct { 103 // events is a channel which will receive AllocatorEvent as IDs are 104 // added, modified or removed from the allocator 105 events AllocatorEventChan 106 107 // keyType is an instance of the type to be used as allocator key. 108 keyType AllocatorKey 109 110 // min is the lower limit when allocating IDs. The allocator will never 111 // allocate an ID lesser than this value. 112 min idpool.ID 113 114 // max is the upper limit when allocating IDs. The allocator will never 115 // allocate an ID greater than this value. 116 max idpool.ID 117 118 // prefixMask if set, will be ORed to all selected IDs prior to 119 // allocation 120 prefixMask idpool.ID 121 122 // localKeys contains all keys including their reference count for keys 123 // which have been allocated and are in local use 124 localKeys *localKeys 125 126 // suffix is the suffix attached to keys which must be node specific, 127 // this is typical set to the node's IP address 128 suffix string 129 130 // backoffTemplate is the backoff configuration while allocating 131 backoffTemplate backoff.Exponential 132 133 // slaveKeysMutex protects the concurrent access of the slave key by this 134 // agent. 135 slaveKeysMutex lock.Mutex 136 137 // mainCache is the main cache, representing the allocator contents of 138 // the primary kvstore connection 139 mainCache cache 140 141 // remoteCachesMutex protects accesse to remoteCaches 142 remoteCachesMutex lock.RWMutex 143 144 // remoteCaches is the list of additional remote caches being watched 145 // in addition to the main cache 146 remoteCaches map[*RemoteCache]struct{} 147 148 // stopGC is the channel used to stop the garbage collector 149 stopGC chan struct{} 150 151 // initialListDone is a channel that is closed when the initial 152 // synchronization has completed 153 initialListDone waitChan 154 155 // idPool maintains a pool of available ids for allocation. 156 idPool *idpool.IDPool 157 158 // enableMasterKeyProtection if true, causes master keys that are still in 159 // local use to be automatically re-created 160 enableMasterKeyProtection bool 161 162 // disableGC disables the garbage collector 163 disableGC bool 164 165 // backend is the upstream, shared, backend to which we syncronize local 166 // information 167 backend Backend 168 } 169 170 // AllocatorOption is the base type for allocator options 171 type AllocatorOption func(*Allocator) 172 173 // NewAllocatorForGC returns an allocator that can be used to run RunGC() 174 func NewAllocatorForGC(backend Backend) *Allocator { 175 return &Allocator{backend: backend} 176 } 177 178 // Backend represents clients to remote ID allocation systems, such as KV 179 // Stores. These are used to coordinate key->ID allocation between cilium 180 // nodes. 181 type Backend interface { 182 // DeleteAllKeys will delete all keys. It is used in tests. 183 DeleteAllKeys() 184 185 // Encode encodes a key string as required to conform to the key 186 // restrictions of the backend 187 Encode(string) string 188 189 // AllocateID creates a new key->ID association. This is expected to be a 190 // create-only operation, and the ID may be allocated by another node. An 191 // error in that case is not expected to be fatal. The actual ID is obtained 192 // by Allocator from the local idPool, which is updated with used-IDs as the 193 // Backend makes calls to the handler in ListAndWatch. 194 AllocateID(ctx context.Context, id idpool.ID, key AllocatorKey) error 195 196 // AllocateIDIfLocked behaves like AllocateID but when lock is non-nil the 197 // operation proceeds only if it is still valid. 198 AllocateIDIfLocked(ctx context.Context, id idpool.ID, key AllocatorKey, lock kvstore.KVLocker) error 199 200 // AcquireReference records that this node is using this key->ID mapping. 201 // This is distinct from any reference counting within this agent; only one 202 // reference exists for this node for any number of managed endpoints using 203 // it. 204 // The semantics of cleaning up stale references is delegated to the Backend 205 // implementation. RunGC may need to be invoked. 206 // This can race, and so lock can be provided (via a Lock call, below). 207 AcquireReference(ctx context.Context, id idpool.ID, key AllocatorKey, lock kvstore.KVLocker) error 208 209 // Release releases the use of an ID associated with the provided key. It 210 // does not guard against concurrent calls to 211 // releases.Release(ctx context.Context, key AllocatorKey) (err error) 212 Release(ctx context.Context, id idpool.ID, key AllocatorKey) (err error) 213 214 // UpdateKey refreshes the record that this node is using this key -> id 215 // mapping. When reliablyMissing is set it will also recreate missing master or 216 // slave keys. 217 UpdateKey(ctx context.Context, id idpool.ID, key AllocatorKey, reliablyMissing bool) error 218 219 // UpdateKeyIfLocked behaves like UpdateKey but when lock is non-nil the operation proceeds only if it is still valid. 220 UpdateKeyIfLocked(ctx context.Context, id idpool.ID, key AllocatorKey, reliablyMissing bool, lock kvstore.KVLocker) error 221 222 // Get returns the allocated ID for this key as seen by the Backend. This may 223 // have been created by other agents. 224 Get(ctx context.Context, key AllocatorKey) (idpool.ID, error) 225 226 // GetIfLocked behaves like Get, but but when lock is non-nil the 227 // operation proceeds only if it is still valid. 228 GetIfLocked(ctx context.Context, key AllocatorKey, lock kvstore.KVLocker) (idpool.ID, error) 229 230 // GetByID returns the key associated with this ID, as seen by the Backend. 231 // This may have been created by other agents. 232 GetByID(id idpool.ID) (AllocatorKey, error) 233 234 // Lock provides an opaque lock object that can be used, later, to ensure 235 // that the key has not changed since the lock was created. This can be done 236 // with GetIfLocked. 237 Lock(ctx context.Context, key AllocatorKey) (kvstore.KVLocker, error) 238 239 // ListAndWatch begins synchronizing the local Backend instance with its 240 // remote. 241 ListAndWatch(handler CacheMutations, stopChan chan struct{}) 242 243 // RunGC reaps stale or unused identities within the Backend and makes them 244 // available for reuse. It is used by the cilium-operator and is not invoked 245 // by cilium-agent. 246 // Note: not all Backend implemenations rely on this, such as the kvstore 247 // backends, and may use leases to expire keys. 248 RunGC(staleKeysPrevRound map[string]uint64) (map[string]uint64, error) 249 250 // RunLocksGC reaps stale or unused locks within the Backend. It is used by 251 // the cilium-operator and is not invoked by cilium-agent. Returns 252 // a map of locks currently being held in the KVStore including the ones 253 // that failed to be GCed. 254 // Note: not all Backend implementations rely on this, such as the kvstore 255 // backends, and may use leases to expire keys. 256 RunLocksGC(staleKeysPrevRound map[string]kvstore.Value) (map[string]kvstore.Value, error) 257 258 // Status returns a human-readable status of the Backend. 259 Status() (string, error) 260 } 261 262 // NewAllocator creates a new Allocator. Any type can be used as key as long as 263 // the type implements the AllocatorKey interface. A variable of the type has 264 // to be passed into NewAllocator() to make the type known. The specified base 265 // path is used to prefix all keys in the kvstore. The provided path must be 266 // unique. 267 // 268 // The allocator can be configured by passing in additional options: 269 // - WithEvents() - enable Events channel 270 // - WithMin(id) - minimum ID to allocate (default: 1) 271 // - WithMax(id) - maximum ID to allocate (default max(uint64)) 272 // 273 // After creation, IDs can be allocated with Allocate() and released with 274 // Release() 275 func NewAllocator(typ AllocatorKey, backend Backend, opts ...AllocatorOption) (*Allocator, error) { 276 a := &Allocator{ 277 keyType: typ, 278 backend: backend, 279 min: idpool.ID(1), 280 max: idpool.ID(^uint64(0)), 281 localKeys: newLocalKeys(), 282 stopGC: make(chan struct{}), 283 suffix: uuid.NewUUID().String()[:10], 284 remoteCaches: map[*RemoteCache]struct{}{}, 285 backoffTemplate: backoff.Exponential{ 286 Min: time.Duration(20) * time.Millisecond, 287 Factor: 2.0, 288 }, 289 } 290 291 for _, fn := range opts { 292 fn(a) 293 } 294 295 a.mainCache = newCache(a) 296 297 if a.suffix == "<nil>" { 298 return nil, errors.New("allocator suffix is <nil> and unlikely unique") 299 } 300 301 if a.min < 1 { 302 return nil, errors.New("minimum ID must be >= 1") 303 } 304 305 if a.max <= a.min { 306 return nil, errors.New("maximum ID must be greater than minimum ID") 307 } 308 309 a.idPool = idpool.NewIDPool(a.min, a.max) 310 311 a.initialListDone = a.mainCache.start() 312 if !a.disableGC { 313 go func() { 314 select { 315 case <-a.initialListDone: 316 case <-time.After(listTimeout): 317 log.Fatalf("Timeout while waiting for initial allocator state") 318 } 319 a.startLocalKeySync() 320 }() 321 } 322 323 return a, nil 324 } 325 326 // WithBackend sets this allocator to use backend. It is expected to be used at 327 // initialization. 328 func WithBackend(backend Backend) AllocatorOption { 329 return func(a *Allocator) { 330 a.backend = backend 331 } 332 } 333 334 // WithEvents enables receiving of events. 335 // 336 // CAUTION: When using this function. The provided channel must be continuously 337 // read while NewAllocator() is being called to ensure that the channel does 338 // not block indefinitely while NewAllocator() emits events on it while 339 // populating the initial cache. 340 func WithEvents(events AllocatorEventChan) AllocatorOption { 341 return func(a *Allocator) { a.events = events } 342 } 343 344 // WithMin sets the minimum identifier to be allocated 345 func WithMin(id idpool.ID) AllocatorOption { 346 return func(a *Allocator) { a.min = id } 347 } 348 349 // WithMax sets the maximum identifier to be allocated 350 func WithMax(id idpool.ID) AllocatorOption { 351 return func(a *Allocator) { a.max = id } 352 } 353 354 // WithPrefixMask sets the prefix used for all ID allocations. If set, the mask 355 // will be ORed to all selected IDs prior to allocation. It is the 356 // responsibility of the caller to ensure that the mask is not conflicting with 357 // min..max. 358 func WithPrefixMask(mask idpool.ID) AllocatorOption { 359 return func(a *Allocator) { a.prefixMask = mask } 360 } 361 362 // WithMasterKeyProtection will watch for delete events on master keys and 363 // re-created them if local usage suggests that the key is still in use 364 func WithMasterKeyProtection() AllocatorOption { 365 return func(a *Allocator) { a.enableMasterKeyProtection = true } 366 } 367 368 // WithoutGC disables the use of the garbage collector 369 func WithoutGC() AllocatorOption { 370 return func(a *Allocator) { a.disableGC = true } 371 } 372 373 // GetEvents returns the events channel given to the allocator when 374 // constructed. 375 // Note: This channel is not owned by the allocator! 376 func (a *Allocator) GetEvents() AllocatorEventChan { 377 return a.events 378 } 379 380 // Delete deletes an allocator and stops the garbage collector 381 func (a *Allocator) Delete() { 382 close(a.stopGC) 383 a.mainCache.stop() 384 385 if a.events != nil { 386 close(a.events) 387 } 388 } 389 390 // WaitForInitialSync waits until the initial sync is complete 391 func (a *Allocator) WaitForInitialSync(ctx context.Context) error { 392 select { 393 case <-a.initialListDone: 394 case <-ctx.Done(): 395 return fmt.Errorf("identity sync was cancelled: %s", ctx.Err()) 396 } 397 398 return nil 399 } 400 401 // RangeFunc is the function called by RangeCache 402 type RangeFunc func(idpool.ID, AllocatorKey) 403 404 // ForeachCache iterates over the allocator cache and calls RangeFunc on each 405 // cached entry 406 func (a *Allocator) ForeachCache(cb RangeFunc) { 407 a.mainCache.foreach(cb) 408 409 a.remoteCachesMutex.RLock() 410 for rc := range a.remoteCaches { 411 rc.cache.foreach(cb) 412 } 413 a.remoteCachesMutex.RUnlock() 414 } 415 416 // selectAvailableID selects an available ID. 417 // Returns a triple of the selected ID ORed with prefixMask, the ID string and 418 // the originally selected ID. 419 func (a *Allocator) selectAvailableID() (idpool.ID, string, idpool.ID) { 420 if id := a.idPool.LeaseAvailableID(); id != idpool.NoID { 421 unmaskedID := id 422 id |= a.prefixMask 423 return id, id.String(), unmaskedID 424 } 425 426 return 0, "", 0 427 } 428 429 // AllocatorKey is the interface to implement in order for a type to be used as 430 // key for the allocator. The key's data is assumed to be a collection of 431 // pkg/label.Label, and the functions reflect this somewhat. 432 type AllocatorKey interface { 433 fmt.Stringer 434 435 // GetKey returns the canonical string representation of the key 436 GetKey() string 437 438 // PutKey stores the information in v into the key. This is is the inverse 439 // operation to GetKey 440 PutKey(v string) AllocatorKey 441 442 // GetAsMap returns the key as a collection of "labels" with a key and value. 443 // This is the inverse operation to PutKeyFromMap. 444 GetAsMap() map[string]string 445 446 // PutKeyFromMap stores the labels in v into the key to be used later. This 447 // is the inverse operation to GetAsMap. 448 PutKeyFromMap(v map[string]string) AllocatorKey 449 } 450 451 func (a *Allocator) encodeKey(key AllocatorKey) string { 452 return a.backend.Encode(key.GetKey()) 453 } 454 455 func (a *Allocator) lockedAllocate(ctx context.Context, key AllocatorKey) (idpool.ID, bool, error) { 456 kvstore.Trace("Allocating key in kvstore", nil, logrus.Fields{fieldKey: key}) 457 458 k := a.encodeKey(key) 459 lock, err := a.backend.Lock(ctx, key) 460 if err != nil { 461 return 0, false, err 462 } 463 464 defer lock.Unlock() 465 466 // fetch first key that matches /value/<key> while ignoring the 467 // node suffix 468 value, err := a.GetIfLocked(ctx, key, lock) 469 if err != nil { 470 return 0, false, err 471 } 472 473 kvstore.Trace("kvstore state is: ", nil, logrus.Fields{fieldID: value}) 474 475 a.slaveKeysMutex.Lock() 476 defer a.slaveKeysMutex.Unlock() 477 478 // We shouldn't assume the fact the master key does not exist in the kvstore 479 // that localKeys does not have it. The KVStore might have lost all of its 480 // data but the local agent still holds a reference for the given master key. 481 if value == 0 { 482 value = a.localKeys.lookupKey(k) 483 if value != 0 { 484 // re-create master key 485 if err := a.backend.UpdateKeyIfLocked(ctx, value, key, true, lock); err != nil { 486 return 0, false, fmt.Errorf("unable to re-create missing master key '%s': %s while allocating ID: %s", key, value, err) 487 } 488 } 489 } else { 490 _, err := a.localKeys.allocate(k, key, value) 491 if err != nil { 492 return 0, false, fmt.Errorf("unable to reserve local key '%s': %s", k, err) 493 } 494 } 495 496 if value != 0 { 497 log.WithField(fieldKey, k).Info("Reusing existing global key") 498 499 if err = a.backend.AcquireReference(ctx, value, key, lock); err != nil { 500 a.localKeys.release(k) 501 return 0, false, errors.Wrapf(err, "unable to create slave key '%s'", k) 502 } 503 504 // mark the key as verified in the local cache 505 if err := a.localKeys.verify(k); err != nil { 506 log.WithError(err).Error("BUG: Unable to verify local key") 507 } 508 509 return value, false, nil 510 } 511 512 log.WithField(fieldKey, k).Debug("Allocating new master ID") 513 id, strID, unmaskedID := a.selectAvailableID() 514 if id == 0 { 515 return 0, false, fmt.Errorf("no more available IDs in configured space") 516 } 517 518 kvstore.Trace("Selected available key ID", nil, logrus.Fields{fieldID: id}) 519 520 releaseKeyAndID := func() { 521 a.localKeys.release(k) 522 a.idPool.Release(unmaskedID) // This returns this ID to be re-used for other keys 523 } 524 525 oldID, err := a.localKeys.allocate(k, key, id) 526 if err != nil { 527 a.idPool.Release(unmaskedID) 528 return 0, false, fmt.Errorf("unable to reserve local key '%s': %s", k, err) 529 } 530 531 // Another local writer beat us to allocating an ID for the same key, 532 // start over 533 if id != oldID { 534 releaseKeyAndID() 535 return 0, false, fmt.Errorf("another writer has allocated key %s", k) 536 } 537 538 // Check that this key has not been allocated in the cluster during our 539 // operation here 540 value, err = a.GetNoCache(ctx, key) 541 if err != nil { 542 releaseKeyAndID() 543 return 0, false, err 544 } 545 if value != 0 { 546 releaseKeyAndID() 547 return 0, false, fmt.Errorf("Found master key after proceeding with new allocation for %s", k) 548 } 549 550 err = a.backend.AllocateIDIfLocked(ctx, id, key, lock) 551 if err != nil { 552 // Creation failed. Another agent most likely beat us to allocting this 553 // ID, retry. 554 releaseKeyAndID() 555 return 0, false, fmt.Errorf("unable to allocate ID %s for key %s: %s", strID, key, err) 556 } 557 558 // Notify pool that leased ID is now in-use. 559 a.idPool.Use(unmaskedID) 560 561 if err = a.backend.AcquireReference(ctx, id, key, lock); err != nil { 562 // We will leak the master key here as the key has already been 563 // exposed and may be in use by other nodes. The garbage 564 // collector will release it again. 565 releaseKeyAndID() 566 return 0, false, errors.Wrapf(err, "slave key creation failed '%s'", k) 567 } 568 569 // mark the key as verified in the local cache 570 if err := a.localKeys.verify(k); err != nil { 571 log.WithError(err).Error("BUG: Unable to verify local key") 572 } 573 574 log.WithField(fieldKey, k).Info("Allocated new global key") 575 576 return id, true, nil 577 } 578 579 // Allocate will retrieve the ID for the provided key. If no ID has been 580 // allocated for this key yet, a key will be allocated. If allocation fails, 581 // most likely due to a parallel allocation of the same ID by another user, 582 // allocation is re-attempted for maxAllocAttempts times. 583 // 584 // Returns the ID allocated to the key, if the ID had to be allocated, then 585 // true is returned. An error is returned in case of failure. 586 func (a *Allocator) Allocate(ctx context.Context, key AllocatorKey) (idpool.ID, bool, error) { 587 var ( 588 err error 589 value idpool.ID 590 isNew bool 591 k = a.encodeKey(key) 592 ) 593 594 log.WithField(fieldKey, key).Debug("Allocating key") 595 596 select { 597 case <-a.initialListDone: 598 case <-ctx.Done(): 599 return 0, false, fmt.Errorf("allocation was cancelled while waiting for initial key list to be received: %s", ctx.Err()) 600 } 601 602 kvstore.Trace("Allocating from kvstore", nil, logrus.Fields{fieldKey: key}) 603 604 // make a copy of the template and customize it 605 boff := a.backoffTemplate 606 boff.Name = key.String() 607 608 for attempt := 0; attempt < maxAllocAttempts; attempt++ { 609 // Check our list of local keys already in use and increment the 610 // refcnt. The returned key must be released afterwards. No kvstore 611 // operation was performed for this allocation. 612 // We also do this on every loop as a different Allocate call might have 613 // allocated the key while we are attempting to allocate in this 614 // execution thread. It does not hurt to check if localKeys contains a 615 // reference for the key that we are attempting to allocate. 616 if val := a.localKeys.use(k); val != idpool.NoID { 617 kvstore.Trace("Reusing local id", nil, logrus.Fields{fieldID: val, fieldKey: key}) 618 a.mainCache.insert(key, val) 619 return val, false, nil 620 } 621 622 // FIXME: Add non-locking variant 623 value, isNew, err = a.lockedAllocate(ctx, key) 624 if err == nil { 625 a.mainCache.insert(key, value) 626 log.WithField(fieldKey, key).WithField(fieldID, value).Debug("Allocated key") 627 return value, isNew, nil 628 } 629 630 scopedLog := log.WithFields(logrus.Fields{ 631 fieldKey: key, 632 logfields.Attempt: attempt, 633 }) 634 635 select { 636 case <-ctx.Done(): 637 scopedLog.WithError(ctx.Err()).Warning("Ongoing key allocation has been cancelled") 638 return 0, false, fmt.Errorf("key allocation cancelled: %s", ctx.Err()) 639 default: 640 // Do not log a warning if the error is caused by ErrIdentityNonExistent 641 // and has not reached the maxAllocAttempts 642 if errors.Cause(err) != ErrIdentityNonExistent || attempt == maxAllocAttempts { 643 scopedLog.WithError(err).Warning("Key allocation attempt failed") 644 } 645 } 646 647 kvstore.Trace("Allocation attempt failed", err, logrus.Fields{fieldKey: key, logfields.Attempt: attempt}) 648 649 if waitErr := boff.Wait(ctx); waitErr != nil { 650 return 0, false, waitErr 651 } 652 } 653 654 return 0, false, err 655 } 656 657 // GetIfLocked returns the ID which is allocated to a key. Returns an ID of NoID if no ID 658 // has been allocated to this key yet if the client is still holding the given 659 // lock. 660 func (a *Allocator) GetIfLocked(ctx context.Context, key AllocatorKey, lock kvstore.KVLocker) (idpool.ID, error) { 661 if id := a.mainCache.get(a.encodeKey(key)); id != idpool.NoID { 662 return id, nil 663 } 664 665 return a.backend.GetIfLocked(ctx, key, lock) 666 } 667 668 // Get returns the ID which is allocated to a key. Returns an ID of NoID if no ID 669 // has been allocated to this key yet. 670 func (a *Allocator) Get(ctx context.Context, key AllocatorKey) (idpool.ID, error) { 671 if id := a.mainCache.get(a.encodeKey(key)); id != idpool.NoID { 672 return id, nil 673 } 674 675 return a.GetNoCache(ctx, key) 676 } 677 678 // GetNoCache returns the ID which is allocated to a key in the kvstore, 679 // bypassing the local copy of allocated keys. 680 func (a *Allocator) GetNoCache(ctx context.Context, key AllocatorKey) (idpool.ID, error) { 681 return a.backend.Get(ctx, key) 682 } 683 684 // GetByID returns the key associated with an ID. Returns nil if no key is 685 // associated with the ID. 686 func (a *Allocator) GetByID(id idpool.ID) (AllocatorKey, error) { 687 if key := a.mainCache.getByID(id); key != nil { 688 return key, nil 689 } 690 691 return a.backend.GetByID(id) 692 } 693 694 // Release releases the use of an ID associated with the provided key. After 695 // the last user has released the ID, the key is removed in the KVstore and 696 // the returned lastUse value is true. 697 func (a *Allocator) Release(ctx context.Context, key AllocatorKey) (lastUse bool, err error) { 698 log.WithField(fieldKey, key).Info("Releasing key") 699 700 select { 701 case <-a.initialListDone: 702 case <-ctx.Done(): 703 return false, fmt.Errorf("release was cancelled while waiting for initial key list to be received: %s", ctx.Err()) 704 } 705 706 k := a.encodeKey(key) 707 708 a.slaveKeysMutex.Lock() 709 defer a.slaveKeysMutex.Unlock() 710 711 // release the key locally, if it was the last use, remove the node 712 // specific value key to remove the global reference mark 713 var id idpool.ID 714 lastUse, id, err = a.localKeys.release(k) 715 if err != nil { 716 return lastUse, err 717 } 718 if lastUse { 719 // Since in CRD mode we don't have a way to map which identity is being 720 // used by a node, we need to also pass the ID to the release function. 721 // This allows the CRD store to find the right identity by its ID and 722 // remove the node reference on that identity. 723 a.backend.Release(ctx, id, key) 724 } 725 726 return lastUse, err 727 } 728 729 // RunGC scans the kvstore for unused master keys and removes them 730 func (a *Allocator) RunGC(staleKeysPrevRound map[string]uint64) (map[string]uint64, error) { 731 return a.backend.RunGC(staleKeysPrevRound) 732 } 733 734 // RunLocksGC scans the kvstore for stale locks and removes them 735 func (a *Allocator) RunLocksGC(staleLocksPrevRound map[string]kvstore.Value) (map[string]kvstore.Value, error) { 736 return a.backend.RunLocksGC(staleLocksPrevRound) 737 } 738 739 // DeleteAllKeys will delete all keys. It is expected to be used in tests. 740 func (a *Allocator) DeleteAllKeys() { 741 a.backend.DeleteAllKeys() 742 } 743 744 // syncLocalKeys checks the kvstore and verifies that a master key exists for 745 // all locally used allocations. This will restore master keys if deleted for 746 // some reason. 747 func (a *Allocator) syncLocalKeys() error { 748 // Create a local copy of all local allocations to not require to hold 749 // any locks while performing kvstore operations. Local use can 750 // disappear while we perform the sync but that is fine as worst case, 751 // a master key is created for a slave key that no longer exists. The 752 // garbage collector will remove it again. 753 ids := a.localKeys.getVerifiedIDs() 754 755 for id, value := range ids { 756 if err := a.backend.UpdateKey(context.TODO(), id, value, false); err != nil { 757 log.WithError(err).WithFields(logrus.Fields{ 758 fieldKey: value, 759 fieldID: id, 760 }).Warning("Unable to sync key") 761 } 762 } 763 764 return nil 765 } 766 767 func (a *Allocator) startLocalKeySync() { 768 go func(a *Allocator) { 769 for { 770 if err := a.syncLocalKeys(); err != nil { 771 log.WithError(err).Warning("Unable to run local key sync routine") 772 } 773 774 select { 775 case <-a.stopGC: 776 log.Debug("Stopped master key sync routine") 777 return 778 case <-time.After(option.Config.KVstorePeriodicSync): 779 } 780 } 781 }(a) 782 } 783 784 // AllocatorEventChan is a channel to receive allocator events on 785 type AllocatorEventChan chan AllocatorEvent 786 787 // AllocatorEvent is an event sent over AllocatorEventChan 788 type AllocatorEvent struct { 789 // Typ is the type of event (create / modify / delete) 790 Typ kvstore.EventType 791 792 // ID is the allocated ID 793 ID idpool.ID 794 795 // Key is the key associated with the ID 796 Key AllocatorKey 797 } 798 799 // RemoteCache represents the cache content of an additional kvstore managing 800 // identities. The contents are not directly accessible but will be merged into 801 // the ForeachCache() function. 802 type RemoteCache struct { 803 cache cache 804 allocator *Allocator 805 } 806 807 // WatchRemoteKVStore starts watching an allocator base prefix the kvstore 808 // represents by the provided backend. A local cache of all identities of that 809 // kvstore will be maintained in the RemoteCache structure returned and will 810 // start being reported in the identities returned by the ForeachCache() 811 // function. 812 func (a *Allocator) WatchRemoteKVStore(remoteAlloc *Allocator) *RemoteCache { 813 rc := &RemoteCache{ 814 cache: newCache(remoteAlloc), 815 allocator: remoteAlloc, 816 } 817 818 a.remoteCachesMutex.Lock() 819 a.remoteCaches[rc] = struct{}{} 820 a.remoteCachesMutex.Unlock() 821 822 rc.cache.start() 823 824 return rc 825 } 826 827 // Close stops watching for identities in the kvstore associated with the 828 // remote cache and will clear the local cache. 829 func (rc *RemoteCache) Close() { 830 rc.allocator.remoteCachesMutex.Lock() 831 delete(rc.allocator.remoteCaches, rc) 832 rc.allocator.remoteCachesMutex.Unlock() 833 834 rc.cache.stop() 835 }