From 576951e03a5438b1a87430d3ad9eb22512acc11d Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Wed, 8 Apr 2015 09:49:52 -0400 Subject: [PATCH 1/7] wip: b2d import Signed-off-by: Evan Hazlett --- drivers/virtualbox/disk.go | 46 +++++++++++++++++ drivers/virtualbox/disk_test.go | 36 ++++++++++++++ drivers/virtualbox/vbm.go | 1 + drivers/virtualbox/virtualbox.go | 84 ++++++++++++++++++++++++-------- 4 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 drivers/virtualbox/disk.go create mode 100644 drivers/virtualbox/disk_test.go diff --git a/drivers/virtualbox/disk.go b/drivers/virtualbox/disk.go new file mode 100644 index 0000000000..20d35b95b9 --- /dev/null +++ b/drivers/virtualbox/disk.go @@ -0,0 +1,46 @@ +package virtualbox + +import ( + "bufio" + "io" + "strings" +) + +type VirtualDisk struct { + UUID string + Path string +} + +func parseDiskInfo(r io.Reader) (*VirtualDisk, error) { + s := bufio.NewScanner(r) + disk := &VirtualDisk{} + for s.Scan() { + line := s.Text() + if line == "" { + continue + } + res := reEqualLine.FindStringSubmatch(line) + if res == nil { + continue + } + switch key, val := res[1], res[2]; key { + case "SATA-1-0": + disk.Path = val + case "SATA-ImageUUID-1-0": + disk.UUID = val + } + } + if err := s.Err(); err != nil { + return nil, err + } + return disk, nil +} + +func getVMDiskInfo(name string) (*VirtualDisk, error) { + out, err := vbmOut("showvminfo", name, "--machinereadable") + if err != nil { + return nil, err + } + r := strings.NewReader(out) + return parseDiskInfo(r) +} diff --git a/drivers/virtualbox/disk_test.go b/drivers/virtualbox/disk_test.go new file mode 100644 index 0000000000..b6b80cfbe7 --- /dev/null +++ b/drivers/virtualbox/disk_test.go @@ -0,0 +1,36 @@ +package virtualbox + +import ( + "strings" + "testing" +) + +var ( + testDiskInfoText = ` +storagecontrollerbootable0="on" +"SATA-0-0"="/home/ehazlett/.boot2docker/boot2docker.iso" +"SATA-IsEjected"="off" +"SATA-1-0"="/home/ehazlett/vm/test/disk.vmdk" +"SATA-ImageUUID-1-0"="12345-abcdefg" +"SATA-2-0"="none" +nic1="nat" + ` +) + +func TestVMDiskInfo(t *testing.T) { + r := strings.NewReader(testDiskInfoText) + disk, err := parseDiskInfo(r) + if err != nil { + t.Fatal(err) + } + + diskPath := "/home/ehazlett/vm/test/disk.vmdk" + diskUUID := "12345-abcdefg" + if disk.Path != diskPath { + t.Fatalf("expected disk path %s", diskPath) + } + + if disk.UUID != diskUUID { + t.Fatalf("expected disk uuid %s", diskUUID) + } +} diff --git a/drivers/virtualbox/vbm.go b/drivers/virtualbox/vbm.go index 9f0170ead3..d2223c9004 100644 --- a/drivers/virtualbox/vbm.go +++ b/drivers/virtualbox/vbm.go @@ -18,6 +18,7 @@ var ( reVMNameUUID = regexp.MustCompile(`"(.+)" {([0-9a-f-]+)}`) reVMInfoLine = regexp.MustCompile(`(?:"(.+)"|(.+))=(?:"(.*)"|(.*))`) reColonLine = regexp.MustCompile(`(.+):\s+(.*)`) + reEqualLine = regexp.MustCompile(`"(.+)"="(.*)"`) reMachineNotFound = regexp.MustCompile(`Could not find a registered machine named '(.+)'`) ) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 453afb5d86..83402c25ae 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -30,19 +30,20 @@ const ( ) type Driver struct { - CPU int - MachineName string - SSHUser string - SSHPort int - Memory int - DiskSize int - Boot2DockerURL string - CaCertPath string - PrivateKeyPath string - SwarmMaster bool - SwarmHost string - SwarmDiscovery string - storePath string + CPU int + MachineName string + SSHUser string + SSHPort int + Memory int + DiskSize int + Boot2DockerURL string + CaCertPath string + PrivateKeyPath string + SwarmMaster bool + SwarmHost string + SwarmDiscovery string + storePath string + Boot2DockerImportVM string } func init() { @@ -80,6 +81,11 @@ func GetCreateFlags() []cli.Flag { Usage: "The URL of the boot2docker image. Defaults to the latest available version", Value: "", }, + cli.StringFlag{ + Name: "virtualbox-import-boot2docker-vm", + Usage: "The name of a Boot2Docker VM to import", + Value: "", + }, } } @@ -147,6 +153,7 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.SwarmHost = flags.String("swarm-host") d.SwarmDiscovery = flags.String("swarm-discovery") d.SSHUser = "docker" + d.Boot2DockerImportVM = flags.String("virtualbox-import-boot2docker-vm") return nil } @@ -170,21 +177,36 @@ func (d *Driver) Create() error { return err } - log.Infof("Creating SSH key...") - b2dutils := utils.NewB2dUtils("", "") if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { return err } - if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { - return err - } - log.Infof("Creating VirtualBox VM...") - if err := d.generateDiskImage(d.DiskSize); err != nil { - return err + // import b2d VM if requested + if d.Boot2DockerImportVM != "" { + log.Debugf("Importing disk image...") + if err := d.importB2DVM(d.Boot2DockerImportVM); err != nil { + return err + } + + log.Debugf("Importing SSH key...") + keyPath := filepath.Join(utils.GetHomeDir(), ".ssh", "id_rsa") + if err := utils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil { + return err + } + } else { + log.Debugf("Creating disk image...") + if err := d.generateDiskImage(d.DiskSize); err != nil { + return err + } + + log.Infof("Creating SSH key...") + + if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { + return err + } } if err := vbm("createvm", @@ -594,6 +616,26 @@ func zeroFill(w io.Writer, n int64) error { return nil } +func (d Driver) importB2DVM(name string) error { + // make sure vm is stopped + _ = vbm("controlvm", name, "poweroff") + + diskInfo, err := getVMDiskInfo(name) + if err != nil { + return err + } + + if _, err := os.Stat(diskInfo.Path); err != nil { + return err + } + + if err := vbm("clonehd", diskInfo.Path, d.diskPath()); err != nil { + return err + } + + return nil +} + func getAvailableTCPPort() (int, error) { port := 0 for i := 0; i <= 10; i++ { From 7a4e16e082834c20b8a9a802b86c2040ca869229 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Wed, 8 Apr 2015 12:05:28 -0400 Subject: [PATCH 2/7] virtualbox: add ability to import b2d instances Signed-off-by: Evan Hazlett --- drivers/virtualbox/virtualbox.go | 3 +-- utils/utils.go | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 83402c25ae..21727a3cd1 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -192,7 +192,7 @@ func (d *Driver) Create() error { } log.Debugf("Importing SSH key...") - keyPath := filepath.Join(utils.GetHomeDir(), ".ssh", "id_rsa") + keyPath := filepath.Join(utils.GetHomeDir(), ".ssh", "id_boot2docker") if err := utils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil { return err } @@ -203,7 +203,6 @@ func (d *Driver) Create() error { } log.Infof("Creating SSH key...") - if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { return err } diff --git a/utils/utils.go b/utils/utils.go index 97a56e7743..c4899ef906 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -79,6 +79,15 @@ func CopyFile(src, dst string) error { return err } + fi, err := os.Stat(src) + if err != nil { + return err + } + + if err := os.Chmod(dst, fi.Mode()); err != nil { + return err + } + return nil } From a01322fa774dc8b2625f50cf06abd940c4dba626 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 9 Apr 2015 09:50:05 -0400 Subject: [PATCH 3/7] fixed ssh generation issue Signed-off-by: Evan Hazlett --- drivers/virtualbox/virtualbox.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 21727a3cd1..2d42d77afc 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -197,13 +197,13 @@ func (d *Driver) Create() error { return err } } else { - log.Debugf("Creating disk image...") - if err := d.generateDiskImage(d.DiskSize); err != nil { + log.Infof("Creating SSH key...") + if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { return err } - log.Infof("Creating SSH key...") - if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { + log.Debugf("Creating disk image...") + if err := d.generateDiskImage(d.DiskSize); err != nil { return err } } From 33eab99e5888747e37c2b606d777d8c2691c8e1d Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Mon, 13 Apr 2015 13:26:34 -0400 Subject: [PATCH 4/7] import cpu and memory settings when importing b2d vm Signed-off-by: Evan Hazlett --- drivers/virtualbox/disk.go | 2 +- drivers/virtualbox/vbm.go | 3 +- drivers/virtualbox/virtualbox.go | 49 +++++++++++++++------------- drivers/virtualbox/vm.go | 55 ++++++++++++++++++++++++++++++++ drivers/virtualbox/vm_test.go | 38 ++++++++++++++++++++++ 5 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 drivers/virtualbox/vm.go create mode 100644 drivers/virtualbox/vm_test.go diff --git a/drivers/virtualbox/disk.go b/drivers/virtualbox/disk.go index 20d35b95b9..8fb0e018cf 100644 --- a/drivers/virtualbox/disk.go +++ b/drivers/virtualbox/disk.go @@ -19,7 +19,7 @@ func parseDiskInfo(r io.Reader) (*VirtualDisk, error) { if line == "" { continue } - res := reEqualLine.FindStringSubmatch(line) + res := reEqualQuoteLine.FindStringSubmatch(line) if res == nil { continue } diff --git a/drivers/virtualbox/vbm.go b/drivers/virtualbox/vbm.go index d2223c9004..21a94e3172 100644 --- a/drivers/virtualbox/vbm.go +++ b/drivers/virtualbox/vbm.go @@ -18,7 +18,8 @@ var ( reVMNameUUID = regexp.MustCompile(`"(.+)" {([0-9a-f-]+)}`) reVMInfoLine = regexp.MustCompile(`(?:"(.+)"|(.+))=(?:"(.*)"|(.*))`) reColonLine = regexp.MustCompile(`(.+):\s+(.*)`) - reEqualLine = regexp.MustCompile(`"(.+)"="(.*)"`) + reEqualLine = regexp.MustCompile(`(.+)=(.*)`) + reEqualQuoteLine = regexp.MustCompile(`"(.+)"="(.*)"`) reMachineNotFound = regexp.MustCompile(`Could not find a registered machine named '(.+)'`) ) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 2d42d77afc..bf2a11a6d1 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -186,11 +186,33 @@ func (d *Driver) Create() error { // import b2d VM if requested if d.Boot2DockerImportVM != "" { - log.Debugf("Importing disk image...") - if err := d.importB2DVM(d.Boot2DockerImportVM); err != nil { + name := d.Boot2DockerImportVM + + // make sure vm is stopped + _ = vbm("controlvm", name, "poweroff") + + diskInfo, err := getVMDiskInfo(name) + if err != nil { + return err + } + + if _, err := os.Stat(diskInfo.Path); err != nil { return err } + if err := vbm("clonehd", diskInfo.Path, d.diskPath()); err != nil { + return err + } + + log.Debugf("Importing VM settings...") + vmInfo, err := getVMInfo(name) + if err != nil { + return err + } + + d.CPU = vmInfo.CPUs + d.Memory = vmInfo.Memory + log.Debugf("Importing SSH key...") keyPath := filepath.Join(utils.GetHomeDir(), ".ssh", "id_boot2docker") if err := utils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil { @@ -215,6 +237,9 @@ func (d *Driver) Create() error { return err } + log.Infof("CPUS: %d", d.CPU) + log.Infof("Mem: %d", d.Memory) + cpus := d.CPU if cpus < 1 { cpus = int(runtime.NumCPU()) @@ -615,26 +640,6 @@ func zeroFill(w io.Writer, n int64) error { return nil } -func (d Driver) importB2DVM(name string) error { - // make sure vm is stopped - _ = vbm("controlvm", name, "poweroff") - - diskInfo, err := getVMDiskInfo(name) - if err != nil { - return err - } - - if _, err := os.Stat(diskInfo.Path); err != nil { - return err - } - - if err := vbm("clonehd", diskInfo.Path, d.diskPath()); err != nil { - return err - } - - return nil -} - func getAvailableTCPPort() (int, error) { port := 0 for i := 0; i <= 10; i++ { diff --git a/drivers/virtualbox/vm.go b/drivers/virtualbox/vm.go new file mode 100644 index 0000000000..ff119fae53 --- /dev/null +++ b/drivers/virtualbox/vm.go @@ -0,0 +1,55 @@ +package virtualbox + +import ( + "bufio" + "io" + "strconv" + "strings" +) + +type VirtualBoxVM struct { + CPUs int + Memory int +} + +func parseVMInfo(r io.Reader) (*VirtualBoxVM, error) { + s := bufio.NewScanner(r) + vm := &VirtualBoxVM{} + for s.Scan() { + line := s.Text() + if line == "" { + continue + } + res := reEqualLine.FindStringSubmatch(line) + if res == nil { + continue + } + switch key, val := res[1], res[2]; key { + case "cpus": + v, err := strconv.Atoi(val) + if err != nil { + return nil, err + } + vm.CPUs = v + case "memory": + v, err := strconv.Atoi(val) + if err != nil { + return nil, err + } + vm.Memory = v + } + } + if err := s.Err(); err != nil { + return nil, err + } + return vm, nil +} + +func getVMInfo(name string) (*VirtualBoxVM, error) { + out, err := vbmOut("showvminfo", name, "--machinereadable") + if err != nil { + return nil, err + } + r := strings.NewReader(out) + return parseVMInfo(r) +} diff --git a/drivers/virtualbox/vm_test.go b/drivers/virtualbox/vm_test.go new file mode 100644 index 0000000000..5c9d86e362 --- /dev/null +++ b/drivers/virtualbox/vm_test.go @@ -0,0 +1,38 @@ +package virtualbox + +import ( + "strings" + "testing" +) + +var ( + testVMInfoText = ` +storagecontrollerbootable0="on" +memory=1024 +cpus=2 +"SATA-0-0"="/home/ehazlett/.boot2docker/boot2docker.iso" +"SATA-IsEjected"="off" +"SATA-1-0"="/home/ehazlett/vm/test/disk.vmdk" +"SATA-ImageUUID-1-0"="12345-abcdefg" +"SATA-2-0"="none" +nic1="nat" +` +) + +func TestVMInfo(t *testing.T) { + r := strings.NewReader(testVMInfoText) + vm, err := parseVMInfo(r) + if err != nil { + t.Fatal(err) + } + + vmCPUs := 2 + vmMemory := 1024 + if vm.CPUs != vmCPUs { + t.Fatalf("expected %d cpus; received %d", vmCPUs, vm.CPUs) + } + + if vm.Memory != vmMemory { + t.Fatalf("expected memory %d; received %d", vmMemory, vm.Memory) + } +} From 8f66be1bc224764f0d342901da439f8b02f00dc3 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Tue, 14 Apr 2015 12:29:15 -0400 Subject: [PATCH 5/7] b2d import: split key/val assignment from switch Signed-off-by: Evan Hazlett --- drivers/virtualbox/disk.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/virtualbox/disk.go b/drivers/virtualbox/disk.go index 8fb0e018cf..7fe2467184 100644 --- a/drivers/virtualbox/disk.go +++ b/drivers/virtualbox/disk.go @@ -23,7 +23,8 @@ func parseDiskInfo(r io.Reader) (*VirtualDisk, error) { if res == nil { continue } - switch key, val := res[1], res[2]; key { + key, val := res[1], res[2] + switch key { case "SATA-1-0": disk.Path = val case "SATA-ImageUUID-1-0": From e9491d22dc2d7f082c83f5716eae743659338f07 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 23 Apr 2015 09:34:48 -0400 Subject: [PATCH 6/7] b2d import: move debug logging to debug Signed-off-by: Evan Hazlett --- drivers/virtualbox/virtualbox.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index bf2a11a6d1..dc749d26d2 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -237,8 +237,8 @@ func (d *Driver) Create() error { return err } - log.Infof("CPUS: %d", d.CPU) - log.Infof("Mem: %d", d.Memory) + log.Debugf("VM CPUS: %d", d.CPU) + log.Debugf("VM Memory: %d", d.Memory) cpus := d.CPU if cpus < 1 { From d881aa80383944e0b67551df15d79d58479faf02 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 23 Apr 2015 09:35:53 -0400 Subject: [PATCH 7/7] b2d import: add migration docs Signed-off-by: Evan Hazlett --- docs/boot2docker_migration.md | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 docs/boot2docker_migration.md diff --git a/docs/boot2docker_migration.md b/docs/boot2docker_migration.md new file mode 100644 index 0000000000..3171e915c9 --- /dev/null +++ b/docs/boot2docker_migration.md @@ -0,0 +1,68 @@ +This guide explains migrating from the Boot2Docker CLI to Docker Machine. + +This guide assumes basic knowledge of the Boot2Docker CLI and Docker Machine. If you are not familiar, please review those docs prior to migrating. + +There are a few differences between the Boot2Docker CLI commands and Machine. Please review the table below for the Boot2Docker command and the corresponding Machine command. You can also see details on Machine commands in the official [Docker Machine Docs](http://docs.docker.com/machine/#subcommands). + +# Migrating + +In order to migrate a Boot2Docker VM to Docker Machine, you must have Docker Machine installed. If you do not have Docker Machine, please see the [install docs](http://docs.docker.com/machine/#installation) before proceeding. + +> Note: when migrating to Docker Machine, this will also update Docker to the latest stable version + +To migrate a Boot2Docker VM, run the following command where `` is the name of your Boot2Docker VM and `` is the name of the new Machine (i.e. `dev`): + +> To get the name of your Boot2Docker VM, use the `boot2docker config` command. Default: `boot2docker-vm`. + +``` +docker-machine create -d virtualbox --virtualbox-import-boot2docker-vm +``` + +> Note: this will stop the Boot2Docker VM in order to safely copy the virtual disk + +You should see output similar to the following: + +``` +$> docker-machine create -d virtualbox --virtualbox-import-boot2docker-vm boot2docker-vm dev +INFO[0000] Creating VirtualBox VM... +INFO[0001] Starting VirtualBox VM... +INFO[0001] Waiting for VM to start... +INFO[0035] "dev" has been created and is now the active machine. +INFO[0035] To point your Docker client at it, run this in your shell: eval "$(docker-machine env dev)" +``` + +You now should have a Machine that contains all of the Docker data from the Boot2Docker VM. See the Docker Machine [usage docs](http://docs.docker.com/machine/#getting-started-with-docker-machine-using-a-local-vm) for details on working with Machine. + +# Cleanup +When migrating a Boot2Docker VM to Docker Machine the Boot2Docker VM is left intact. Once you have verified that all of your Docker data (containers, images, etc) are in the new Machine, you can remove the Boot2Docker VM using `boot2docker delete`. + +# Command Comparison + +| boot2docker cli | machine | machine description | +|----|----|----| +| init | create | creates a new docker host | +| up | start | starts a stopped machine | +| ssh | ssh | runs a command or interactive ssh session on the machine | +| save | - | n/a | +| down | stop | stops a running machine | +| poweroff | stop | stops a running machine | +| reset | restart | restarts a running machine | +| config | inspect (*) | shows details about machine | +| status | ls (**) | shows a list of all machines | +| info | inspect (*) | shows details about machine | +| ip | url (***) | shows the Docker URL for the machine | +| shellinit | env | shows the environment configuration needed to configure the Docker CLI for the machine | +| delete | rm | removes a machine | +| download | - | | +| upgrade | upgrade | upgrades Docker on the machine to the latest stable release | + + +\* provides similar functionality but not exact + +** `ls` will show all machines including their status + +** the `url` command reports the entire Docker URL including the IP / Hostname: +``` +``` +``` +```