diff --git a/go.mod b/go.mod index 6e667c6da..92c12013f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/nutanix-cloud-native/prism-go-client v0.5.1 github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba k8s.io/api v0.31.1 k8s.io/apimachinery v0.31.1 k8s.io/client-go v0.31.1 diff --git a/go.sum b/go.sum index 748c512e4..3dbc12f50 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/internal/testing/mock/mock_environment.go b/internal/testing/mock/mock_environment.go index 813e19338..27f906f59 100644 --- a/internal/testing/mock/mock_environment.go +++ b/internal/testing/mock/mock_environment.go @@ -113,9 +113,13 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE filteredAddressesVM := getDefaultVMSpec(MockVMNameFilteredNodeAddresses, cluster) filteredAddressesVM.Status.Resources.NicList = []*prismClientV3.VMNicOutputStatus{{ IPEndpointList: []*prismClientV3.IPAddress{{ - IP: ptr.To("127.100.100.1"), + IP: ptr.To("127.100.10.1"), }, { - IP: ptr.To("127.200.200.1"), + IP: ptr.To("127.200.20.1"), + }, { + IP: ptr.To("127.200.100.64"), + }, { + IP: ptr.To("127.200.200.10"), }, { IP: ptr.To(MockIP), }}, diff --git a/pkg/provider/instances_v2_test.go b/pkg/provider/instances_v2_test.go index 0172bb6fd..a8b3ae0c8 100644 --- a/pkg/provider/instances_v2_test.go +++ b/pkg/provider/instances_v2_test.go @@ -21,6 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "go4.org/netipx" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/rand" "k8s.io/client-go/kubernetes/fake" @@ -67,9 +68,10 @@ var _ = Describe("Test InstancesV2", func() { } i = instancesV2{ nutanixManager: &nutanixManager{ - config: categoryTopologyConfig, - client: kClient, - nutanixClient: mock.CreateMockClient(*mockEnvironment), + config: categoryTopologyConfig, + client: kClient, + nutanixClient: mock.CreateMockClient(*mockEnvironment), + ignoredNodeIPs: &netipx.IPSet{}, }, } }) diff --git a/pkg/provider/manager.go b/pkg/provider/manager.go index e61f6913b..869867b42 100644 --- a/pkg/provider/manager.go +++ b/pkg/provider/manager.go @@ -19,9 +19,11 @@ package provider import ( "context" "fmt" + "net/netip" "strings" prismclientv3 "github.com/nutanix-cloud-native/prism-go-client/v3" + "go4.org/netipx" v1 "k8s.io/api/core/v1" "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" @@ -38,16 +40,40 @@ type nutanixManager struct { client clientset.Interface config config.Config nutanixClient interfaces.Client - ignoredNodeIPs map[string]struct{} + ignoredNodeIPs *netipx.IPSet } func newNutanixManager(config config.Config) (*nutanixManager, error) { klog.V(1).Info("Creating new newNutanixManager") - // Initialize the ignoredNodeIPs map - ignoredNodeIPs := make(map[string]struct{}, len(config.IgnoredNodeIPs)) + // Initialize the ignoredNodeIPs set + ignoredIPsBuilder := netipx.IPSetBuilder{} for _, ip := range config.IgnoredNodeIPs { - ignoredNodeIPs[ip] = struct{}{} + switch { + case strings.Contains(ip, "-"): + ipRange, err := netipx.ParseIPRange(ip) + if err != nil { + return nil, fmt.Errorf("failed to parse ignoredNodeIPs IP range %q: %v", ip, err) + } + ignoredIPsBuilder.AddRange(ipRange) + case strings.Contains(ip, "/"): + prefix, err := netip.ParsePrefix(ip) + if err != nil { + return nil, fmt.Errorf("failed to parse ignoredNodeIPs IP prefix %q: %v", ip, err) + } + ignoredIPsBuilder.AddPrefix(prefix) + default: + parsedIP, err := netip.ParseAddr(ip) + if err != nil { + return nil, fmt.Errorf("failed to parse ignoredNodeIPs IP %q: %v", ip, err) + } + ignoredIPsBuilder.Add(parsedIP) + } + } + + ignoredIPSet, err := ignoredIPsBuilder.IPSet() + if err != nil { + return nil, fmt.Errorf("failed to build ignoredNodeIPs IP set: %v", err) } m := &nutanixManager{ @@ -56,7 +82,7 @@ func newNutanixManager(config config.Config) (*nutanixManager, error) { config: config, clientCache: prismclientv3.NewClientCache(prismclientv3.WithSessionAuth(true)), }, - ignoredNodeIPs: ignoredNodeIPs, + ignoredNodeIPs: ignoredIPSet, } return m, nil } @@ -280,8 +306,12 @@ func (n *nutanixManager) getNodeAddresses(_ context.Context, vm *prismclientv3.V for _, nic := range vm.Status.Resources.NicList { for _, ipEndpoint := range nic.IPEndpointList { if ipEndpoint.IP != nil { + parsedIP, err := netip.ParseAddr(*ipEndpoint.IP) + if err != nil { + return addresses, fmt.Errorf("failed to parse IP address %q: %v", *ipEndpoint.IP, err) + } // Ignore the IP address if it is one of the specified ignoredNodeIPs. - if _, ok := n.ignoredNodeIPs[*ipEndpoint.IP]; !ok { + if !n.ignoredNodeIPs.Contains(parsedIP) { addresses = append(addresses, v1.NodeAddress{ Type: v1.NodeInternalIP, Address: *ipEndpoint.IP, diff --git a/pkg/provider/manager_test.go b/pkg/provider/manager_test.go index 2332c81a9..7b4e142e2 100644 --- a/pkg/provider/manager_test.go +++ b/pkg/provider/manager_test.go @@ -50,8 +50,8 @@ var _ = Describe("Test Manager", func() { nutanixClient := mock.CreateMockClient(*mockEnvironment) nClient, err = nutanixClient.Get() Expect(err).ToNot(HaveOccurred()) - m = nutanixManager{ - config: config.Config{ + mgr, err := newNutanixManager( + config.Config{ TopologyDiscovery: config.TopologyDiscovery{ Type: config.CategoriesTopologyDiscoveryType, TopologyCategories: &config.TopologyCategories{ @@ -59,15 +59,13 @@ var _ = Describe("Test Manager", func() { ZoneCategory: mock.MockDefaultZone, }, }, - IgnoredNodeIPs: []string{"127.100.100.1", "127.200.200.1"}, + IgnoredNodeIPs: []string{"127.100.10.1", "127.200.20.1", "127.200.100.1/24", "127.200.200.1-127.200.200.10"}, }, - client: kClient, - nutanixClient: nutanixClient, - ignoredNodeIPs: map[string]struct{}{ - "127.100.100.1": struct{}{}, - "127.200.200.1": struct{}{}, - }, - } + ) + Expect(err).ShouldNot(HaveOccurred()) + mgr.client = kClient + mgr.nutanixClient = nutanixClient + m = *mgr }) Context("Test HasEmptyTopologyInfo", func() { @@ -158,9 +156,10 @@ var _ = Describe("Test Manager", func() { addresses, err := m.getNodeAddresses(ctx, vm) Expect(err).ShouldNot(HaveOccurred()) Expect(len(addresses)).To(Equal(2), "Received addresses: %v", addresses) - Expect(addresses).Should(ContainElement(v1.NodeAddress{Type: v1.NodeInternalIP, Address: mock.MockIP})) - Expect(addresses).ShouldNot(ContainElement(v1.NodeAddress{Type: v1.NodeInternalIP, Address: "127.100.100.1"})) - Expect(addresses).ShouldNot(ContainElement(v1.NodeAddress{Type: v1.NodeInternalIP, Address: "127.200.200.1"})) + Expect(addresses).Should(ConsistOf( + v1.NodeAddress{Type: v1.NodeInternalIP, Address: mock.MockIP}, + v1.NodeAddress{Type: v1.NodeHostName, Address: *vm.Spec.Name}, + )) }) })