kube-router/pkg/utils/utils.go
Aaron U'Ren 6c44013bc5 fix(ipset): ignore non-kube-router ipsets
Attempt to filter out sets that we are not authoritative for to avoid
race conditions with other operators (like Istio) that might be
attempting to modify ipsets at the same time.
2025-10-04 18:30:28 -05:00

89 lines
2.3 KiB
Go

package utils
import (
"context"
"fmt"
"io"
"net"
"strconv"
"sync"
"time"
)
const maxListenTestTimeout = 5 * time.Second
type Listener interface {
OnUpdate(instance interface{})
}
type ListenerFunc func(instance interface{})
func (f ListenerFunc) OnUpdate(instance interface{}) {
f(instance)
}
// Broadcaster holds the details of registered listeners
type Broadcaster struct {
listenerLock sync.RWMutex
listeners []Listener
}
// Add lets to register a listener
func (b *Broadcaster) Add(listener Listener) {
b.listenerLock.Lock()
defer b.listenerLock.Unlock()
b.listeners = append(b.listeners, listener)
}
// Notify notifies an update to registered listeners
func (b *Broadcaster) Notify(instance interface{}) {
b.listenerLock.RLock()
listeners := b.listeners
b.listenerLock.RUnlock()
for _, listener := range listeners {
go listener.OnUpdate(instance)
}
}
// CloseCloserDisregardError it is a common need throughout kube-router's code base to need close a closer in defer
// statements, this allows an action like that to pass a linter as well as describe its intention well
func CloseCloserDisregardError(handler io.Closer) {
_ = handler.Close()
}
// SliceContainsString checks to see if needle is contained within haystack, returns true if found, otherwise
// returns false
func SliceContainsString(needle string, haystack []string) bool {
for _, hay := range haystack {
if needle == hay {
return true
}
}
return false
}
// TCPAddressBindable checks to see if an IP/port is bindable by attempting to open a listener then closing it
// returns nil if successful
func TCPAddressBindable(addr string, port uint16) error {
ctx, cancel := context.WithTimeout(context.Background(), maxListenTestTimeout)
defer cancel()
endpoint := addr + ":" + strconv.Itoa(int(port))
lc := net.ListenConfig{}
ln, err := lc.Listen(ctx, "tcp", endpoint)
if err != nil {
return fmt.Errorf("unable to open %s: %w", endpoint, err)
}
return ln.Close()
}
// ConvertMapKeysToSlice takes a map with a set of keys and then extracts the keys into a slice of the same length
func ConvertMapKeysToSlice[K comparable, V any](mapContainingKeys map[K]V) []K {
keys := make([]K, 0, len(mapContainingKeys))
for k := range mapContainingKeys {
keys = append(keys, k)
}
return keys
}