package iso import ( "bytes" "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "runtime" "strings" "testing" builderT "github.com/hashicorp/packer/packer-plugin-sdk/acctest" packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" "github.com/hashicorp/packer/packer-plugin-sdk/tmp" ) const vmxTestTemplate string = `{"builders":[{%s}],"provisioners":[{%s}]}` func createFloppyOutput(prefix string) (string, map[string]string, error) { f, err := tmp.File(prefix) if err != nil { return "", map[string]string{}, fmt.Errorf("unable to create temp file") } f.Close() output := f.Name() outputFile := strings.Replace(output, "\\", "\\\\", -1) vmxData := map[string]string{ "floppy0.present": "TRUE", "floppy0.fileType": "file", "floppy0.clientDevice": "FALSE", "floppy0.fileName": outputFile, "floppy0.startConnected": "TRUE", } return output, vmxData, nil } func readFloppyOutput(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", fmt.Errorf("Unable to open file %s", path) } defer f.Close() data, err := ioutil.ReadAll(f) if err != nil { return "", fmt.Errorf("Unable to read file: %s", err) } if len(data) == 0 { return "", nil } return string(data[:bytes.IndexByte(data, 0)]), nil } // RenderConfig helps create dynamic packer template configs for parsing by // builderT without having to write the config to a file. func RenderConfig(builderConfig map[string]interface{}, provisionerConfig map[string]string) string { // set up basic build template t := map[string][]map[string]interface{}{ "builders": { map[string]interface{}{ "type": "test", "iso_url": "https://archive.org/download/ut-ttylinux-i686-12.6/ut-ttylinux-i686-12.6.iso", "iso_checksum": "md5:43c1feeae55a44c6ef694b8eb18408a6", "ssh_username": "root", "ssh_password": "password", "ssh_wait_timeout": "45s", "boot_command": []string{"", "rootpassword", "udhcpc"}, "shutdown_command": "/sbin/shutdown -h; exit 0", "ssh_key_exchange_algorithms": []string{"diffie-hellman-group1-sha1"}, }, }, "provisioners": { map[string]interface{}{ "type": "shell", "inline": []string{"echo hola mundo"}, }, }, } // apply special builder overrides for k, v := range builderConfig { t["builders"][0][k] = v } // Apply special provisioner overrides for k, v := range provisionerConfig { t["provisioners"][0][k] = v } j, _ := json.Marshal(t) return string(j) } func TestStepCreateVmx_SerialFile(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the VMware binaries.") } tmpfile, err := tmp.File("SerialFileInput.") if err != nil { t.Fatalf("unable to create temp file") } serialConfig := map[string]interface{}{ "serial": fmt.Sprintf("file:%s", filepath.ToSlash(tmpfile.Name())), } configString := RenderConfig(serialConfig, map[string]string{}) builderT.Test(t, builderT.TestCase{ Builder: &Builder{}, Template: configString, Check: func(a []packersdk.Artifact) error { _, err := os.Stat(tmpfile.Name()) if err != nil { return fmt.Errorf("VMware builder did not create a file for serial port: %s", err) } return nil }, Teardown: func() error { f, _ := os.Stat(tmpfile.Name()) if f != nil { if err := os.Remove(tmpfile.Name()); err != nil { return fmt.Errorf("Unable to remove file %s: %s", tmpfile.Name(), err) } } return nil }, }) } func TestStepCreateVmx_SerialPort(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the VMware binaries.") } var defaultSerial string if runtime.GOOS == "windows" { defaultSerial = "COM1" } else { defaultSerial = "/dev/ttyS0" } config := map[string]interface{}{ "serial": fmt.Sprintf("device:%s", filepath.ToSlash(defaultSerial)), } provision := map[string]string{ "inline": "dmesg | egrep -o '^serial8250: ttyS1 at' > /dev/fd0", } // where to write output output, vmxData, err := createFloppyOutput("SerialPortOutput.") if err != nil { t.Fatalf("Error creating output: %s", err) } config["vmx_data"] = vmxData configString := RenderConfig(config, provision) builderT.Test(t, builderT.TestCase{ Builder: &Builder{}, Template: configString, Check: func(a []packersdk.Artifact) error { _, err := os.Stat(output) if err != nil { return fmt.Errorf("VMware builder did not create a file for serial port: %s", err) } // check the output data, err := readFloppyOutput(output) if err != nil { return fmt.Errorf("%s", err) } if data != "serial8250: ttyS1 at\n" { return fmt.Errorf("Serial port not detected : %v", data) } return nil }, Teardown: func() error { if _, err := os.Stat(output); err == nil { os.Remove(output) } return nil }, }) } func TestStepCreateVmx_ParallelPort(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the VMware binaries.") } var defaultParallel string if runtime.GOOS == "windows" { defaultParallel = "LPT1" } else { defaultParallel = "/dev/lp0" } config := map[string]interface{}{ "parallel": fmt.Sprintf("device:%s,uni", filepath.ToSlash(defaultParallel)), } provision := map[string]string{ "inline": "cat /proc/modules | egrep -o '^parport ' > /dev/fd0", } // where to write output output, vmxData, err := createFloppyOutput("ParallelPortOutput.") if err != nil { t.Fatalf("Error creating output: %s", err) } config["vmx_data"] = vmxData configString := RenderConfig(config, provision) builderT.Test(t, builderT.TestCase{ Builder: &Builder{}, Template: configString, Check: func(a []packersdk.Artifact) error { _, err := os.Stat(output) if err != nil { return fmt.Errorf("VMware builder did not create a file for serial port: %s", err) } // check the output data, err := readFloppyOutput(output) if err != nil { t.Errorf("%s", err) } if data != "parport \n" { t.Errorf("Parallel port not detected : %v", data) } return nil }, Teardown: func() error { if _, err := os.Stat(output); err == nil { os.Remove(output) } return nil }, }) } func TestStepCreateVmx_Usb(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the VMware binaries.") } config := map[string]interface{}{ "usb": "TRUE", } provision := map[string]string{ "inline": "dmesg | egrep -m1 -o 'USB hub found$' > /dev/fd0", } output, vmxData, err := createFloppyOutput("UsbOutput.") if err != nil { t.Fatalf("Error creating output: %s", err) } config["vmx_data"] = vmxData configString := RenderConfig(config, provision) builderT.Test(t, builderT.TestCase{ Builder: &Builder{}, Template: configString, Check: func(a []packersdk.Artifact) error { _, err := os.Stat(output) if err != nil { return fmt.Errorf("VMware builder did not create a file for serial port: %s", err) } // check the output data, err := readFloppyOutput(output) if err != nil { t.Errorf("%s", err) } if data != "USB hub found\n" { t.Errorf("USB support not detected : %v", data) } return nil }, Teardown: func() error { if _, err := os.Stat(output); err == nil { os.Remove(output) } return nil }, }) } func TestStepCreateVmx_Sound(t *testing.T) { if os.Getenv("PACKER_ACC") == "" { t.Skip("This test is only run with PACKER_ACC=1 due to the requirement of access to the VMware binaries.") } config := map[string]interface{}{ "sound": "TRUE", } provision := map[string]string{ "inline": "cat /proc/modules | egrep -o '^soundcore' > /dev/fd0", } // where to write output output, vmxData, err := createFloppyOutput("SoundOutput.") if err != nil { t.Fatalf("Error creating output: %s", err) } defer func() { if _, err := os.Stat(output); err == nil { os.Remove(output) } }() config["vmx_data"] = vmxData configString := RenderConfig(config, provision) builderT.Test(t, builderT.TestCase{ Builder: &Builder{}, Template: configString, Check: func(a []packersdk.Artifact) error { _, err := os.Stat(output) if err != nil { return fmt.Errorf("VMware builder did not create a file for serial port: %s", err) } // check the output data, err := readFloppyOutput(output) if err != nil { t.Errorf("%s", err) } if data != "soundcore\n" { t.Errorf("Soundcard not detected : %v", data) } return nil }, Teardown: func() error { if _, err := os.Stat(output); err == nil { os.Remove(output) } return nil }, }) }