[lxc-devel] [lxd/master] Show download progress for `lxc file` and `lxc import` subcommands

monstermunchkin on Github lxc-bot at linuxcontainers.org
Mon Aug 6 11:32:44 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 314 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180806/cfc3b8dd/attachment.bin>
-------------- next part --------------
From 5a89687c9a2f89a5ea08335e94f6f6068f4d6713 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 2 Aug 2018 19:59:18 +0200
Subject: [PATCH 1/2] lxc/file: Show progress

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxc/file.go    | 116 ++++++++++++++++++++++++++++++++++++++++++++++---
 shared/util.go |  17 ++++++++
 2 files changed, 126 insertions(+), 7 deletions(-)

diff --git a/lxc/file.go b/lxc/file.go
index a237f50686..b82a9b9f87 100644
--- a/lxc/file.go
+++ b/lxc/file.go
@@ -15,9 +15,11 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/lxc/lxd/client"
+	"github.com/lxc/lxd/lxc/utils"
 	"github.com/lxc/lxd/shared"
 	cli "github.com/lxc/lxd/shared/cmd"
 	"github.com/lxc/lxd/shared/i18n"
+	"github.com/lxc/lxd/shared/ioprogress"
 	"github.com/lxc/lxd/shared/logger"
 	"github.com/lxc/lxd/shared/termios"
 )
@@ -328,10 +330,28 @@ func (c *cmdFilePull) Run(cmd *cobra.Command, args []string) error {
 			}
 		}
 
-		_, err = io.Copy(f, buf)
+		progress := utils.ProgressRenderer{
+			Format: i18n.G(fmt.Sprintf("Pulling %s from %s: %%s", targetPath, pathSpec[1])),
+		}
+
+		writer := &ioprogress.ProgressWriter{
+			WriteCloser: f,
+			Tracker: &ioprogress.ProgressTracker{
+				Handler: func(bytesReceived int64, speed int64) {
+					progress.UpdateProgress(ioprogress.ProgressData{
+						Text: fmt.Sprintf("%s (%s/s)",
+							shared.GetByteSizeString(bytesReceived, 2),
+							shared.GetByteSizeString(speed, 2))})
+				},
+			},
+		}
+
+		_, err = io.Copy(writer, buf)
 		if err != nil {
+			progress.Done("")
 			return err
 		}
+		progress.Done("")
 	}
 
 	return nil
@@ -523,10 +543,9 @@ func (c *cmdFilePush) Run(cmd *cobra.Command, args []string) error {
 
 		// Transfer the files
 		args := lxd.ContainerFileArgs{
-			Content: f,
-			UID:     -1,
-			GID:     -1,
-			Mode:    -1,
+			UID:  -1,
+			GID:  -1,
+			Mode: -1,
 		}
 
 		if !c.noModeChange {
@@ -560,11 +579,34 @@ func (c *cmdFilePush) Run(cmd *cobra.Command, args []string) error {
 		}
 		args.Type = "file"
 
+		fstat, err := f.Stat()
+		if err != nil {
+			return err
+		}
+
+		progress := utils.ProgressRenderer{
+			Format: i18n.G(fmt.Sprintf("Pushing %s to %s: %%s", f.Name(), fpath)),
+		}
+
+		args.Content = shared.NewReadSeeker(&ioprogress.ProgressReader{
+			ReadCloser: f,
+			Tracker: &ioprogress.ProgressTracker{
+				Length: fstat.Size(),
+				Handler: func(percent int64, speed int64) {
+					progress.UpdateProgress(ioprogress.ProgressData{
+						Text: fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2)),
+					})
+				},
+			},
+		}, f)
+
 		logger.Infof("Pushing %s to %s (%s)", f.Name(), fpath, args.Type)
 		err = resource.server.CreateContainerFile(resource.name, fpath, args)
 		if err != nil {
 			return err
+			progress.Done("")
 		}
+		progress.Done("")
 	}
 
 	return nil
@@ -605,10 +647,28 @@ func (c *cmdFile) recursivePullFile(d lxd.ContainerServer, container string, p s
 			return err
 		}
 
-		_, err = io.Copy(f, buf)
+		progress := utils.ProgressRenderer{
+			Format: i18n.G(fmt.Sprintf("Pulling %s from %s: %%s", p, target)),
+		}
+
+		writer := &ioprogress.ProgressWriter{
+			WriteCloser: f,
+			Tracker: &ioprogress.ProgressTracker{
+				Handler: func(bytesReceived int64, speed int64) {
+					progress.UpdateProgress(ioprogress.ProgressData{
+						Text: fmt.Sprintf("%s (%s/s)",
+							shared.GetByteSizeString(bytesReceived, 2),
+							shared.GetByteSizeString(speed, 2))})
+				},
+			},
+		}
+
+		_, err = io.Copy(writer, buf)
 		if err != nil {
+			progress.Done("")
 			return err
 		}
