Skip to content

Commit e229d23

Browse files
feat: use the node VM customAttributes to set the nodes providerID if it exists (#231)
In CCM node controller, if the below category is set in the node VM, set the "providerID" value with the category value, instead of the current vmUUID value. Only when the category does not exist with the VM, use the current Node.status.nodeInfo.SystemUUID to set the "providerID". ahvMetro-<clusterName>-<vmName> : <vmUUID> The providerID use the format of "nutanix://<vmUUID>".
1 parent 6b0ca26 commit e229d23

File tree

5 files changed

+154
-5
lines changed

5 files changed

+154
-5
lines changed

internal/testing/mock/constants.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ const (
3434
MockVMNamePoweredOnClusterCategories = "mock-vm-poweredon-cluster-categories"
3535
MockVMNameDpOffload = "mock-vm-dp-offload"
3636
MockVMNameSecondaryIPs = "mock-vm-secondary-ips"
37+
MockVMNameCustomProviderID = "mock-vm-custom-provider-id"
3738

38-
MockSecondaryIP1 = "2.2.2.2"
39-
MockSecondaryIP2 = "3.3.3.3"
39+
MockSecondaryIP1 = "2.2.2.2"
40+
MockSecondaryIP2 = "3.3.3.3"
41+
MockCustomProviderID = "custom-provider-uuid-1234"
4042

4143
MockNodeNameVMNotExisting = "mock-node-no-vm-exists"
4244
MockNodeNameNoSystemUUID = "mock-node-no-system-uuid"
@@ -65,6 +67,7 @@ const (
6567
MockVMPoweredOnClusterCategoriesUUID = "00000000-0000-0000-0000-000000000105"
6668
MockVMDpOffloadUUID = "00000000-0000-0000-0000-000000000106"
6769
MockVMSecondaryIPsUUID = "00000000-0000-0000-0000-000000000107"
70+
MockVMCustomProviderIDUUID = "00000000-0000-0000-0000-000000000108"
6871
MockCategoryRegionUUID = "00000000-0000-0000-0000-000000000200"
6972
MockCategoryZoneUUID = "00000000-0000-0000-0000-000000000201"
7073
)

internal/testing/mock/helpers.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,49 @@ func getDefaultVMWithSecondaryIPs(vmName string, vmUUID string, cluster *cluster
175175
return vm
176176
}
177177

178+
func getDefaultVMWithCustomAttributes(vmName string, vmUUID string, cluster *clusterModels.Cluster, host *clusterModels.Host, customAttributes []string) *vmmModels.Vm {
179+
nic := vmmModels.NewNic()
180+
nicNetInfo := vmmModels.NewVirtualEthernetNicNetworkInfo()
181+
182+
nicNetInfo.Ipv4Config = vmmModels.NewIpv4Config()
183+
nicNetInfo.Ipv4Config.IpAddress = &vmmCommonModels.IPv4Address{
184+
Value: ptr.To(MockIP),
185+
}
186+
187+
nicNetInfo.Ipv4Info = vmmModels.NewIpv4Info()
188+
nicNetInfo.Ipv4Info.LearnedIpAddresses = []vmmCommonModels.IPv4Address{
189+
{
190+
Value: ptr.To(MockIP),
191+
},
192+
}
193+
194+
err := nic.SetNicNetworkInfo(*nicNetInfo)
195+
if err != nil {
196+
fmt.Printf("error setting nic network info: %+v\n", err)
197+
return nil
198+
}
199+
200+
vm := &vmmModels.Vm{
201+
ExtId: ptr.To(vmUUID),
202+
Categories: make([]vmmModels.CategoryReference, 0),
203+
PowerState: vmmModels.POWERSTATE_ON.Ref(),
204+
Name: ptr.To(vmName),
205+
CustomAttributes: customAttributes,
206+
Cluster: &vmmModels.ClusterReference{
207+
ExtId: cluster.ExtId,
208+
},
209+
Nics: []vmmModels.Nic{
210+
*nic,
211+
},
212+
}
213+
if host != nil {
214+
vm.Host = &vmmModels.HostReference{
215+
ExtId: host.ExtId,
216+
}
217+
}
218+
return vm
219+
}
220+
178221
func getDefaultCluster(clusterName string, clusterUUID string) *clusterModels.Cluster {
179222
cluster := clusterModels.NewCluster()
180223
cluster.ExtId = ptr.To(clusterUUID)

internal/testing/mock/mock_environment.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,18 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
181181
return nil, err
182182
}
183183

184+
customProviderIDVM := getDefaultVMWithCustomAttributes(
185+
MockVMNameCustomProviderID,
186+
MockVMCustomProviderIDUUID,
187+
cluster,
188+
host,
189+
[]string{"providerID:" + MockCustomProviderID, "otherKey:otherValue"},
190+
)
191+
customProviderIDNode, err := createNodeForVM(ctx, kClient, customProviderIDVM)
192+
if err != nil {
193+
return nil, err
194+
}
195+
184196
return &MockEnvironment{
185197
managedMockMachines: map[string]*vmmModels.Vm{
186198
*poweredOnVM.ExtId: poweredOnVM,
@@ -191,6 +203,7 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
191203
*filteredAddressesVM.ExtId: filteredAddressesVM,
192204
*dpOffloadVM.ExtId: dpOffloadVM,
193205
*secondaryIPsVM.ExtId: secondaryIPsVM,
206+
*customProviderIDVM.ExtId: customProviderIDVM,
194207
},
195208
managedMockClusters: map[string]*clusterModels.Cluster{
196209
*cluster.ExtId: cluster,
@@ -215,6 +228,7 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
215228
MockVMNameFilteredNodeAddresses: filteredAddressesNode,
216229
MockVMNameDpOffload: dpOffloadNode,
217230
MockVMNameSecondaryIPs: secondaryIPsNode,
231+
MockVMNameCustomProviderID: customProviderIDNode,
218232
},
219233
vmNameToExtId: map[string]string{
220234
MockVMNamePoweredOn: *poweredOnVM.ExtId,
@@ -225,6 +239,7 @@ func CreateMockEnvironment(ctx context.Context, kClient *fake.Clientset) (*MockE
225239
MockVMNamePoweredOnClusterCategories: *poweredOnVMClusterCategories.ExtId,
226240
MockVMNameDpOffload: *dpOffloadVM.ExtId,
227241
MockVMNameSecondaryIPs: *secondaryIPsVM.ExtId,
242+
MockVMNameCustomProviderID: *customProviderIDVM.ExtId,
228243
},
229244
}, nil
230245
}

pkg/provider/manager.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,17 @@ func (n *nutanixManager) getInstanceMetadata(ctx context.Context, node *v1.Node)
123123
return nil, err
124124
}
125125

126-
providerID, err := n.generateProviderID(ctx, vmUUID)
126+
nClient, err := n.nutanixClient.Get()
127127
if err != nil {
128128
return nil, err
129129
}
130-
nClient, err := n.nutanixClient.Get()
130+
vm, err := nClient.GetVM(ctx, vmUUID)
131131
if err != nil {
132132
return nil, err
133133
}
134-
vm, err := nClient.GetVM(ctx, vmUUID)
134+
135+
// Generate providerID from VM (checks customAttributes first, then falls back to vmUUID)
136+
providerID, err := n.generateProviderIDFromVM(ctx, vm)
135137
if err != nil {
136138
return nil, err
137139
}
@@ -322,6 +324,39 @@ func (n *nutanixManager) generateProviderID(ctx context.Context, vmUUID string)
322324
return fmt.Sprintf("%s://%s", constants.ProviderName, strings.ToLower(vmUUID)), nil
323325
}
324326

