github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/vm_unit_test.go (about)

     1  //go:build vm || unit || ALL
     2  
     3  /*
     4  * Copyright 2019 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     5   */
     6  
     7  package govcd
     8  
     9  import (
    10  	"reflect"
    11  	"testing"
    12  
    13  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    14  )
    15  
    16  func init() {
    17  	testingTags["unit"] = "vm_unit_test.go"
    18  }
    19  
    20  // Test_updateNicParameters_multinic is meant to check functionality of a complicated
    21  // code structure used in vm.ChangeNetworkConfig which is abstracted into
    22  // vm.updateNicParameters() method so that it does not contain any API calls, but
    23  // only adjust the object which is meant to be sent to API. Initially we hit a bug
    24  // which occurred only when API returned NICs in random order.
    25  func Test_VMupdateNicParameters_multiNIC(t *testing.T) {
    26  
    27  	// Mock VM struct
    28  	c := Client{}
    29  	vm := NewVM(&c)
    30  
    31  	// Sample config which is rendered by .tf schema parsed
    32  	tfCfg := []map[string]interface{}{
    33  		map[string]interface{}{
    34  			"network_name":       "multinic-net",
    35  			"ip_allocation_mode": "POOL",
    36  			"ip":                 "",
    37  			"is_primary":         false,
    38  		},
    39  		map[string]interface{}{
    40  			"network_name":       "multinic-net",
    41  			"ip_allocation_mode": "DHCP",
    42  			"ip":                 "",
    43  			"is_primary":         true,
    44  		},
    45  		map[string]interface{}{
    46  			"ip_allocation_mode": "NONE",
    47  		},
    48  		map[string]interface{}{
    49  			"network_name":       "multinic-net2",
    50  			"ip_allocation_mode": "MANUAL",
    51  			"ip":                 "1.1.1.1",
    52  			"is_primary":         false,
    53  		},
    54  	}
    55  
    56  	// A sample NetworkConnectionSection object simulating API returning ordered list
    57  	vcdConfig := types.NetworkConnectionSection{
    58  		PrimaryNetworkConnectionIndex: 0,
    59  		NetworkConnection: []*types.NetworkConnection{
    60  			&types.NetworkConnection{
    61  				Network:                 "multinic-net",
    62  				NetworkConnectionIndex:  0,
    63  				IPAddress:               "",
    64  				IsConnected:             true,
    65  				MACAddress:              "00:00:00:00:00:00",
    66  				IPAddressAllocationMode: "POOL",
    67  				NetworkAdapterType:      "VMXNET3",
    68  			},
    69  			&types.NetworkConnection{
    70  				Network:                 "multinic-net",
    71  				NetworkConnectionIndex:  1,
    72  				IPAddress:               "",
    73  				IsConnected:             true,
    74  				MACAddress:              "00:00:00:00:00:01",
    75  				IPAddressAllocationMode: "POOL",
    76  				NetworkAdapterType:      "VMXNET3",
    77  			},
    78  			&types.NetworkConnection{
    79  				Network:                 "multinic-net",
    80  				NetworkConnectionIndex:  2,
    81  				IPAddress:               "",
    82  				IsConnected:             true,
    83  				MACAddress:              "00:00:00:00:00:02",
    84  				IPAddressAllocationMode: "POOL",
    85  				NetworkAdapterType:      "VMXNET3",
    86  			},
    87  			&types.NetworkConnection{
    88  				Network:                 "multinic-net",
    89  				NetworkConnectionIndex:  3,
    90  				IPAddress:               "",
    91  				IsConnected:             true,
    92  				MACAddress:              "00:00:00:00:00:03",
    93  				IPAddressAllocationMode: "POOL",
    94  				NetworkAdapterType:      "VMXNET3",
    95  			},
    96  		},
    97  	}
    98  
    99  	// NIC configuration when API returns an ordered list
   100  	vcdCfg := &vcdConfig
   101  	err := vm.updateNicParameters(tfCfg, vcdCfg)
   102  	if err != nil {
   103  		t.Error(err)
   104  	}
   105  
   106  	// Test NIC updates when API returns an unordered list
   107  	// Swap two &types.NetworkConnection so that it is not ordered correctly
   108  	vcdConfig2 := vcdConfig
   109  	vcdConfig2.NetworkConnection[2], vcdConfig2.NetworkConnection[0] = vcdConfig2.NetworkConnection[0], vcdConfig2.NetworkConnection[2]
   110  	vcdCfg2 := &vcdConfig2
   111  	err = vm.updateNicParameters(tfCfg, vcdCfg2)
   112  	if err != nil {
   113  		t.Error(err)
   114  	}
   115  
   116  	var tableTests = []struct {
   117  		title     string
   118  		tfConfig  []map[string]interface{}
   119  		vcdConfig *types.NetworkConnectionSection
   120  	}{
   121  		{"Ordered NICs list", tfCfg, vcdCfg},
   122  		{"Unordered NIC list", tfCfg, vcdCfg2},
   123  	}
   124  
   125  	for _, tableTest := range tableTests {
   126  		t.Run(tableTest.title, func(t *testing.T) {
   127  			// Check that primary interface is reset to 1 as hardcoded in tfCfg "is_primary" parameter
   128  			if vcdCfg.PrimaryNetworkConnectionIndex != 1 {
   129  				t.Errorf("PrimaryNetworkConnectionIndex expected: 1, got: %d", vcdCfg.PrimaryNetworkConnectionIndex)
   130  			}
   131  
   132  			for loopIndex := range tableTest.vcdConfig.NetworkConnection {
   133  				vcdNic := tableTest.vcdConfig.NetworkConnection[loopIndex]
   134  				vcdNicSlot := vcdNic.NetworkConnectionIndex
   135  				tfNic := tableTest.tfConfig[vcdNicSlot]
   136  
   137  				if vcdNic.IPAddressAllocationMode != tfNic["ip_allocation_mode"].(string) {
   138  					t.Errorf("IPAddressAllocationMode expected: %s, got: %s", tfNic["ip_allocation_mode"].(string), vcdNic.IPAddressAllocationMode)
   139  				}
   140  
   141  				if vcdNic.IPAddressAllocationMode != tfNic["ip_allocation_mode"].(string) {
   142  					t.Errorf("IPAddressAllocationMode expected: %s, got: %s", tfNic["ip_allocation_mode"].(string), vcdNic.IPAddressAllocationMode)
   143  				}
   144  
   145  				if vcdNic.IsConnected != true {
   146  					t.Errorf("IsConnected expected: true, got: %t", vcdNic.IsConnected)
   147  				}
   148  
   149  				if vcdNic.NeedsCustomization != true {
   150  					t.Errorf("NeedsCustomization expected: true, got: %t", vcdNic.NeedsCustomization)
   151  				}
   152  
   153  				if vcdNic.IPAddressAllocationMode != types.IPAllocationModeNone {
   154  					if vcdNic.Network != tfNic["network_name"].(string) {
   155  						t.Errorf("Network expected: %s, got: %s", tfNic["network_name"].(string), vcdNic.Network)
   156  					}
   157  				} else {
   158  					if vcdNic.Network != "none" {
   159  						t.Errorf("Network expected: none, got: %s", vcdNic.Network)
   160  					}
   161  				}
   162  			}
   163  		})
   164  	}
   165  }
   166  
   167  // // Test_updateNicParameters_singleNIC is meant to check functionality when single NIC
   168  // // is being configured and meant to check functionality so that the function is able
   169  // // to cover legacy scenarios when Terraform provider was able to create single IP only.
   170  // // TODO v3.0 this test should become irrelevant once `ip` and `network_name` parameters are removed.
   171  func Test_VMupdateNicParameters_singleNIC(t *testing.T) {
   172  	// Mock VM struct
   173  	c := Client{}
   174  	vm := NewVM(&c)
   175  
   176  	tfCfgDHCP := []map[string]interface{}{
   177  		map[string]interface{}{
   178  			"network_name": "multinic-net",
   179  			"ip":           "dhcp",
   180  		},
   181  	}
   182  
   183  	tfCfgAllocated := []map[string]interface{}{
   184  		map[string]interface{}{
   185  			"network_name": "multinic-net",
   186  			"ip":           "allocated",
   187  		},
   188  	}
   189  
   190  	tfCfgNone := []map[string]interface{}{
   191  		map[string]interface{}{
   192  			"network_name": "multinic-net",
   193  			"ip":           "none",
   194  		},
   195  	}
   196  
   197  	tfCfgManual := []map[string]interface{}{
   198  		map[string]interface{}{
   199  			"network_name": "multinic-net",
   200  			"ip":           "1.1.1.1",
   201  		},
   202  	}
   203  
   204  	tfCfgInvalidIp := []map[string]interface{}{
   205  		map[string]interface{}{
   206  			"network_name": "multinic-net",
   207  			"ip":           "invalidIp",
   208  		},
   209  	}
   210  
   211  	tfCfgNoNetworkName := []map[string]interface{}{
   212  		map[string]interface{}{
   213  			"ip": "invalidIp",
   214  		},
   215  	}
   216  
   217  	vcdConfig := types.NetworkConnectionSection{
   218  		PrimaryNetworkConnectionIndex: 1,
   219  		NetworkConnection: []*types.NetworkConnection{
   220  			&types.NetworkConnection{
   221  				Network:                 "singlenic-net",
   222  				NetworkConnectionIndex:  0,
   223  				IPAddress:               "",
   224  				IsConnected:             true,
   225  				MACAddress:              "00:00:00:00:00:00",
   226  				IPAddressAllocationMode: "POOL",
   227  				NetworkAdapterType:      "VMXNET3",
   228  			},
   229  		},
   230  	}
   231  
   232  	var tableTests = []struct {
   233  		title                           string
   234  		tfConfig                        []map[string]interface{}
   235  		expectedIPAddressAllocationMode string
   236  		expectedIPAddress               string
   237  		mustNotError                    bool
   238  	}{
   239  		{"IPAllocationModeDHCP", tfCfgDHCP, types.IPAllocationModeDHCP, "Any", true},
   240  		{"IPAllocationModePool", tfCfgAllocated, types.IPAllocationModePool, "Any", true},
   241  		{"IPAllocationModeNone", tfCfgNone, types.IPAllocationModeNone, "Any", true},
   242  		{"IPAllocationModeManual", tfCfgManual, types.IPAllocationModeManual, tfCfgManual[0]["ip"].(string), true},
   243  		{"IPAllocationModeDHCPInvalidIP", tfCfgInvalidIp, types.IPAllocationModeDHCP, "Any", true},
   244  		{"ErrNoNetworkName", tfCfgNoNetworkName, types.IPAllocationModeDHCP, "Any", false},
   245  	}
   246  
   247  	for _, tableTest := range tableTests {
   248  
   249  		t.Run(tableTest.title, func(t *testing.T) {
   250  			vcdCfg := &vcdConfig
   251  			err := vm.updateNicParameters(tableTest.tfConfig, vcdCfg) // Execute parsing procedure
   252  
   253  			// if we got an error which was expected abandon the subtest
   254  			if err != nil && tableTest.mustNotError {
   255  				t.Errorf("unexpected error got: %s", err)
   256  				return
   257  			}
   258  
   259  			if vcdCfg.PrimaryNetworkConnectionIndex != 0 {
   260  				t.Errorf("PrimaryNetworkConnectionIndex expected: 0, got: %d", vcdCfg.PrimaryNetworkConnectionIndex)
   261  			}
   262  
   263  			if vcdCfg.NetworkConnection[0].IPAddressAllocationMode != tableTest.expectedIPAddressAllocationMode {
   264  				t.Errorf("IPAddressAllocationMode expected: %s, got: %s", tableTest.expectedIPAddressAllocationMode, vcdCfg.NetworkConnection[0].IPAddressAllocationMode)
   265  			}
   266  
   267  			if vcdCfg.NetworkConnection[0].IPAddress != tableTest.expectedIPAddress {
   268  				t.Errorf("IPAddress expected: %s, got: %s", tableTest.expectedIPAddress, vcdCfg.NetworkConnection[0].IPAddress)
   269  			}
   270  		})
   271  
   272  	}
   273  }
   274  
   275  // TestProductSectionList_SortByPropertyKeyName validates that a
   276  // SortByPropertyKeyName() works on ProductSectionList and can handle empty properties as well as
   277  // sort correctly
   278  func TestProductSectionList_SortByPropertyKeyName(t *testing.T) {
   279  	sliceProductSection := &types.ProductSectionList{
   280  		ProductSection: &types.ProductSection{},
   281  	}
   282  
   283  	emptyProductSection := &types.ProductSectionList{
   284  		ProductSection: &types.ProductSection{
   285  			Info: "Custom properties",
   286  		},
   287  	}
   288  
   289  	// unordered list for test
   290  	sortOrder := &types.ProductSectionList{
   291  		ProductSection: &types.ProductSection{
   292  			Info: "Custom properties",
   293  			Property: []*types.Property{
   294  				&types.Property{
   295  					UserConfigurable: false,
   296  					Key:              "sys_owner",
   297  					Label:            "sys_owner_label",
   298  					Type:             "string",
   299  					DefaultValue:     "sys_owner_default",
   300  					Value:            &types.Value{Value: "test"},
   301  				},
   302  				&types.Property{
   303  					UserConfigurable: true,
   304  					Key:              "asset_tag",
   305  					Label:            "asset_tag_label",
   306  					Type:             "string",
   307  					DefaultValue:     "asset_tag_default",
   308  					Value:            &types.Value{Value: "xxxyyy"},
   309  				},
   310  				&types.Property{
   311  					UserConfigurable: true,
   312  					Key:              "guestinfo.config.bootstrap.ip",
   313  					Label:            "guestinfo.config.bootstrap.ip_label",
   314  					Type:             "string",
   315  					DefaultValue:     "default_ip",
   316  					Value:            &types.Value{Value: "192.168.12.180"},
   317  				},
   318  			},
   319  		},
   320  	}
   321  	// correct state after ordering
   322  	expectedSortedOrder := &types.ProductSectionList{
   323  		ProductSection: &types.ProductSection{
   324  			Info: "Custom properties",
   325  			Property: []*types.Property{
   326  				&types.Property{
   327  					UserConfigurable: true,
   328  					Key:              "asset_tag",
   329  					Label:            "asset_tag_label",
   330  					Type:             "string",
   331  					DefaultValue:     "asset_tag_default",
   332  					Value:            &types.Value{Value: "xxxyyy"},
   333  				},
   334  				&types.Property{
   335  					UserConfigurable: true,
   336  					Key:              "guestinfo.config.bootstrap.ip",
   337  					Label:            "guestinfo.config.bootstrap.ip_label",
   338  					Type:             "string",
   339  					DefaultValue:     "default_ip",
   340  					Value:            &types.Value{Value: "192.168.12.180"},
   341  				},
   342  				&types.Property{
   343  					UserConfigurable: false,
   344  					Key:              "sys_owner",
   345  					Label:            "sys_owner_label",
   346  					Type:             "string",
   347  					DefaultValue:     "sys_owner_default",
   348  					Value:            &types.Value{Value: "test"},
   349  				},
   350  			},
   351  		},
   352  	}
   353  
   354  	tests := []struct {
   355  		name          string
   356  		setValue      *types.ProductSectionList
   357  		expectedValue *types.ProductSectionList
   358  	}{
   359  		{name: "Empty", setValue: emptyProductSection, expectedValue: emptyProductSection},
   360  		{name: "Slice", setValue: sliceProductSection, expectedValue: sliceProductSection},
   361  		{name: "SortOrder", setValue: sortOrder, expectedValue: expectedSortedOrder},
   362  	}
   363  	for _, tt := range tests {
   364  		t.Run(tt.name, func(t *testing.T) {
   365  			p := tt.setValue
   366  			p.SortByPropertyKeyName()
   367  
   368  			if !reflect.DeepEqual(p, tt.expectedValue) {
   369  				t.Errorf("Objects were not deeply equal: \n%#+v\n, got:\n %#+v\n", tt.expectedValue, p)
   370  			}
   371  
   372  		})
   373  	}
   374  }