Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions pkg/cloudprovider/vsphere/nodemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ import (
"github.com/vmware/govmomi/vim25/types"
)

const (
// instanceType field key in the annotation YAML field of VirtualMachineConfigInfo(vim.vm.ConfigInfo) Data Object
instanceTypeFieldKey = "instanceType"
)

// Errors
var (
// ErrVCenterNotFound is returned when the configured vCenter cannot be
Expand Down Expand Up @@ -358,17 +363,7 @@ func (nm *NodeManager) DiscoverNode(nodeID string, searchBy cm.FindVM) error {
nodeID, vmDI.VM, vmDI.VcServer, vmDI.DataCenter.Name())
klog.V(2).Info("Hostname: ", oVM.Guest.HostName, " UUID: ", vmDI.UUID)

os := "unknown"
if g, ok := GuestOSLookup[oVM.Summary.Config.GuestId]; ok {
os = g
}

// store instance type in nodeinfo map
instanceType := fmt.Sprintf("vsphere-vm.cpu-%d.mem-%dgb.os-%s",
oVM.Summary.Config.NumCpu,
(oVM.Summary.Config.MemorySizeMB / 1024),
os,
)
instanceType := getInstanceType(oVM.Config.Annotation, oVM.Summary.Config)

nodeInfo := &NodeInfo{
tenantRef: tenantRef, dataCenter: vmDI.DataCenter, vm: vmDI.VM, vcServer: vmDI.VcServer,
Expand All @@ -379,6 +374,35 @@ func (nm *NodeManager) DiscoverNode(nodeID string, searchBy cm.FindVM) error {
return nil
}

func getInstanceType(annotation string, config types.VirtualMachineConfigSummary) string {
annotation = strings.TrimSpace(annotation)
if annotation == "" {
return getInstanceTypeFallback(config)
}

var yamlData map[string]any
Comment thread
k0da marked this conversation as resolved.
if err := yaml.Unmarshal([]byte(annotation), &yamlData); err == nil {
if instanceTypeFieldValue, ok := yamlData[instanceTypeFieldKey].(string); ok && instanceTypeFieldValue != "" {
return instanceTypeFieldValue
}
}
Comment thread
dihlorfos marked this conversation as resolved.
return getInstanceTypeFallback(config)
}

func getInstanceTypeFallback(config types.VirtualMachineConfigSummary) string {
os := "unknown"
Comment thread
k0da marked this conversation as resolved.
if g, ok := GuestOSLookup[config.GuestId]; ok {
os = g
}

// store instance type in nodeinfo map
return fmt.Sprintf("vsphere-vm.cpu-%d.mem-%dgb.os-%s",
config.NumCpu,
(config.MemorySizeMB / 1024),
os,
)
}

// discoverIPs returns a pair of *ipAddrNetworkNames. The first representing
// the internal network IP and the second being the external network IP.
//
Expand Down
202 changes: 202 additions & 0 deletions pkg/cloudprovider/vsphere/nodemanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2254,3 +2254,205 @@ network.encoding: %s
network: %s`,
encoding, encodedNetconfig)
}

func TestGetInstanceType(t *testing.T) {
tests := []struct {
name string
annotation string
config vimtypes.VirtualMachineConfigSummary
expected string
}{
{
name: "camel-case YAML field",
annotation: "instanceType: c-2x",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 4096,
},
expected: "c-2x",
},
{
name: "camel-case multiline YAML",
annotation: `owner: platform-team
instanceType: c-4x
environment: production`,
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 4,
MemorySizeMB: 8192,
},
expected: "c-4x",
},
{
name: "camel-case JSON is valid YAML",
annotation: `{"instanceType":"m-8x"}`,
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 8,
MemorySizeMB: 16384,
},
expected: "m-8x",
},
{
name: "camel-case human-readable key-value format",
annotation: `owner: platform-team
instanceType: r-16x`,
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 16,
MemorySizeMB: 32768,
},
expected: "r-16x",
},
{
name: "snake-case YAML field is ignored",
annotation: "instance_type: c-2x",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 4096,
},
expected: "vsphere-vm.cpu-2.mem-4gb.os-unknown",
},
{
name: "snake-case JSON field is ignored",
annotation: `{"instance_type":"c-4x"}`,
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 4,
MemorySizeMB: 8192,
},
expected: "vsphere-vm.cpu-4.mem-8gb.os-unknown",
},
{
name: "snake-case multiline field is ignored",
annotation: `owner: platform-team
instance_type: c-8x`,
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 8,
MemorySizeMB: 16384,
},
expected: "vsphere-vm.cpu-8.mem-16gb.os-unknown",
},
{
name: "missing instanceType uses fallback",
annotation: "owner: platform-team",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 4,
MemorySizeMB: 8192,
},
expected: "vsphere-vm.cpu-4.mem-8gb.os-unknown",
},
{
name: "empty instanceType uses fallback",
annotation: `instanceType: ""`,
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 8,
MemorySizeMB: 32768,
},
expected: "vsphere-vm.cpu-8.mem-32gb.os-unknown",
},
{
name: "non-string instanceType uses fallback",
annotation: "instanceType: 123",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 2048,
},
expected: "vsphere-vm.cpu-2.mem-2gb.os-unknown",
},
{
name: "boolean instanceType uses fallback",
annotation: "instanceType: true",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 4096,
},
expected: "vsphere-vm.cpu-2.mem-4gb.os-unknown",
},
{
name: "malformed YAML uses fallback",
annotation: "instanceType: [invalid",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 4,
MemorySizeMB: 16384,
},
expected: "vsphere-vm.cpu-4.mem-16gb.os-unknown",
},
{
name: "unknown guest OS uses unknown",
annotation: "owner: platform-team",
config: vimtypes.VirtualMachineConfigSummary{
GuestId: "unsupportedGuest",
NumCpu: 4,
MemorySizeMB: 8192,
},
expected: "vsphere-vm.cpu-4.mem-8gb.os-unknown",
},
{
name: "memory is converted from MB to whole GB",
annotation: "owner: platform-team",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 2560,
},
expected: "vsphere-vm.cpu-2.mem-2gb.os-unknown",
},

// This documents the current implementation. It returns before
// constructing the default instance type.
{
name: "empty annotation returns empty string",
annotation: "",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 4096,
},
expected: "vsphere-vm.cpu-2.mem-4gb.os-unknown",
},
{
name: "whitespace-only annotation returns empty string",
annotation: " \n\t ",
config: vimtypes.VirtualMachineConfigSummary{
NumCpu: 2,
MemorySizeMB: 4096,
},
expected: "vsphere-vm.cpu-2.mem-4gb.os-unknown",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := getInstanceType(test.annotation, test.config)
if actual != test.expected {
t.Errorf(
"getInstanceType(%q, %+v) returned %q, expected %q",
test.annotation,
test.config,
actual,
test.expected,
)
}
})
}
}

func TestGetInstanceTypeUsesGuestOSLookup(t *testing.T) {
const guestID = "sles12_64Guest"

expectedOS, ok := GuestOSLookup[guestID]
if !ok {
t.Fatalf("GuestOSLookup does not contain %q", guestID)
}

config := vimtypes.VirtualMachineConfigSummary{
GuestId: guestID,
NumCpu: 4,
MemorySizeMB: 8192,
}

actual := getInstanceType("owner: platform-team", config)
expected := fmt.Sprintf(
"vsphere-vm.cpu-4.mem-8gb.os-%s",
expectedOS,
)

if actual != expected {
t.Errorf("getInstanceType() returned %q, expected %q", actual, expected)
}
}