github.com/cilium/cilium@v1.16.2/pkg/service/id_local.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package service
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/cilium/cilium/pkg/loadbalancer"
    10  	"github.com/cilium/cilium/pkg/lock"
    11  )
    12  
    13  // IDAllocator contains an internal state of the ID allocator.
    14  type IDAllocator struct {
    15  	// Protects entitiesID, entities, nextID and maxID
    16  	lock.RWMutex
    17  
    18  	// entitiesID is a map of all entities indexed by service or backend ID
    19  	entitiesID map[uint32]*loadbalancer.L3n4AddrID
    20  
    21  	// entities is a map of all entities indexed by L3n4Addr.StringID()
    22  	entities map[string]uint32
    23  
    24  	// nextID is the next ID to attempt to allocate
    25  	nextID uint32
    26  
    27  	// maxID is the maximum ID available for allocation
    28  	maxID uint32
    29  
    30  	// initNextID is the initial nextID
    31  	initNextID uint32
    32  
    33  	// initMaxID is the initial maxID
    34  	initMaxID uint32
    35  }
    36  
    37  var (
    38  	serviceIDAlloc = NewIDAllocator(FirstFreeServiceID, MaxSetOfServiceID)
    39  	backendIDAlloc = NewIDAllocator(FirstFreeBackendID, MaxSetOfBackendID)
    40  )
    41  
    42  // NewIDAllocator creates a new ID allocator instance.
    43  func NewIDAllocator(nextID uint32, maxID uint32) *IDAllocator {
    44  	return &IDAllocator{
    45  		entitiesID: map[uint32]*loadbalancer.L3n4AddrID{},
    46  		entities:   map[string]uint32{},
    47  		nextID:     nextID,
    48  		maxID:      maxID,
    49  		initNextID: nextID,
    50  		initMaxID:  maxID,
    51  	}
    52  }
    53  
    54  func (alloc *IDAllocator) addID(svc loadbalancer.L3n4Addr, id uint32) *loadbalancer.L3n4AddrID {
    55  	svcID := newID(svc, id)
    56  	alloc.entitiesID[id] = svcID
    57  	alloc.entities[svc.StringID()] = id
    58  
    59  	return svcID
    60  }
    61  
    62  func (alloc *IDAllocator) acquireLocalID(svc loadbalancer.L3n4Addr, desiredID uint32) (*loadbalancer.L3n4AddrID, error) {
    63  	alloc.Lock()
    64  	defer alloc.Unlock()
    65  
    66  	if svcID, ok := alloc.entities[svc.StringID()]; ok {
    67  		if svc, ok := alloc.entitiesID[svcID]; ok {
    68  			return svc, nil
    69  		}
    70  	}
    71  
    72  	if desiredID != 0 {
    73  		foundSVC, ok := alloc.entitiesID[desiredID]
    74  		if !ok {
    75  			if desiredID >= alloc.nextID {
    76  				// We don't set nextID to desiredID+1 here, as we don't want to
    77  				// duplicate the logic which deals with the rollover. Next
    78  				// invocation of acquireLocalID(..., 0) will fix the nextID.
    79  				alloc.nextID = desiredID
    80  			}
    81  			return alloc.addID(svc, desiredID), nil
    82  		}
    83  		return nil, fmt.Errorf("Service ID %d is already registered to %q",
    84  			desiredID, foundSVC)
    85  	}
    86  
    87  	startingID := alloc.nextID
    88  	rollover := false
    89  	for {
    90  		if alloc.nextID == startingID && rollover {
    91  			break
    92  		} else if alloc.nextID == alloc.maxID {
    93  			alloc.nextID = alloc.initNextID
    94  			rollover = true
    95  		}
    96  
    97  		if _, ok := alloc.entitiesID[alloc.nextID]; !ok {
    98  			svcID := alloc.addID(svc, alloc.nextID)
    99  			alloc.nextID++
   100  			return svcID, nil
   101  		}
   102  
   103  		alloc.nextID++
   104  	}
   105  
   106  	return nil, fmt.Errorf("no service ID available")
   107  }
   108  
   109  func (alloc *IDAllocator) getLocalID(id uint32) (*loadbalancer.L3n4AddrID, error) {
   110  	alloc.RLock()
   111  	defer alloc.RUnlock()
   112  
   113  	if svc, ok := alloc.entitiesID[id]; ok {
   114  		return svc, nil
   115  	}
   116  
   117  	return nil, nil
   118  }
   119  
   120  func (alloc *IDAllocator) deleteLocalID(id uint32) error {
   121  	alloc.Lock()
   122  	defer alloc.Unlock()
   123  
   124  	if svc, ok := alloc.entitiesID[id]; ok {
   125  		delete(alloc.entitiesID, id)
   126  		delete(alloc.entities, svc.StringID())
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  func (alloc *IDAllocator) lookupLocalID(svc loadbalancer.L3n4Addr) (uint32, error) {
   133  	alloc.RLock()
   134  	defer alloc.RUnlock()
   135  
   136  	if svcID, ok := alloc.entities[svc.StringID()]; ok {
   137  		return svcID, nil
   138  	}
   139  
   140  	return 0, fmt.Errorf("ID not found")
   141  }
   142  
   143  func (alloc *IDAllocator) setLocalIDSpace(next, max uint32) error {
   144  	alloc.Lock()
   145  	alloc.nextID = next
   146  	alloc.maxID = max
   147  	alloc.Unlock()
   148  
   149  	return nil
   150  }
   151  
   152  func (alloc *IDAllocator) getLocalMaxID() (uint32, error) {
   153  	alloc.RLock()
   154  	defer alloc.RUnlock()
   155  	return alloc.nextID, nil
   156  }
   157  
   158  func (alloc *IDAllocator) resetLocalID() {
   159  	alloc.Lock()
   160  	alloc.entitiesID = map[uint32]*loadbalancer.L3n4AddrID{}
   161  	alloc.entities = map[string]uint32{}
   162  	alloc.nextID = alloc.initNextID
   163  	alloc.maxID = alloc.initMaxID
   164  	alloc.Unlock()
   165  }
   166  
   167  func newID(svc loadbalancer.L3n4Addr, id uint32) *loadbalancer.L3n4AddrID {
   168  	return &loadbalancer.L3n4AddrID{
   169  		L3n4Addr: svc,
   170  		ID:       loadbalancer.ID(id),
   171  	}
   172  }