+		progress.Done("")
 	} else if resp.Type == "symlink" {
 		linkTarget, err := ioutil.ReadAll(buf)
 		if err != nil {
@@ -650,6 +710,8 @@ func (c *cmdFile) recursivePushFile(d lxd.ContainerServer, container string, sou
 			Mode: int(mode.Perm()),
 		}
 
+		var readCloser io.ReadCloser
+
 		if fInfo.IsDir() {
 			// Directory handling
 			args.Type = "directory"
@@ -662,6 +724,7 @@ func (c *cmdFile) recursivePushFile(d lxd.ContainerServer, container string, sou
 
 			args.Type = "symlink"
 			args.Content = bytes.NewReader([]byte(symlinkTarget))
+			readCloser = ioutil.NopCloser(args.Content)
 		} else {
 			// File handling
 			f, err := os.Open(p)
@@ -672,10 +735,49 @@ func (c *cmdFile) recursivePushFile(d lxd.ContainerServer, container string, sou
 
 			args.Type = "file"
 			args.Content = f
+			readCloser = f
+		}
+
+		progress := utils.ProgressRenderer{
+			Format: i18n.G(fmt.Sprintf("Pushing %s to %s: %%s", p, targetPath)),
+		}
+
+		if args.Type != "directory" {
+			contentLength, err := args.Content.Seek(0, io.SeekEnd)
+			if err != nil {
+				return err
+			}
+
+			_, err = args.Content.Seek(0, io.SeekStart)
+			if err != nil {
+				return err
+			}
+
+			args.Content = shared.NewReadSeeker(&ioprogress.ProgressReader{
+				ReadCloser: readCloser,
+				Tracker: &ioprogress.ProgressTracker{
+					Length: contentLength,
+					Handler: func(percent int64, speed int64) {
+						progress.UpdateProgress(ioprogress.ProgressData{
+							Text: fmt.Sprintf("%d%% (%s/s)", percent,
+								shared.GetByteSizeString(speed, 2))})
+					},
+				},
+			}, args.Content)
 		}
 
 		logger.Infof("Pushing %s to %s (%s)", p, targetPath, args.Type)
-		return d.CreateContainerFile(container, targetPath, args)
+		err = d.CreateContainerFile(container, targetPath, args)
+		if err != nil {
+			if args.Type != "directory" {
+				progress.Done("")
+			}
+			return err
+		}
+		if args.Type != "directory" {
+			progress.Done("")
+		}
+		return nil
 	}
 
 	return filepath.Walk(source, sendFile)
diff --git a/shared/util.go b/shared/util.go
index c098b0d50f..85706702f5 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -1087,3 +1087,20 @@ func ParseNumberFromFile(file string) (int64, error) {
 
 	return int64(nr), nil
 }
+
+type ReadSeeker struct {
+	io.Reader
+	io.Seeker
+}
+
+func NewReadSeeker(reader io.Reader, seeker io.Seeker) *ReadSeeker {
+	return &ReadSeeker{Reader: reader, Seeker: seeker}
+}
+
+func (r *ReadSeeker) Read(p []byte) (n int, err error) {
+	return r.Reader.Read(p)
+}
+
+func (r *ReadSeeker) Seek(offset int64, whence int) (int64, error) {
+	return r.Seeker.Seek(offset, whence)
+}

From 4bbe6f566506727b5d8bccd2384e9f6346a8d757 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 3 Aug 2018 10:29:52 +0200
Subject: [PATCH 2/2] lxc/import: Show progress

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxc/import.go | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/lxc/import.go b/lxc/import.go
index b532a6a7d1..0138a35603 100644
--- a/lxc/import.go
+++ b/lxc/import.go
@@ -1,14 +1,17 @@
 package main
 
 import (
+	"fmt"
 	"os"
 
 	"github.com/spf13/cobra"
 
 	"github.com/lxc/lxd/client"
+	"github.com/lxc/lxd/lxc/utils"
 	"github.com/lxc/lxd/shared"
 	cli "github.com/lxc/lxd/shared/cmd"
 	"github.com/lxc/lxd/shared/i18n"
+	"github.com/lxc/lxd/shared/ioprogress"
 )
 
 type cmdImport struct {
@@ -56,13 +59,35 @@ func (c *cmdImport) Run(cmd *cobra.Command, args []string) error {
 	}
 	defer file.Close()
 
+	fstat, err := file.Stat()
+	if err != nil {
+		return err
+	}
+
+	progress := utils.ProgressRenderer{Format: i18n.G("Importing container: %s")}
+
 	createArgs := lxd.ContainerBackupArgs{}
-	createArgs.BackupFile = file
+	createArgs.BackupFile = &ioprogress.ProgressReader{
+		ReadCloser: file,
+		Tracker: &ioprogress.ProgressTracker{
+			Length: fstat.Size(),
+			Handler: func(percent int64, speed int64) {
+				progress.UpdateProgress(ioprogress.ProgressData{Text: fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(speed, 2))})
+			},
+		},
+	}
 
 	op, err := resource.server.CreateContainerFromBackup(createArgs)
 	if err != nil {
 		return err
 	}
 
-	return op.Wait()
+	err = op.Wait()
+	if err != nil {
+		progress.Done("")
+		return err
+	}
+	progress.Done("")
+
+	return nil
 }


More information about the lxc-devel mailing list