418 lines
10 KiB
Go
418 lines
10 KiB
Go
package iso
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io/ioutil"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
|
"github.com/hashicorp/packer/packer"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/multistep"
|
|
)
|
|
|
|
func TestCreateConfig_Prepare(t *testing.T) {
|
|
// Empty config - check defaults
|
|
config := &CreateConfig{
|
|
// Storage is required
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if errs := config.Prepare(); len(errs) != 0 {
|
|
t.Fatalf("Config preprare should not fail: %s", errs[0])
|
|
}
|
|
if config.GuestOSType != "otherGuest" {
|
|
t.Fatalf("GuestOSType should default to 'otherGuest'")
|
|
}
|
|
if len(config.StorageConfig.DiskControllerType) != 1 {
|
|
t.Fatalf("DiskControllerType should have at least one element as default")
|
|
}
|
|
|
|
// Data validation
|
|
tc := []struct {
|
|
name string
|
|
config *CreateConfig
|
|
fail bool
|
|
expectedErrMsg string
|
|
}{
|
|
{
|
|
name: "Storage validate disk_size",
|
|
config: &CreateConfig{
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 0,
|
|
DiskThinProvisioned: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: true,
|
|
expectedErrMsg: "storage[0].'disk_size' is required",
|
|
},
|
|
{
|
|
name: "Storage validate disk_controller_index",
|
|
config: &CreateConfig{
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
DiskControllerIndex: 3,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: true,
|
|
expectedErrMsg: "storage[0].'disk_controller_index' references an unknown disk controller",
|
|
},
|
|
{
|
|
name: "USBController validate 'usb' and 'xhci' can be set together",
|
|
config: &CreateConfig{
|
|
USBController: []string{"usb", "xhci"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: false,
|
|
},
|
|
{
|
|
name: "USBController validate '1' and '0' can be set together",
|
|
config: &CreateConfig{
|
|
USBController: []string{"1", "0"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: false,
|
|
},
|
|
{
|
|
name: "USBController validate 'true' and 'false' can be set together",
|
|
config: &CreateConfig{
|
|
USBController: []string{"true", "false"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: false,
|
|
},
|
|
{
|
|
name: "USBController validate 'true' and 'usb' cannot be set together",
|
|
config: &CreateConfig{
|
|
USBController: []string{"true", "usb"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: true,
|
|
expectedErrMsg: "there can only be one usb controller and one xhci controller",
|
|
},
|
|
{
|
|
name: "USBController validate '1' and 'usb' cannot be set together",
|
|
config: &CreateConfig{
|
|
USBController: []string{"1", "usb"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: true,
|
|
expectedErrMsg: "there can only be one usb controller and one xhci controller",
|
|
},
|
|
{
|
|
name: "USBController validate 'xhci' cannot be set more that once",
|
|
config: &CreateConfig{
|
|
USBController: []string{"xhci", "xhci"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: true,
|
|
expectedErrMsg: "there can only be one usb controller and one xhci controller",
|
|
},
|
|
{
|
|
name: "USBController validate unknown value cannot be set",
|
|
config: &CreateConfig{
|
|
USBController: []string{"unknown"},
|
|
StorageConfig: common.StorageConfig{
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
fail: true,
|
|
expectedErrMsg: "usb_controller[0] references an unknown usb controller",
|
|
},
|
|
}
|
|
|
|
for _, c := range tc {
|
|
errs := c.config.Prepare()
|
|
if c.fail {
|
|
if len(errs) == 0 {
|
|
t.Fatalf("Config preprare should fail")
|
|
}
|
|
if errs[0].Error() != c.expectedErrMsg {
|
|
t.Fatalf("Expected error message: %s but was '%s'", c.expectedErrMsg, errs[0].Error())
|
|
}
|
|
} else {
|
|
if len(errs) != 0 {
|
|
t.Fatalf("Config preprare should not fail: %s", errs[0])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStepCreateVM_Run(t *testing.T) {
|
|
state := basicStateBag()
|
|
driverMock := driver.NewDriverMock()
|
|
state.Put("driver", driverMock)
|
|
step := basicStepCreateVM()
|
|
step.Force = true
|
|
vmPath := path.Join(step.Location.Folder, step.Location.VMName)
|
|
|
|
if action := step.Run(context.TODO(), state); action == multistep.ActionHalt {
|
|
t.Fatalf("Should not halt.")
|
|
}
|
|
|
|
// Pre clean VM
|
|
if !driverMock.PreCleanVMCalled {
|
|
t.Fatalf("driver.PreCleanVM should be called.")
|
|
}
|
|
if driverMock.PreCleanForce != step.Force {
|
|
t.Fatalf("Force PreCleanVM should be %t but was %t.", step.Force, driverMock.PreCleanForce)
|
|
}
|
|
if driverMock.PreCleanVMPath != vmPath {
|
|
t.Fatalf("VM path expected to be %s but was %s", vmPath, driverMock.PreCleanVMPath)
|
|
}
|
|
|
|
if !driverMock.CreateVMCalled {
|
|
t.Fatalf("driver.CreateVM should be called.")
|
|
}
|
|
if diff := cmp.Diff(driverMock.CreateConfig, driverCreateConfig(step.Config, step.Location)); diff != "" {
|
|
t.Fatalf("wrong driver.CreateConfig: %s", diff)
|
|
}
|
|
vm, ok := state.GetOk("vm")
|
|
if !ok {
|
|
t.Fatal("state must contain the VM")
|
|
}
|
|
if vm != driverMock.VM {
|
|
t.Fatalf("state doesn't contain the created VM.")
|
|
}
|
|
}
|
|
|
|
func TestStepCreateVM_RunHalt(t *testing.T) {
|
|
state := basicStateBag()
|
|
step := basicStepCreateVM()
|
|
|
|
// PreCleanVM fails
|
|
driverMock := driver.NewDriverMock()
|
|
driverMock.PreCleanShouldFail = true
|
|
state.Put("driver", driverMock)
|
|
if action := step.Run(context.TODO(), state); action != multistep.ActionHalt {
|
|
t.Fatalf("Step should halt.")
|
|
}
|
|
if !driverMock.PreCleanVMCalled {
|
|
t.Fatalf("driver.PreCleanVM should be called")
|
|
}
|
|
|
|
// CreateVM fails
|
|
driverMock = driver.NewDriverMock()
|
|
driverMock.CreateVMShouldFail = true
|
|
state.Put("driver", driverMock)
|
|
if action := step.Run(context.TODO(), state); action != multistep.ActionHalt {
|
|
t.Fatalf("Step should halt.")
|
|
}
|
|
if !driverMock.PreCleanVMCalled {
|
|
t.Fatalf("driver.PreCleanVM should be called")
|
|
}
|
|
if !driverMock.CreateVMCalled {
|
|
t.Fatalf("driver.PreCleanVM should be called")
|
|
}
|
|
if _, ok := state.GetOk("vm"); ok {
|
|
t.Fatal("state should not contain a VM")
|
|
}
|
|
}
|
|
|
|
func TestStepCreateVM_Cleanup(t *testing.T) {
|
|
state := basicStateBag()
|
|
step := basicStepCreateVM()
|
|
vm := new(driver.VirtualMachineMock)
|
|
state.Put("vm", vm)
|
|
|
|
// Clean up when state is cancelled
|
|
state.Put(multistep.StateCancelled, true)
|
|
step.Cleanup(state)
|
|
if !vm.DestroyCalled {
|
|
t.Fatalf("vm.Destroy should be called")
|
|
}
|
|
vm.DestroyCalled = false
|
|
state.Remove(multistep.StateCancelled)
|
|
|
|
// Clean up when state is halted
|
|
state.Put(multistep.StateHalted, true)
|
|
step.Cleanup(state)
|
|
if !vm.DestroyCalled {
|
|
t.Fatalf("vm.Destroy should be called")
|
|
}
|
|
vm.DestroyCalled = false
|
|
state.Remove(multistep.StateHalted)
|
|
|
|
// Clean up when state is destroy_vm is set
|
|
state.Put("destroy_vm", true)
|
|
step.Cleanup(state)
|
|
if !vm.DestroyCalled {
|
|
t.Fatalf("vm.Destroy should be called")
|
|
}
|
|
vm.DestroyCalled = false
|
|
state.Remove("destroy_vm")
|
|
|
|
// Don't clean up if state is not set with previous values
|
|
step.Cleanup(state)
|
|
if vm.DestroyCalled {
|
|
t.Fatalf("vm.Destroy should not be called")
|
|
}
|
|
|
|
// Destroy fail
|
|
errorBuffer := &strings.Builder{}
|
|
ui := &packer.BasicUi{
|
|
Reader: strings.NewReader(""),
|
|
Writer: ioutil.Discard,
|
|
ErrorWriter: errorBuffer,
|
|
}
|
|
state.Put("ui", ui)
|
|
state.Put(multistep.StateCancelled, true)
|
|
vm.DestroyError = errors.New("destroy failed")
|
|
|
|
step.Cleanup(state)
|
|
if !vm.DestroyCalled {
|
|
t.Fatalf("vm.Destroy should be called")
|
|
}
|
|
if !strings.Contains(errorBuffer.String(), vm.DestroyError.Error()) {
|
|
t.Fatalf("Destroy should fail with error message '%s' but failed with '%s'", vm.DestroyError.Error(), errorBuffer.String())
|
|
}
|
|
vm.DestroyCalled = false
|
|
state.Remove(multistep.StateCancelled)
|
|
|
|
// Should not destroy if VM is not set
|
|
state.Remove("vm")
|
|
state.Put(multistep.StateCancelled, true)
|
|
step.Cleanup(state)
|
|
if vm.DestroyCalled {
|
|
t.Fatalf("vm.Destroy should not be called")
|
|
}
|
|
}
|
|
|
|
func basicStepCreateVM() *StepCreateVM {
|
|
step := &StepCreateVM{
|
|
Config: createConfig(),
|
|
Location: basicLocationConfig(),
|
|
}
|
|
return step
|
|
}
|
|
|
|
func basicLocationConfig() *common.LocationConfig {
|
|
return &common.LocationConfig{
|
|
VMName: "test-vm",
|
|
Folder: "test-folder",
|
|
Cluster: "test-cluster",
|
|
Host: "test-host",
|
|
ResourcePool: "test-resource-pool",
|
|
Datastore: "test-datastore",
|
|
}
|
|
}
|
|
|
|
func createConfig() *CreateConfig {
|
|
return &CreateConfig{
|
|
Version: 1,
|
|
GuestOSType: "ubuntu64Guest",
|
|
StorageConfig: common.StorageConfig{
|
|
DiskControllerType: []string{"pvscsi"},
|
|
Storage: []common.DiskConfig{
|
|
{
|
|
DiskSize: 32768,
|
|
DiskThinProvisioned: true,
|
|
},
|
|
},
|
|
},
|
|
NICs: []NIC{
|
|
{
|
|
Network: "VM Network",
|
|
NetworkCard: "vmxnet3",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func driverCreateConfig(config *CreateConfig, location *common.LocationConfig) *driver.CreateConfig {
|
|
var networkCards []driver.NIC
|
|
for _, nic := range config.NICs {
|
|
networkCards = append(networkCards, driver.NIC{
|
|
Network: nic.Network,
|
|
NetworkCard: nic.NetworkCard,
|
|
MacAddress: nic.MacAddress,
|
|
Passthrough: nic.Passthrough,
|
|
})
|
|
}
|
|
|
|
var disks []driver.Disk
|
|
for _, disk := range config.StorageConfig.Storage {
|
|
disks = append(disks, driver.Disk{
|
|
DiskSize: disk.DiskSize,
|
|
DiskEagerlyScrub: disk.DiskEagerlyScrub,
|
|
DiskThinProvisioned: disk.DiskThinProvisioned,
|
|
ControllerIndex: disk.DiskControllerIndex,
|
|
})
|
|
}
|
|
|
|
return &driver.CreateConfig{
|
|
StorageConfig: driver.StorageConfig{
|
|
DiskControllerType: config.StorageConfig.DiskControllerType,
|
|
Storage: disks,
|
|
},
|
|
Annotation: config.Notes,
|
|
Name: location.VMName,
|
|
Folder: location.Folder,
|
|
Cluster: location.Cluster,
|
|
Host: location.Host,
|
|
ResourcePool: location.ResourcePool,
|
|
Datastore: location.Datastore,
|
|
GuestOS: config.GuestOSType,
|
|
NICs: networkCards,
|
|
USBController: config.USBController,
|
|
Version: config.Version,
|
|
}
|
|
}
|