diff options
Diffstat (limited to 'pkg/machine/pull.go')
-rw-r--r-- | pkg/machine/pull.go | 96 |
1 files changed, 68 insertions, 28 deletions
diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go index 3c8422a30..280b47f96 100644 --- a/pkg/machine/pull.go +++ b/pkg/machine/pull.go @@ -1,8 +1,9 @@ -// +build amd64,!windows arm64,!windows +// +build amd64 arm64 package machine import ( + "bufio" "fmt" "io" "io/ioutil" @@ -17,6 +18,7 @@ import ( "github.com/containers/image/v5/pkg/compression" "github.com/containers/storage/pkg/archive" "github.com/sirupsen/logrus" + "github.com/ulikunitz/xz" "github.com/vbauerster/mpb/v6" "github.com/vbauerster/mpb/v6/decor" ) @@ -43,7 +45,7 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload return nil, err } if len(getURL.Scheme) > 0 { - urlSplit := strings.Split(pullPath, "/") + urlSplit := strings.Split(getURL.Path, "/") imageName = urlSplit[len(urlSplit)-1] dl.LocalUncompressedFile = filepath.Join(dataDir, imageName) dl.URL = getURL @@ -63,39 +65,48 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload return gd, nil } -func (g GenericDownload) getLocalUncompressedName() string { +func (d Download) getLocalUncompressedName() string { var ( extension string ) switch { - case strings.HasSuffix(g.LocalPath, ".bz2"): + case strings.HasSuffix(d.LocalPath, ".bz2"): extension = ".bz2" - case strings.HasSuffix(g.LocalPath, ".gz"): + case strings.HasSuffix(d.LocalPath, ".gz"): extension = ".gz" - case strings.HasSuffix(g.LocalPath, ".xz"): + case strings.HasSuffix(d.LocalPath, ".xz"): extension = ".xz" } - uncompressedFilename := filepath.Join(filepath.Dir(g.LocalUncompressedFile), g.VMName+"_"+g.ImageName) + uncompressedFilename := filepath.Join(filepath.Dir(d.LocalPath), d.VMName+"_"+d.ImageName) return strings.TrimSuffix(uncompressedFilename, extension) } -func (g GenericDownload) DownloadImage() error { +func (g GenericDownload) Get() *Download { + return &g.Download +} + +func (g GenericDownload) HasUsableCache() (bool, error) { // If we have a URL for this "downloader", we now pull it - if g.URL != nil { - if err := DownloadVMImage(g.URL, g.LocalPath); err != nil { + return g.URL == nil, nil +} + +func DownloadImage(d DistributionDownload) error { + // check if the latest image is already present + ok, err := d.HasUsableCache() + if err != nil { + return err + } + if !ok { + if err := DownloadVMImage(d.Get().URL, d.Get().LocalPath); err != nil { return err } } - return Decompress(g.LocalPath, g.getLocalUncompressedName()) -} - -func (g GenericDownload) Get() *Download { - return &g.Download + return Decompress(d.Get().LocalPath, d.Get().getLocalUncompressedName()) } // DownloadVMImage downloads a VM image from url to given path // with download status -func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error { +func DownloadVMImage(downloadURL *url2.URL, localImagePath string) error { out, err := os.Create(localImagePath) if err != nil { return err @@ -120,7 +131,7 @@ func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error { return fmt.Errorf("error downloading VM image %s: %s", downloadURL, resp.Status) } size := resp.ContentLength - urlSplit := strings.Split(downloadURL.String(), "/") + urlSplit := strings.Split(downloadURL.Path, "/") prefix := "Downloading VM image: " + urlSplit[len(urlSplit)-1] onComplete := prefix + ": done" @@ -177,24 +188,50 @@ func Decompress(localPath, uncompressedPath string) error { // Will error out if file without .xz already exists // Maybe extracting then renameing is a good idea here.. // depends on xz: not pre-installed on mac, so it becomes a brew dependency -func decompressXZ(src string, output io.Writer) error { - cmd := exec.Command("xzcat", "-k", src) - //cmd := exec.Command("xz", "-d", "-k", "-v", src) - stdOut, err := cmd.StdoutPipe() - if err != nil { - return err +func decompressXZ(src string, output io.WriteCloser) error { + var read io.Reader + var cmd *exec.Cmd + // Prefer xz utils for fastest performance, fallback to go xi2 impl + if _, err := exec.LookPath("xzcat"); err == nil { + cmd = exec.Command("xzcat", "-k", src) + read, err = cmd.StdoutPipe() + if err != nil { + return err + } + cmd.Stderr = os.Stderr + } else { + file, err := os.Open(src) + if err != nil { + return err + } + defer file.Close() + // This XZ implementation is reliant on buffering. It is also 3x+ slower than XZ utils. + // Consider replacing with a faster implementation (e.g. xi2) if podman machine is + // updated with a larger image for the distribution base. + buf := bufio.NewReader(file) + read, err = xz.NewReader(buf) + if err != nil { + return err + } } - //cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + + done := make(chan bool) go func() { - if _, err := io.Copy(output, stdOut); err != nil { + if _, err := io.Copy(output, read); err != nil { logrus.Error(err) } + output.Close() + done <- true }() - return cmd.Run() + + if cmd != nil { + return cmd.Run() + } + <-done + return nil } -func decompressEverythingElse(src string, output io.Writer) error { +func decompressEverythingElse(src string, output io.WriteCloser) error { f, err := os.Open(src) if err != nil { return err @@ -207,6 +244,9 @@ func decompressEverythingElse(src string, output io.Writer) error { if err := uncompressStream.Close(); err != nil { logrus.Error(err) } + if err := output.Close(); err != nil { + logrus.Error(err) + } }() _, err = io.Copy(output, uncompressStream) |