123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- package port
- import (
- "fmt"
- "net"
- "strconv"
- "sync"
- "k8s.io/apimachinery/pkg/util/sets"
- )
- type Allocator struct {
- reserved sets.Set[int]
- used sets.Set[int]
- mu sync.Mutex
- }
- // NewAllocator return a port allocator for testing.
- // Example: from: 10, to: 20, mod 4, index 1
- // Reserved ports: 13, 17
- func NewAllocator(from int, to int, mod int, index int) *Allocator {
- pa := &Allocator{
- reserved: sets.New[int](),
- used: sets.New[int](),
- }
- for i := from; i <= to; i++ {
- if i%mod == index {
- pa.reserved.Insert(i)
- }
- }
- return pa
- }
- func (pa *Allocator) Get() int {
- return pa.GetByName("")
- }
- func (pa *Allocator) GetByName(portName string) int {
- var builder *nameBuilder
- if portName == "" {
- builder = &nameBuilder{}
- } else {
- var err error
- builder, err = unmarshalFromName(portName)
- if err != nil {
- fmt.Println(err, portName)
- return 0
- }
- }
- pa.mu.Lock()
- defer pa.mu.Unlock()
- for i := 0; i < 20; i++ {
- port := pa.getByRange(builder.rangePortFrom, builder.rangePortTo)
- if port == 0 {
- return 0
- }
- l, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", strconv.Itoa(port)))
- if err != nil {
- // Maybe not controlled by us, mark it used.
- pa.used.Insert(port)
- continue
- }
- l.Close()
- udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort("0.0.0.0", strconv.Itoa(port)))
- if err != nil {
- continue
- }
- udpConn, err := net.ListenUDP("udp", udpAddr)
- if err != nil {
- // Maybe not controlled by us, mark it used.
- pa.used.Insert(port)
- continue
- }
- udpConn.Close()
- pa.used.Insert(port)
- pa.reserved.Delete(port)
- return port
- }
- return 0
- }
- func (pa *Allocator) getByRange(from, to int) int {
- if from <= 0 {
- port, _ := pa.reserved.PopAny()
- return port
- }
- // choose a random port between from - to
- ports := pa.reserved.UnsortedList()
- for _, port := range ports {
- if port >= from && port <= to {
- return port
- }
- }
- return 0
- }
- func (pa *Allocator) Release(port int) {
- if port <= 0 {
- return
- }
- pa.mu.Lock()
- defer pa.mu.Unlock()
- if pa.used.Has(port) {
- pa.used.Delete(port)
- pa.reserved.Insert(port)
- }
- }
|