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 }