Skip to content

Commit 5cc04af

Browse files
authored
feat: Add optional filtered node IP address config (#187)
This can be used to e.g. filter out CP endpoint IPs as used by kube-vip. Without this there is the potential to assign the kube-vip IP to the node address, leading to cluster networking failures, as see when testing with DHCP subnets on Nutanix.
1 parent 747d79e commit 5cc04af

File tree

10 files changed

+65
-17
lines changed

10 files changed

+65
-17
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
k8s.io/cloud-provider v0.30.2
1616
k8s.io/component-base v0.30.2
1717
k8s.io/klog/v2 v2.130.0
18+
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
1819
)
1920

2021
require (
@@ -110,7 +111,6 @@ require (
110111
k8s.io/controller-manager v0.30.2 // indirect
111112
k8s.io/kms v0.30.2 // indirect
112113
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
113-
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
114114
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
115115
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
116116
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect

internal/testing/mock/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
MockVMNamePoweredOff = "mock-vm-poweredoff"
3131
MockVMNameCategories = "mock-vm-categories"
3232
MockVMNameNoAddresses = "mock-vm-no-addresses"
33+
MockVMNameFilteredNodeAddresses = "mock-vm-filtered-node-addresses"
3334
MockVMNamePoweredOnClusterCategories = "mock-vm-poweredon-cluster-categories"
3435

3536
MockNodeNameVMNotExisting = "mock-node-no-vm-exists"

internal/testing/mock/mock_environment.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
v1 "k8s.io/api/core/v1"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727
"k8s.io/client-go/kubernetes/fake"
28+
"k8s.io/utils/ptr"
2829

2930
"github.com/nutanix-cloud-native/cloud-provider-nutanix/internal/constants"
3031
)
@@ -109,6 +110,21 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
109110
return nil, err
110111
}
111112

113+
filteredAddressesVM := getDefaultVMSpec(MockVMNameFilteredNodeAddresses, cluster)
114+
filteredAddressesVM.Status.Resources.NicList = []*prismClientV3.VMNicOutputStatus{{
115+
IPEndpointList: []*prismClientV3.IPAddress{{
116+
IP: ptr.To("127.100.100.1"),
117+
}, {
118+
IP: ptr.To("127.200.200.1"),
119+
}, {
120+
IP: ptr.To("127.300.300.1"),
121+
}},
122+
}}
123+
filteredAddressesNode, err := createNodeForVM(ctx, kClient, filteredAddressesVM)
124+
if err != nil {
125+
return nil, err
126+
}
127+
112128
nonExistingVMNode := &v1.Node{
113129
ObjectMeta: metav1.ObjectMeta{
114130
Name: MockNodeNameVMNotExisting,
@@ -139,6 +155,7 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
139155
*categoriesVM.Metadata.UUID: categoriesVM,
140156
*noAddressesVM.Metadata.UUID: noAddressesVM,
141157
*poweredOnVMClusterCategories.Metadata.UUID: poweredOnVMClusterCategories,
158+
*filteredAddressesVM.Metadata.UUID: filteredAddressesVM,
142159
},
143160
managedMockClusters: map[string]*prismClientV3.ClusterIntentResponse{
144161
*cluster.Metadata.UUID: cluster,
@@ -153,6 +170,7 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
153170
MockNodeNameVMNotExisting: nonExistingVMNode,
154171
MockNodeNameNoSystemUUID: noSystemUUIDNode,
155172
MockVMNamePoweredOnClusterCategories: poweredOnClusterCategoriesNode,
173+
MockVMNameFilteredNodeAddresses: filteredAddressesNode,
156174
},
157175
}, nil
158176
}

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func main() {
5151

5252
command := app.NewCloudControllerManagerCommand(ccmOptions,
5353
cloudInitializer, controllerInitializers, map[string]string{}, fss, wait.NeverStop)
54+
5455
code := cli.Run(command)
5556
os.Exit(code)
5657
}

manifests/cm.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ data:
1919
"enableCustomLabeling": false,
2020
"topologyDiscovery": {
2121
"type": "Prism"
22-
}
22+
},
23+
"ignoredNodeIPs": []
2324
}

pkg/provider/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Config struct {
2929
PrismCentral credentialTypes.NutanixPrismEndpoint `json:"prismCentral"`
3030
TopologyDiscovery TopologyDiscovery `json:"topologyDiscovery"`
3131
EnableCustomLabeling bool `json:"enableCustomLabeling"`
32+
IgnoredNodeIPs []string `json:"ignoredNodeIPs,omitempty"`
3233
}
3334