327+
// generateProviderIDFromVM generates the providerID for a node from the VM object.
328+
// It first checks if the VM's customAttributes field has "providerID:<UUID>".
329+
// If yes, it uses this UUID value to set the providerID.
330+
// If not, it falls back to using the vmUUID (backward compatible).
331+
func (n *nutanixManager) generateProviderIDFromVM(ctx context.Context, vm *vmmModels.Vm) (string, error) {
332+
if vm == nil {
333+
return "", fmt.Errorf("VM cannot be nil when generating nutanix provider ID for node")
334+
}
335+
336+
// Check if customAttributes contains providerID
337+
if vm.CustomAttributes != nil {
338+
for _, attr := range vm.CustomAttributes {
339+
// customAttributes are in the format "key:value"
340+
parts := strings.SplitN(attr, ":", 2)
341+
if len(parts) == 2 && strings.ToLower(strings.TrimSpace(parts[0])) == "providerid" {
342+
customProviderID := strings.TrimSpace(parts[1])
343+
if customProviderID != "" {
344+
klog.V(2).Infof("Using custom providerID from customAttributes: %s", customProviderID) //nolint:typecheck
345+
return fmt.Sprintf("%s://%s", constants.ProviderName, customProviderID), nil
346+
}
347+
}
348+
}
349+
}
350+
351+
// Fallback to using vmUUID
352+
if vm.ExtId == nil || *vm.ExtId == "" {
353+
return "", fmt.Errorf("VM ExtId cannot be empty when generating nutanix provider ID for node")
354+
}
355+
356+
klog.V(2).Infof("Using VM ExtId as providerID: %s", *vm.ExtId) //nolint:typecheck
357+
return fmt.Sprintf("%s://%s", constants.ProviderName, strings.ToLower(*vm.ExtId)), nil
358+
}
359+
325360
func (n *nutanixManager) isNodeAddressesSet(node *v1.Node) bool {
326361
if node == nil {
327362
return false

pkg/provider/manager_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,59 @@ var _ = Describe("Test Manager", func() { // nolint:typecheck
268268
})
269269
})
270270

