From a7bd9e06d20e2daae13bab184f19ba4e93f804c5 Mon Sep 17 00:00:00 2001 From: David Gageot Date: Fri, 18 Dec 2015 09:47:12 +0100 Subject: [PATCH] Update B2D cache in PreCreateCheck phase Signed-off-by: David Gageot --- drivers/hyperv/hyperv.go | 23 +++++-- drivers/virtualbox/virtualbox.go | 7 +++ drivers/vmwarefusion/fusion_darwin.go | 13 +++- drivers/vmwarevsphere/vsphere.go | 20 ++++-- libmachine/mcnutils/b2d.go | 89 +++++++++++---------------- libmachine/mcnutils/b2d_test.go | 7 ++- 6 files changed, 92 insertions(+), 67 deletions(-) diff --git a/drivers/hyperv/hyperv.go b/drivers/hyperv/hyperv.go index 8293e641da..ef0bb35ac0 100644 --- a/drivers/hyperv/hyperv.go +++ b/drivers/hyperv/hyperv.go @@ -19,7 +19,7 @@ import ( type Driver struct { *drivers.BaseDriver - boot2DockerURL string + Boot2DockerURL string boot2DockerLoc string vSwitch string diskImage string @@ -73,7 +73,7 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { } func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { - d.boot2DockerURL = flags.String("hyperv-boot2docker-url") + d.Boot2DockerURL = flags.String("hyperv-boot2docker-url") d.boot2DockerLoc = flags.String("hyperv-boot2docker-location") d.vSwitch = flags.String("hyperv-virtual-switch") d.DiskSize = flags.Int("hyperv-disk-size") @@ -139,16 +139,27 @@ func (d *Driver) GetState() (state.State, error) { return state.None, nil } -func (d *Driver) Create() error { - err := hypervAvailable() - if err != nil { +// PreCreateCheck checks that the machine creation process can be started safely. +func (d *Driver) PreCreateCheck() error { + if err := hypervAvailable(); err != nil { return err } + // Downloading boot2docker to cache should be done here to make sure + // that a download failure will not leave a machine half created. + b2dutils := mcnutils.NewB2dUtils(d.StorePath) + if err := b2dutils.UpdateISOCache(d.Boot2DockerURL); err != nil { + return err + } + + return nil +} + +func (d *Driver) Create() error { d.setMachineNameIfNotSet() b2dutils := mcnutils.NewB2dUtils(d.StorePath) - if err := b2dutils.CopyIsoToMachineDir(d.boot2DockerURL, d.MachineName); err != nil { + if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { return err } diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index a5eed341df..444246e4b5 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -216,6 +216,13 @@ func (d *Driver) PreCreateCheck() error { log.Warn("This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory.") } + // Downloading boot2docker to cache should be done here to make sure + // that a download failure will not leave a machine half created. + b2dutils := mcnutils.NewB2dUtils(d.StorePath) + if err := b2dutils.UpdateISOCache(d.Boot2DockerURL); err != nil { + return err + } + return nil } diff --git a/drivers/vmwarefusion/fusion_darwin.go b/drivers/vmwarefusion/fusion_darwin.go index f76d2e9ce7..8d17660ddf 100644 --- a/drivers/vmwarefusion/fusion_darwin.go +++ b/drivers/vmwarefusion/fusion_darwin.go @@ -206,6 +206,18 @@ func (d *Driver) GetState() (state.State, error) { return state.Stopped, nil } +// PreCreateCheck checks that the machine creation process can be started safely. +func (d *Driver) PreCreateCheck() error { + // Downloading boot2docker to cache should be done here to make sure + // that a download failure will not leave a machine half created. + b2dutils := mcnutils.NewB2dUtils(d.StorePath) + if err := b2dutils.UpdateISOCache(d.Boot2DockerURL); err != nil { + return err + } + + return nil +} + func (d *Driver) Create() error { b2dutils := mcnutils.NewB2dUtils(d.StorePath) if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { @@ -214,7 +226,6 @@ func (d *Driver) Create() error { // download cloud-init config drive if d.ConfigDriveURL != "" { - log.Infof("Downloading %s from %s", isoConfigDrive, d.ConfigDriveURL) if err := b2dutils.DownloadISO(d.ResolveStorePath("."), isoConfigDrive, d.ConfigDriveURL); err != nil { return err } diff --git a/drivers/vmwarevsphere/vsphere.go b/drivers/vmwarevsphere/vsphere.go index a8e8692dae..920afada89 100644 --- a/drivers/vmwarevsphere/vsphere.go +++ b/drivers/vmwarevsphere/vsphere.go @@ -212,16 +212,28 @@ func (d *Driver) GetState() (state.State, error) { return state.None, nil } +// PreCreateCheck checks that the machine creation process can be started safely. +func (d *Driver) PreCreateCheck() error { + if err := d.checkVsphereConfig(); err != nil { + return err + } + + // Downloading boot2docker to cache should be done here to make sure + // that a download failure will not leave a machine half created. + b2dutils := mcnutils.NewB2dUtils(d.StorePath) + if err := b2dutils.UpdateISOCache(d.Boot2DockerURL); err != nil { + return err + } + + return nil +} + // Create has the following implementation: // 1. check whether the docker directory contains the boot2docker ISO // 2. generate an SSH keypair and bundle it in a tar. // 3. create a virtual machine with the boot2docker ISO mounted; // 4. reconfigure the virtual machine network and disk size; func (d *Driver) Create() error { - if err := d.checkVsphereConfig(); err != nil { - return err - } - b2dutils := mcnutils.NewB2dUtils(d.StorePath) if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { return err diff --git a/libmachine/mcnutils/b2d.go b/libmachine/mcnutils/b2d.go index 4b9bb67b87..c7a42048d7 100644 --- a/libmachine/mcnutils/b2d.go +++ b/libmachine/mcnutils/b2d.go @@ -43,11 +43,9 @@ func getClient() *http.Client { Dial: defaultTimeout, } - client := http.Client{ + return &http.Client{ Transport: &transport, } - - return &client } func getRequest(apiURL string) (*http.Request, error) { @@ -204,11 +202,7 @@ func (*b2dReleaseGetter) download(dir, file, isoURL string) error { return err } - if err := os.Rename(f.Name(), dest); err != nil { - return err - } - - return nil + return os.Rename(f.Name(), dest) } // iso is an ISO volume. @@ -305,6 +299,7 @@ func NewB2dUtils(storePath string) *B2dUtils { // DownloadISO downloads boot2docker ISO image for the given tag and save it at dest. func (b *B2dUtils) DownloadISO(dir, file, isoURL string) error { + log.Infof("Downloading %s from %s...", b.path(), isoURL) return b.download(dir, file, isoURL) } @@ -351,31 +346,51 @@ func (b *B2dUtils) DownloadLatestBoot2Docker(apiURL string) error { } func (b *B2dUtils) DownloadISOFromURL(latestReleaseURL string) error { - log.Infof("Downloading %s to %s...", latestReleaseURL, b.path()) - if err := b.DownloadISO(b.imgCachePath, b.filename(), latestReleaseURL); err != nil { - return err + return b.DownloadISO(b.imgCachePath, b.filename(), latestReleaseURL) +} + +func (b *B2dUtils) UpdateISOCache(isoURL string) error { + // recreate the cache dir if it has been manually deleted + if _, err := os.Stat(b.imgCachePath); os.IsNotExist(err) { + log.Infof("Image cache directory does not exist, creating it at %s...", b.imgCachePath) + if err := os.Mkdir(b.imgCachePath, 0700); err != nil { + return err + } + } + + if isoURL != "" { + // Non-default B2D are not cached + return nil + } + + exists := b.exists() + if !exists { + log.Info("No default Boot2Docker ISO found locally, downloading the latest release...") + return b.DownloadLatestBoot2Docker("") + } + + latest := b.isLatest() + if !latest { + log.Info("Default Boot2Docker ISO is out-of-date, downloading the latest release...") + return b.DownloadLatestBoot2Docker("") } return nil } func (b *B2dUtils) CopyIsoToMachineDir(isoURL, machineName string) error { + if err := b.UpdateISOCache(isoURL); err != nil { + return err + } + // TODO: This is a bit off-color. machineDir := filepath.Join(b.storePath, "machines", machineName) machineIsoPath := filepath.Join(machineDir, b.filename()) - // just in case the cache dir has been manually deleted, - // check for it and recreate it if it's gone - if _, err := os.Stat(b.imgCachePath); os.IsNotExist(err) { - log.Infof("Image cache does not exist, creating it at %s...", b.imgCachePath) - if err := os.Mkdir(b.imgCachePath, 0700); err != nil { - return err - } - } - // By default just copy the existing "cached" iso to the machine's directory... if isoURL == "" { - return b.copyDefaultISOToMachine(machineIsoPath) + log.Infof("Copying %s to %s...", b.path(), machineIsoPath) + return CopyFile(b.path(), machineIsoPath) } // if ISO is specified, check if it matches a github releases url or fallback to a direct download @@ -384,41 +399,9 @@ func (b *B2dUtils) CopyIsoToMachineDir(isoURL, machineName string) error { return err } - log.Infof("Downloading %s from %s...", b.filename(), downloadURL) return b.DownloadISO(machineDir, b.filename(), downloadURL) } -func (b *B2dUtils) copyDefaultISOToMachine(machineIsoPath string) error { - // just in case the cache dir has been manually deleted, - // check for it and recreate it if it's gone - if _, err := os.Stat(b.imgCachePath); os.IsNotExist(err) { - log.Infof("Image cache directory does not exist, creating it at %s...", b.imgCachePath) - if err := os.Mkdir(b.imgCachePath, 0700); err != nil { - return err - } - } - - exists := b.exists() - latest := b.isLatest() - - if exists && latest { - log.Infof("Latest Boot2Docker ISO found locally, copying it to %s...", machineIsoPath) - return CopyFile(b.path(), machineIsoPath) - } - - if !exists { - log.Info("No default Boot2Docker ISO found locally, downloading the latest release...") - } else if !latest { - log.Info("Default Boot2Docker ISO is out-of-date, downloading the latest release...") - } - if err := b.DownloadLatestBoot2Docker(""); err != nil { - return err - } - - log.Infof("Copying %s to %s...", b.path(), machineIsoPath) - return CopyFile(b.path(), machineIsoPath) -} - // isLatest checks the latest release tag and // reports whether the local ISO cache is the latest version. // diff --git a/libmachine/mcnutils/b2d_test.go b/libmachine/mcnutils/b2d_test.go index e3242089fb..3437ffb372 100644 --- a/libmachine/mcnutils/b2d_test.go +++ b/libmachine/mcnutils/b2d_test.go @@ -261,13 +261,14 @@ func TestCopyDefaultISOToMachine(t *testing.T) { imgCachePath: imgCachePath, } - dir := filepath.Join(storePath, tt.machineName) + dir := filepath.Join(storePath, "machines", tt.machineName) err = os.MkdirAll(dir, 0700) - assert.NoError(t, err, "machine: %s", tt.machineName) + err = b.CopyIsoToMachineDir("", tt.machineName) + assert.NoError(t, err) + dest := filepath.Join(dir, b.filename()) - err = b.copyDefaultISOToMachine(dest) _, pathErr := os.Stat(dest) assert.NoError(t, err, "machine: %s", tt.machineName)