github.com/elfadel/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  }