271+
Context("Test generateProviderIDFromVM", func() {
272+
It("should fail if VM is nil", func() { // nolint:typecheck
273+
_, err := m.generateProviderIDFromVM(ctx, nil)
274+
Expect(err).Should(HaveOccurred())
275+
})
276+
277+
It("should return providerID from VM ExtId when no customAttributes", func() { // nolint:typecheck
278+
vm := mockEnvironment.GetVM(ctx, mock.MockVMNamePoweredOn)
279+
Expect(vm).ToNot(BeNil())
280+
providerID, err := m.generateProviderIDFromVM(ctx, vm)
281+
Expect(err).ToNot(HaveOccurred())
282+
Expect(providerID).To(Equal(fmt.Sprintf("nutanix://%s", *vm.ExtId)))
283+
})
284+
285+
It("should return providerID from customAttributes when present", func() { // nolint:typecheck
286+
vm := mockEnvironment.GetVM(ctx, mock.MockVMNameCustomProviderID)
287+
Expect(vm).ToNot(BeNil())
288+
providerID, err := m.generateProviderIDFromVM(ctx, vm)
289+
Expect(err).ToNot(HaveOccurred())
290+
Expect(providerID).To(Equal(fmt.Sprintf("nutanix://%s", mock.MockCustomProviderID)))
291+
})
292+
293+
It("should handle customAttributes with spaces around providerID", func() { // nolint:typecheck
294+
vm := mockEnvironment.GetVM(ctx, mock.MockVMNamePoweredOn)
295+
Expect(vm).ToNot(BeNil())
296+
// Add customAttributes with spaces
297+
vm.CustomAttributes = []string{" providerID : test-uuid-with-spaces ", "otherKey:value"}
298+
providerID, err := m.generateProviderIDFromVM(ctx, vm)
299+
Expect(err).ToNot(HaveOccurred())
300+
Expect(providerID).To(Equal("nutanix://test-uuid-with-spaces"))
301+
})
302+
303+
It("should fallback to ExtId when customAttributes has no providerID", func() { // nolint:typecheck
304+
vm := mockEnvironment.GetVM(ctx, mock.MockVMNamePoweredOn)
305+
Expect(vm).ToNot(BeNil())
306+
// Add customAttributes without providerID
307+
vm.CustomAttributes = []string{"someKey:someValue", "anotherKey:anotherValue"}
308+
providerID, err := m.generateProviderIDFromVM(ctx, vm)
309+
Expect(err).ToNot(HaveOccurred())
310+
Expect(providerID).To(Equal(fmt.Sprintf("nutanix://%s", *vm.ExtId)))
311+
})
312+
313+
It("should fallback to ExtId when providerID value is empty", func() { // nolint:typecheck
314+
vm := mockEnvironment.GetVM(ctx, mock.MockVMNamePoweredOn)
315+
Expect(vm).ToNot(BeNil())
316+
// Add customAttributes with empty providerID value
317+
vm.CustomAttributes = []string{"providerID:", "otherKey:value"}
318+
providerID, err := m.generateProviderIDFromVM(ctx, vm)
319+
Expect(err).ToNot(HaveOccurred())
320+
Expect(providerID).To(Equal(fmt.Sprintf("nutanix://%s", *vm.ExtId)))
321+
})
322+
})
323+
271324
Context("Test getTopologyInfoFromVM", func() {
272325
It("should fail if vm is empty", func() { // nolint:typecheck
273326
err := m.getTopologyInfoFromVM(ctx, nClient, nil, &config.TopologyInfo{})

0 commit comments

Comments
 (0)