3435
type TopologyDiscovery struct {

pkg/provider/manager.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,28 @@ import (
3535
)
3636

3737
type nutanixManager struct {
38-
client clientset.Interface
39-
config config.Config
40-
nutanixClient interfaces.Client
38+
client clientset.Interface
39+
config config.Config
40+
nutanixClient interfaces.Client
41+
ignoredNodeIPs map[string]struct{}
4142
}
4243

4344
func newNutanixManager(config config.Config) (*nutanixManager, error) {
4445
klog.V(1).Info("Creating new newNutanixManager")
46+
47+
// Initialize the ignoredNodeIPs map
48+
ignoredNodeIPs := make(map[string]struct{}, len(config.IgnoredNodeIPs))
49+
for _, ip := range config.IgnoredNodeIPs {
50+
ignoredNodeIPs[ip] = struct{}{}
51+
}
52+
4553
m := &nutanixManager{
4654
config: config,
4755
nutanixClient: &nutanixClient{
4856
config: config,
4957
clientCache: prismclientv3.NewClientCache(prismclientv3.WithSessionAuth(true)),
5058
},
59+
ignoredNodeIPs: ignoredNodeIPs,
5160
}
5261
return m, nil
5362
}
@@ -262,26 +271,29 @@ func (n *nutanixManager) generateProviderID(ctx context.Context, vmUUID string)
262271
return fmt.Sprintf("%s://%s", constants.ProviderName, strings.ToLower(vmUUID)), nil
263272
}
264273

265-
func (n *nutanixManager) getNodeAddresses(ctx context.Context, vm *prismclientv3.VMIntentResponse) ([]v1.NodeAddress, error) {
274+
func (n *nutanixManager) getNodeAddresses(_ context.Context, vm *prismclientv3.VMIntentResponse) ([]v1.NodeAddress, error) {
266275
if vm == nil {
267276
return nil, fmt.Errorf("vm cannot be nil when getting node addresses")
268277
}
269-
addresses := make([]v1.NodeAddress, 0)
270-
foundIPs := 0
278+
279+
var addresses []v1.NodeAddress
271280
for _, nic := range vm.Status.Resources.NicList {
272281
for _, ipEndpoint := range nic.IPEndpointList {
273282
if ipEndpoint.IP != nil {
274-
addresses = append(addresses, v1.NodeAddress{
275-
Type: v1.NodeInternalIP,
276-
Address: *ipEndpoint.IP,
277-
})
278-
foundIPs++
283+
// Ignore the IP address if it is one of the specified ignoredNodeIPs.
284+
if _, ok := n.ignoredNodeIPs[*ipEndpoint.IP]; !ok {
285+
addresses = append(addresses, v1.NodeAddress{
286+
Type: v1.NodeInternalIP,
287+
Address: *ipEndpoint.IP,
288+
})
289+
}
279290
}
280291
}
281292
}
282-
if foundIPs == 0 {
293+
if len(addresses) == 0 {
283294
return addresses, fmt.Errorf("unable to determine network interfaces from VM with UUID %s", *vm.Metadata.UUID)
284295
}
296+
285297
addresses = append(addresses, v1.NodeAddress{
286298
Type: v1.NodeHostName,
287299
Address: *vm.Spec.Name,

pkg/provider/manager_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,14 @@ var _ = Describe("Test Manager", func() {
5959
ZoneCategory: mock.MockDefaultZone,
6060
},
6161
},
62+
IgnoredNodeIPs: []string{"127.100.100.1", "127.200.200.1"},
6263
},
6364
client: kClient,
6465
nutanixClient: nutanixClient,
66+
ignoredNodeIPs: map[string]struct{}{
67+
"127.100.100.1": struct{}{},
68+
"127.200.200.1": struct{}{},
69+
},
6570
}
6671
})
6772

@@ -146,6 +151,15 @@ var _ = Describe("Test Manager", func() {
146151
),
147152
)
148153
})
154+
155+
It("should filter node addresses if matching specified filtered addresses", func() {
156+
vm := mockEnvironment.GetVM(ctx, mock.MockVMNameFilteredNodeAddresses)
157+
Expect(vm).ToNot(BeNil())
158+
addresses, err := m.getNodeAddresses(ctx, vm)
159+
Expect(err).ShouldNot(HaveOccurred())
160+
Expect(len(addresses)).To(Equal(2), "Received addresses: %v", addresses)
161+
Expect(addresses).Should(ContainElement(v1.NodeAddress{Type: v1.NodeInternalIP, Address: "127.300.300.1"}))
162+
})
149163
})
150164

151165
Context("Test generateProviderID", func() {

test/e2e/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ require (
3232
github.com/cloudflare/circl v1.3.7 // indirect
3333
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
3434
github.com/distribution/reference v0.5.0 // indirect
35-
github.com/docker/docker v26.1.4+incompatible // indirect
35+
github.com/docker/docker v26.1.5+incompatible // indirect
3636
github.com/docker/go-connections v0.5.0 // indirect
3737
github.com/docker/go-units v0.4.0 // indirect
3838
github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect

test/e2e/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
5252
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5353
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
5454
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
55-
github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU=
56-
github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
55+
github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g=
56+
github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
5757
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
5858
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
5959
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=

0 commit comments

Comments
 (0)