diff --git a/.gitignore b/.gitignore index 4346282c1..6673d67d4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,14 @@ Dockerfile.media *.tag *.iso *.vhd +*.vmdk +*.vmdk.lck *.tar *.gz *.vhdx *.efi *.qcow2 -*-kernel$ +*-kernel *-cmdline +*-state artifacts/* diff --git a/Makefile b/Makefile index 85232dfec..f57e08deb 100644 --- a/Makefile +++ b/Makefile @@ -74,4 +74,4 @@ ci-pr: .PHONY: clean clean: - rm -rf bin *.log *-kernel *-cmdline *.img *.iso *.gz *.qcow2 *.vhd *.vmx *.vmdk *.tar + rm -rf bin *.log *-kernel *-cmdline *-state *.img *.iso *.gz *.qcow2 *.vhd *.vmx *.vmdk *.tar diff --git a/src/cmd/linuxkit/run_gcp.go b/src/cmd/linuxkit/run_gcp.go index fc8506035..d6f63d5b3 100644 --- a/src/cmd/linuxkit/run_gcp.go +++ b/src/cmd/linuxkit/run_gcp.go @@ -27,31 +27,31 @@ const ( // Process the run arguments and execute run func runGcp(args []string) { - gcpCmd := flag.NewFlagSet("gcp", flag.ExitOnError) + flags := flag.NewFlagSet("gcp", flag.ExitOnError) invoked := filepath.Base(os.Args[0]) - gcpCmd.Usage = func() { + flags.Usage = func() { fmt.Printf("USAGE: %s run gcp [options] [name]\n\n", invoked) fmt.Printf("'name' specifies either the name of an already uploaded\n") fmt.Printf("GCP image or the full path to a image file which will be\n") fmt.Printf("uploaded before it is run.\n\n") fmt.Printf("Options:\n\n") - gcpCmd.PrintDefaults() + flags.PrintDefaults() } - zoneFlag := gcpCmd.String("zone", defaultZone, "GCP Zone") - machineFlag := gcpCmd.String("machine", defaultMachine, "GCP Machine Type") - keysFlag := gcpCmd.String("keys", "", "Path to Service Account JSON key file") - projectFlag := gcpCmd.String("project", "", "GCP Project Name") - diskSizeFlag := gcpCmd.Int("disk-size", 0, "Size of system disk in GB") - skipCleanup := gcpCmd.Bool("skip-cleanup", false, "Don't remove images or VMs") + zoneFlag := flags.String("zone", defaultZone, "GCP Zone") + machineFlag := flags.String("machine", defaultMachine, "GCP Machine Type") + keysFlag := flags.String("keys", "", "Path to Service Account JSON key file") + projectFlag := flags.String("project", "", "GCP Project Name") + diskSizeFlag := flags.Int("disk-size", 0, "Size of system disk in GB") + skipCleanup := flags.Bool("skip-cleanup", false, "Don't remove images or VMs") - if err := gcpCmd.Parse(args); err != nil { + if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") } - remArgs := gcpCmd.Args() + remArgs := flags.Args() if len(remArgs) == 0 { fmt.Printf("Please specify the name of the image to boot\n") - gcpCmd.Usage() + flags.Usage() os.Exit(1) } name := remArgs[0] diff --git a/src/cmd/linuxkit/run_hyperkit.go b/src/cmd/linuxkit/run_hyperkit.go index 17ae46d90..7dcd177b0 100644 --- a/src/cmd/linuxkit/run_hyperkit.go +++ b/src/cmd/linuxkit/run_hyperkit.go @@ -15,34 +15,42 @@ import ( // Process the run arguments and execute run func runHyperKit(args []string) { - hyperkitCmd := flag.NewFlagSet("hyperkit", flag.ExitOnError) + flags := flag.NewFlagSet("hyperkit", flag.ExitOnError) invoked := filepath.Base(os.Args[0]) - hyperkitCmd.Usage = func() { + flags.Usage = func() { fmt.Printf("USAGE: %s run hyperkit [options] prefix\n\n", invoked) fmt.Printf("'prefix' specifies the path to the VM image.\n") fmt.Printf("\n") fmt.Printf("Options:\n") - hyperkitCmd.PrintDefaults() + flags.PrintDefaults() } - hyperkitPath := hyperkitCmd.String("hyperkit", "", "Path to hyperkit binary (if not in default location)") - cpus := hyperkitCmd.Int("cpus", 1, "Number of CPUs") - mem := hyperkitCmd.Int("mem", 1024, "Amount of memory in MB") - diskSz := hyperkitCmd.Int("disk-size", 0, "Size of Disk in MB") - disk := hyperkitCmd.String("disk", "", "Path to disk image to used") - data := hyperkitCmd.String("data", "", "Metadata to pass to VM (either a path to a file or a string)") - ipStr := hyperkitCmd.String("ip", "", "IP address for the VM") + hyperkitPath := flags.String("hyperkit", "", "Path to hyperkit binary (if not in default location)") + cpus := flags.Int("cpus", 1, "Number of CPUs") + mem := flags.Int("mem", 1024, "Amount of memory in MB") + diskSz := flags.Int("disk-size", 0, "Size of Disk in MB") + disk := flags.String("disk", "", "Path to disk image to used") + data := flags.String("data", "", "Metadata to pass to VM (either a path to a file or a string)") + ipStr := flags.String("ip", "", "IP address for the VM") + state := flags.String("state", "", "Path to directory to keep VM state in") - if err := hyperkitCmd.Parse(args); err != nil { + if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") } - remArgs := hyperkitCmd.Args() + remArgs := flags.Args() if len(remArgs) == 0 { fmt.Println("Please specify the prefix to the image to boot\n") - hyperkitCmd.Usage() + flags.Usage() os.Exit(1) } prefix := remArgs[0] + if *state == "" { + *state = prefix + "-state" + } + if err := os.MkdirAll(*state, 0755); err != nil { + log.Fatalf("Could not create state directory: %v", err) + } + isoPath := "" if *data != "" { var d []byte @@ -54,9 +62,9 @@ func runHyperKit(args []string) { log.Fatalf("Cannot read user data: %v", err) } } - isoPath = prefix + "-data.iso" + isoPath = filepath.Join(*state, "data.iso") if err := WriteMetadataISO(isoPath, d); err != nil { - log.Fatalf("Cannot write user data ISO: %s", err) + log.Fatalf("Cannot write user data ISO: %v", err) } } @@ -84,10 +92,10 @@ func runHyperKit(args []string) { } if *diskSz != 0 && *disk == "" { - *disk = prefix + "-disk.img" + *disk = filepath.Join(*state, "disk.img") } - h, err := hyperkit.New(*hyperkitPath, "auto", "") + h, err := hyperkit.New(*hyperkitPath, "auto", *state) if err != nil { log.Fatalln("Error creating hyperkit: ", err) } @@ -98,6 +106,7 @@ func runHyperKit(args []string) { h.UUID = vmUUID h.DiskImage = *disk h.ISOImage = isoPath + h.VSock = true h.CPUs = *cpus h.Memory = *mem h.DiskSize = *diskSz diff --git a/src/cmd/linuxkit/run_packet.go b/src/cmd/linuxkit/run_packet.go index 129b0b998..4e1b7812a 100644 --- a/src/cmd/linuxkit/run_packet.go +++ b/src/cmd/linuxkit/run_packet.go @@ -40,25 +40,25 @@ func ValidateHTTPURL(url string) { // Process the run arguments and execute run func runPacket(args []string) { - packetCmd := flag.NewFlagSet("packet", flag.ExitOnError) + flags := flag.NewFlagSet("packet", flag.ExitOnError) invoked := filepath.Base(os.Args[0]) - packetCmd.Usage = func() { + flags.Usage = func() { fmt.Printf("USAGE: %s run packet [options] [name]\n\n", invoked) fmt.Printf("Options:\n\n") - packetCmd.PrintDefaults() + flags.PrintDefaults() } - baseURLFlag := packetCmd.String("base-url", "", "Base URL that the kernel and initrd are served from.") - zoneFlag := packetCmd.String("zone", packetDefaultZone, "Packet Zone") - machineFlag := packetCmd.String("machine", packetDefaultMachine, "Packet Machine Type") - apiKeyFlag := packetCmd.String("api-key", "", "Packet API key") - projectFlag := packetCmd.String("project-id", "", "Packet Project ID") - hostNameFlag := packetCmd.String("hostname", packetDefaultHostname, "Hostname of new instance") - nameFlag := packetCmd.String("img-name", "", "Overrides the prefix used to identify the files. Defaults to [name]") - if err := packetCmd.Parse(args); err != nil { + baseURLFlag := flags.String("base-url", "", "Base URL that the kernel and initrd are served from.") + zoneFlag := flags.String("zone", packetDefaultZone, "Packet Zone") + machineFlag := flags.String("machine", packetDefaultMachine, "Packet Machine Type") + apiKeyFlag := flags.String("api-key", "", "Packet API key") + projectFlag := flags.String("project-id", "", "Packet Project ID") + hostNameFlag := flags.String("hostname", packetDefaultHostname, "Hostname of new instance") + nameFlag := flags.String("img-name", "", "Overrides the prefix used to identify the files. Defaults to [name]") + if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") } - remArgs := packetCmd.Args() + remArgs := flags.Args() prefix := "packet" if len(remArgs) > 0 { prefix = remArgs[0] diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index e5ed029df..3eedf6171 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -38,63 +38,63 @@ type QemuConfig struct { func runQemu(args []string) { invoked := filepath.Base(os.Args[0]) - qemuFlags := flag.NewFlagSet("qemu", flag.ExitOnError) - qemuFlags.Usage = func() { + flags := flag.NewFlagSet("qemu", flag.ExitOnError) + flags.Usage = func() { fmt.Printf("USAGE: %s run qemu [options] prefix\n\n", invoked) fmt.Printf("'prefix' specifies the path to the VM image.\n") fmt.Printf("\n") fmt.Printf("Options:\n") - qemuFlags.PrintDefaults() + flags.PrintDefaults() } // Determine Flags - qemuGUI := qemuFlags.Bool("gui", false, "Set qemu to use video output instead of stdio") - qemuUEFI := qemuFlags.Bool("uefi", false, "Set UEFI boot from 'prefix'-efi.iso") - qemuIso := qemuFlags.Bool("iso", false, "Set Legacy BIOS boot from 'prefix'.iso") - qemuKernel := qemuFlags.Bool("kernel", true, "Set boot using 'prefix'-kernel/-initrd/-cmdline") + enableGUI := flags.Bool("gui", false, "Set qemu to use video output instead of stdio") + uefiBoot := flags.Bool("uefi", false, "Set UEFI boot from 'prefix'-efi.iso") + isoBoot := flags.Bool("iso", false, "Set Legacy BIOS boot from 'prefix'.iso") + kernelBoot := flags.Bool("kernel", true, "Set boot using 'prefix'-kernel/-initrd/-cmdline") // Paths and settings for Disks and UEFI firware - qemuDiskPath := qemuFlags.String("disk", "", "Path to disk image to use") - qemuDiskSize := qemuFlags.String("disk-size", "", "Size of disk to create, only created if it doesn't exist") - qemuFWPath := qemuFlags.String("fw", "/usr/share/ovmf/bios.bin", "Path to OVMF firmware for UEFI boot") + disk := flags.String("disk", "", "Path to disk image to use") + diskSz := flags.String("disk-size", "", "Size of disk to create, only created if it doesn't exist") + fw := flags.String("fw", "/usr/share/ovmf/bios.bin", "Path to OVMF firmware for UEFI boot") // VM configuration - qemuArch := qemuFlags.String("arch", "x86_64", "Type of architecture to use, e.g. x86_64, aarch64") - qemuCPUs := qemuFlags.String("cpus", "1", "Number of CPUs") - qemuMem := qemuFlags.String("mem", "1024", "Amount of memory in MB") + arch := flags.String("arch", "x86_64", "Type of architecture to use, e.g. x86_64, aarch64") + cpus := flags.String("cpus", "1", "Number of CPUs") + mem := flags.String("mem", "1024", "Amount of memory in MB") publishFlags := multipleFlag{} - qemuFlags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])") + flags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])") - if err := qemuFlags.Parse(args); err != nil { + if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") } - remArgs := qemuFlags.Args() + remArgs := flags.Args() if len(remArgs) == 0 { fmt.Println("Please specify the prefix to the image to boot") - qemuFlags.Usage() + flags.Usage() os.Exit(1) } prefix := remArgs[0] // Print warning if conflicting UEFI and ISO flags are set - if *qemuUEFI && *qemuIso { + if *uefiBoot && *isoBoot { log.Warnf("Both -iso and -uefi have been used") } config := QemuConfig{ Prefix: prefix, - ISO: *qemuIso, - UEFI: *qemuUEFI, - Kernel: *qemuKernel, - GUI: *qemuGUI, - DiskPath: *qemuDiskPath, - DiskSize: *qemuDiskSize, - FWPath: *qemuFWPath, - Arch: *qemuArch, - CPUs: *qemuCPUs, - Memory: *qemuMem, + ISO: *isoBoot, + UEFI: *uefiBoot, + Kernel: *kernelBoot, + GUI: *enableGUI, + DiskPath: *disk, + DiskSize: *diskSz, + FWPath: *fw, + Arch: *arch, + CPUs: *cpus, + Memory: *mem, PublishedPorts: publishFlags, } diff --git a/src/cmd/linuxkit/run_vmware.go b/src/cmd/linuxkit/run_vmware.go index 73187dd77..c4a7f726f 100644 --- a/src/cmd/linuxkit/run_vmware.go +++ b/src/cmd/linuxkit/run_vmware.go @@ -66,44 +66,52 @@ guestOS = "other3xlinux-64" func runVMware(args []string) { invoked := filepath.Base(os.Args[0]) - vmwareArgs := flag.NewFlagSet("vmware", flag.ExitOnError) - vmwareArgs.Usage = func() { + flags := flag.NewFlagSet("vmware", flag.ExitOnError) + flags.Usage = func() { fmt.Printf("USAGE: %s run vmware [options] prefix\n\n", invoked) fmt.Printf("'prefix' specifies the path to the VM image.\n") fmt.Printf("\n") fmt.Printf("Options:\n") - vmwareArgs.PrintDefaults() + flags.PrintDefaults() } - vmwareCPUs := vmwareArgs.Int("cpus", 1, "Number of CPUs") - vmwareMem := vmwareArgs.Int("mem", 1024, "Amount of memory in MB") - vmwareDisk := vmwareArgs.String("disk", "", "Path to disk image to use") - vmwareDiskSize := vmwareArgs.String("disk-size", "", "Size of the disk to create, only created if it doesn't exist") + cpus := flags.Int("cpus", 1, "Number of CPUs") + mem := flags.Int("mem", 1024, "Amount of memory in MB") + disk := flags.String("disk", "", "Path to disk image to use") + diskSz := flags.String("disk-size", "", "Size of the disk to create, only created if it doesn't exist") + state := flags.String("state", "", "Path to directory to keep VM state in") - if err := vmwareArgs.Parse(args); err != nil { + if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") } - remArgs := vmwareArgs.Args() + remArgs := flags.Args() if len(remArgs) == 0 { fmt.Println("Please specify the prefix to the image to boot") - vmwareArgs.Usage() + flags.Usage() os.Exit(1) } prefix := remArgs[0] + if *state == "" { + *state = prefix + "-state" + } + if err := os.MkdirAll(*state, 0755); err != nil { + log.Fatalf("Could not create state directory: %v", err) + } + var vmrunPath, vmDiskManagerPath string var vmrunArgs []string if runtime.GOOS == "windows" { vmrunPath = "C:\\Program\\ files\\VMware Workstation\\vmrun.exe" vmDiskManagerPath = "C:\\Program\\ files\\VMware Workstation\\vmware-vdiskmanager.exe" - vmrunArgs = []string{"-T", "ws", "start", prefix + ".vmx"} + vmrunArgs = []string{"-T", "ws", "start"} } if runtime.GOOS == "darwin" { vmrunPath = "/Applications/VMware Fusion.app/Contents/Library/vmrun" vmDiskManagerPath = "/Applications/VMware Fusion.app/Contents/Library/vmware-vdiskmanager" - vmrunArgs = []string{"-T", "fusion", "start", prefix + ".vmx"} + vmrunArgs = []string{"-T", "fusion", "start"} } if runtime.GOOS == "linux" { @@ -115,7 +123,7 @@ func runVMware(args []string) { log.Fatalf("Unable to find %s within the $PATH", vmrunPath) } vmrunPath = fullVMrunPath - vmrunArgs = []string{"-T", "ws", "start", prefix + ".vmx"} + vmrunArgs = []string{"-T", "ws", "start"} } // Check vmrunPath exists before attempting to execute @@ -123,48 +131,50 @@ func runVMware(args []string) { log.Fatalf("ERROR VMware executables can not be found, ensure software is installed") } - if *vmwareDisk != "" { + if *disk == "" && *diskSz != "" { + *disk = filepath.Join(*state, "disk.vmdk") + } + if *disk != "" { // Check vmDiskManagerPath exist before attempting to execute if _, err := os.Stat(vmDiskManagerPath); os.IsNotExist(err) { log.Fatalf("ERROR VMware Disk Manager executables can not be found, ensure software is installed") } // If disk doesn't exist then create one, error if disk is unreadable - if _, err := os.Stat(*vmwareDisk); err != nil { + if _, err := os.Stat(*disk); err != nil { if os.IsPermission(err) { - log.Fatalf("Unable to read file [%s], please check permissions", *vmwareDisk) + log.Fatalf("Unable to read file [%s], please check permissions", *disk) } if os.IsNotExist(err) { - log.Infof("Creating new VMware disk [%s]", *vmwareDisk) - vmDiskCmd := exec.Command(vmDiskManagerPath, "-c", "-s", *vmwareDiskSize, "-a", "lsilogic", "-t", "0", *vmwareDisk) + log.Infof("Creating new VMware disk [%s]", *disk) + vmDiskCmd := exec.Command(vmDiskManagerPath, "-c", "-s", *diskSz, "-a", "lsilogic", "-t", "0", *disk) if err = vmDiskCmd.Run(); err != nil { - log.Fatalf("Error creating disk [%s]: %s", *vmwareDisk, err.Error()) + log.Fatalf("Error creating disk [%s]: %v", *disk, err) } } } else { - log.Infof("Using existing disk [%s]", *vmwareDisk) + log.Infof("Using existing disk [%s]", *disk) } } // Build the contents of the VMWare .vmx file - vmx := buildVMX(*vmwareCPUs, *vmwareMem, *vmwareDisk, prefix) - + vmx := buildVMX(*cpus, *mem, *disk, prefix) if vmx == "" { log.Fatalf("VMware .vmx file could not be generated, please confirm inputs") } // Create the .vmx file - err := ioutil.WriteFile(prefix+".vmx", []byte(vmx), 0644) - + vmxPath := filepath.Join(*state, "linuxkit.vmx") + err := ioutil.WriteFile(vmxPath, []byte(vmx), 0644) if err != nil { - log.Fatalf("Error writing .vmx file") + log.Fatalf("Error writing .vmx file: %v", err) } + vmrunArgs = append(vmrunArgs, vmxPath) cmd := exec.Command(vmrunPath, vmrunArgs...) out, err := cmd.Output() - if err != nil { - log.Fatalf("Error starting vmrun") + log.Fatalf("Error starting vmrun: %v", err) } // check there is output to push to logging @@ -185,7 +195,10 @@ func buildVMX(cpus int, mem int, persistentDisk string, prefix string) string { returnString += fmt.Sprintf(vmxCdrom, cdromPath) } - vmdkPath := prefix + ".vmdk" + vmdkPath, err := filepath.Abs(prefix + ".vmdk") + if err != nil { + log.Fatalf("Unable get absolute path for boot vmdk: %v", err) + } if _, err := os.Stat(vmdkPath); err != nil { if os.IsPermission(err) { log.Fatalf("Unable to read file [%s], please check permissions", vmdkPath) @@ -198,6 +211,10 @@ func buildVMX(cpus int, mem int, persistentDisk string, prefix string) string { } // Add persistentDisk to the vmx if it has been specified in the args. if persistentDisk != "" { + persistentDisk, err = filepath.Abs(persistentDisk) + if err != nil { + log.Fatalf("Unable get absolute path for persistent disk: %v", err) + } returnString += fmt.Sprintf(vmxDiskPersistent, persistentDisk) }