Skip to content

Commit 6a43f4f

Browse files
committed
706 feat: Add progress
add color to progress add info at the end
1 parent 0ac389e commit 6a43f4f

File tree

5 files changed

+83
-6
lines changed

5 files changed

+83
-6
lines changed

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ test-race:
2525
go test -race ./...
2626

2727
build:
28-
${GOPATH}/bin/gox -ldflags="-w -s" -osarch=${TARGETS}
28+
#${GOPATH}/bin/gox -ldflags="-w -s" -osarch=${TARGETS}
29+
go build
2930

3031
build-dev:
3132
${GOPATH}/bin/gox -osarch=${TARGETS} -output="{{.Dir}}_{{.OS}}_{{.Arch}}-${COMMIT_HASH}"

backup/backup.go

+49-3
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ import (
3838

3939
"github.com/cenkalti/backoff"
4040
"github.com/dustin/go-humanize"
41+
"github.com/k0kubun/go-ansi"
4142
"github.com/miolini/datacounter"
4243
"github.com/nightlyone/lockfile"
44+
"github.com/schollz/progressbar/v3"
4345
"golang.org/x/sync/errgroup"
4446

4547
"github.com/someone1/zfsbackup-go/backends"
@@ -320,6 +322,7 @@ func Backup(pctx context.Context, jobInfo *files.JobInfo) error {
320322

321323
// Prepare backends and setup plumbing
322324
for _, destination := range jobInfo.Destinations {
325+
log.AppLogger.Infof("Initializing backend for destination %s", destination)
323326
backend, berr := prepareBackend(ctx, jobInfo, destination, uploadBuffer)
324327
if berr != nil {
325328
log.AppLogger.Errorf("Could not initialize backend due to error - %v.", berr)
@@ -416,16 +419,19 @@ func Backup(pctx context.Context, jobInfo *files.JobInfo) error {
416419
} else {
417420
fmt.Fprintf(
418421
config.Stdout,
419-
"Done.\n\tTotal ZFS Stream Bytes: %d (%s)\n\tTotal Bytes Written: %d (%s)\n\tElapsed Time: %v\n\tTotal Files Uploaded: %d\n",
422+
"Done.\n\tTotal ZFS Stream Bytes: %d (%s)\n\tTotal Bytes Written: %d (%s)\n\tElapsed Time: %v\n\tTotal Files Uploaded: %d\n\tAverage Upload Rate: %s\n",
420423
jobInfo.ZFSStreamBytes,
421424
humanize.IBytes(jobInfo.ZFSStreamBytes),
422425
totalWrittenBytes,
423426
humanize.IBytes(totalWrittenBytes),
424427
time.Since(jobInfo.StartTime),
425428
len(jobInfo.Volumes)+1,
429+
fmt.Sprintf("%.2f TB/hr", float64(totalWrittenBytes)/1e12/time.Since(jobInfo.StartTime).Hours()),
426430
)
427431
}
428432

433+
fmt.Printf("Backup of %s completed successfully.\n", jobInfo.VolumeName)
434+
429435
log.AppLogger.Debugf("Cleaning up resources...")
430436

431437
for _, backend := range usedBackends {
@@ -478,6 +484,7 @@ func saveManifest(ctx context.Context, j *files.JobInfo, final bool) (*files.Vol
478484
}
479485

480486
// nolint:funlen,gocyclo // Difficult to break this apart
487+
481488
func sendStream(ctx context.Context, j *files.JobInfo, c chan<- *files.VolumeInfo, buffer <-chan bool) error {
482489
var group *errgroup.Group
483490
group, ctx = errgroup.WithContext(ctx)
@@ -493,6 +500,39 @@ func sendStream(ctx context.Context, j *files.JobInfo, c chan<- *files.VolumeInf
493500
usingPipe = true
494501
}
495502

503+
// Get total dataset size for progress tracking
504+
totalSize, err := zfs.GetDatasetSize(ctx, j.VolumeName)
505+
if err != nil {
506+
return err
507+
}
508+
509+
// Initialize progress bar
510+
bar := progressbar.NewOptions64(int64(totalSize),
511+
progressbar.OptionSetWriter(ansi.NewAnsiStdout()),
512+
progressbar.OptionEnableColorCodes(true),
513+
progressbar.OptionShowBytes(true),
514+
progressbar.OptionSetWidth(50),
515+
progressbar.OptionThrottle(65*time.Millisecond),
516+
progressbar.OptionShowCount(),
517+
progressbar.OptionSetDescription("[cyan]Backing up...[reset]"),
518+
progressbar.OptionOnCompletion(func() {
519+
fmt.Fprint(ansi.NewAnsiStdout(), "\n")
520+
}),
521+
progressbar.OptionSpinnerType(14),
522+
progressbar.OptionFullWidth(),
523+
progressbar.OptionSetTheme(progressbar.Theme{
524+
Saucer: "[green]=[reset]",
525+
SaucerHead: "[green]>[reset]",
526+
SaucerPadding: " ",
527+
BarStart: "[",
528+
BarEnd: "]",
529+
}),
530+
)
531+
532+
// Initialize chunk tracking variables
533+
totalChunks := int(totalSize / (j.VolumeSize * humanize.MiByte))
534+
var processedChunks int
535+
496536
group.Go(func() error {
497537
var lastTotalBytes uint64
498538
defer close(c)
@@ -527,6 +567,8 @@ func sendStream(ctx context.Context, j *files.JobInfo, c chan<- *files.VolumeInf
527567
if !usingPipe {
528568
c <- volume
529569
}
570+
processedChunks++
571+
bar.Describe(fmt.Sprintf("Backing up... (%d/%d chunks)", processedChunks, totalChunks))
530572
}
531573
<-buffer
532574
volume, err = files.CreateBackupVolume(ctx, j, volNum)
@@ -542,7 +584,7 @@ func sendStream(ctx context.Context, j *files.JobInfo, c chan<- *files.VolumeInf
542584
}
543585

544586
// Write a little at a time and break the output between volumes as needed
545-
_, ierr := io.CopyN(volume, counter, files.BufferSize*2)
587+
bytesWritten, ierr := io.CopyN(volume, counter, files.BufferSize*2)
546588
if ierr == io.EOF {
547589
// We are done!
548590
log.AppLogger.Debugf("Finished creating volume %s", volume.ObjectName)
@@ -554,17 +596,21 @@ func sendStream(ctx context.Context, j *files.JobInfo, c chan<- *files.VolumeInf
554596
if !usingPipe {
555597
c <- volume
556598
}
599+
processedChunks++
600+
bar.Describe(fmt.Sprintf("Backing up... (%d/%d chunks)", processedChunks, totalChunks))
557601
return nil
558602
} else if ierr != nil {
559603
log.AppLogger.Errorf("Error while trying to read from the zfs stream for volume %s - %v", volume.ObjectName, ierr)
560604
return ierr
561605
}
606+
// Update progress bar
607+
bar.Add64(int64(bytesWritten))
562608
}
563609
})
564610

565611
// Start the zfs send command
566612
log.AppLogger.Infof("Starting zfs send command: %s", strings.Join(cmd.Args, " "))
567-
err := cmd.Start()
613+
err = cmd.Start()
568614
if err != nil {
569615
log.AppLogger.Errorf("Error starting zfs command - %v", err)
570616
return err

go.mod

+7-2
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,20 @@ require (
4747
github.com/googleapis/gax-go/v2 v2.5.1 // indirect
4848
github.com/inconshreveable/mousetrap v1.0.1 // indirect
4949
github.com/jmespath/go-jmespath v0.4.0 // indirect
50+
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
5051
github.com/klauspost/compress v1.15.10 // indirect
5152
github.com/kr/fs v0.1.0 // indirect
5253
github.com/mattn/go-ieproxy v0.0.9 // indirect
54+
github.com/mattn/go-isatty v0.0.20 // indirect
55+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
56+
github.com/rivo/uniseg v0.4.7 // indirect
57+
github.com/schollz/progressbar/v3 v3.14.3 // indirect
5358
github.com/spf13/pflag v1.0.5 // indirect
5459
go.opencensus.io v0.23.0 // indirect
5560
golang.org/x/net v0.0.0-20220921203646-d300de134e69 // indirect
5661
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
57-
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
58-
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
62+
golang.org/x/sys v0.20.0 // indirect
63+
golang.org/x/term v0.20.0 // indirect
5964
golang.org/x/text v0.3.7 // indirect
6065
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
6166
google.golang.org/appengine v1.6.7 // indirect

go.sum

+16
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
252252
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
253253
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
254254
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
255+
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
256+
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
255257
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
256258
github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
257259
github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
@@ -268,8 +270,12 @@ github.com/kurin/blazer v0.5.3/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt
268270
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
269271
github.com/mattn/go-ieproxy v0.0.9 h1:RvVbLiMv/Hbjf1gRaC2AQyzwbdVhdId7D2vPnXIml4k=
270272
github.com/mattn/go-ieproxy v0.0.9/go.mod h1:eF30/rfdQUO9EnzNIZQr0r9HiLMlZNCpJkHbmMuOAE0=
273+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
274+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
271275
github.com/miolini/datacounter v1.0.3 h1:tanOZPVblGXQl7/bSZWoEM8l4KK83q24qwQLMrO/HOA=
272276
github.com/miolini/datacounter v1.0.3/go.mod h1:C45dc2hBumHjDpEU64IqPwR6TDyPVpzOqqRTN7zmBUA=
277+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
278+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
273279
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
274280
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
275281
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -284,15 +290,20 @@ github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfx
284290
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
285291
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
286292
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
293+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
294+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
287295
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
288296
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
289297
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
298+
github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
299+
github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
290300
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
291301
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
292302
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
293303
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
294304
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
295305
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
306+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
296307
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
297308
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
298309
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -506,10 +517,15 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
506517
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
507518
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
508519
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
520+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
521+
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
522+
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
509523
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
510524
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
511525
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
512526
golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
527+
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
528+
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
513529
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
514530
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
515531
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

zfs/zfs.go

+9
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ func GetZFSProperty(ctx context.Context, prop, target string) (string, error) {
110110
return strings.TrimSpace(b.String()), nil
111111
}
112112

113+
// GetDatasetSize returns the size of the given ZFS dataset.
114+
func GetDatasetSize(ctx context.Context, dataset string) (uint64, error) {
115+
sizeStr, err := GetZFSProperty(ctx, "used", dataset)
116+
if err != nil {
117+
return 0, err
118+
}
119+
return strconv.ParseUint(sizeStr, 10, 64)
120+
}
121+
113122
// GetZFSSendCommand will return the send command to use for the given JobInfo
114123
func GetZFSSendCommand(ctx context.Context, j *files.JobInfo) *exec.Cmd {
115124
// Prepare the zfs send command

0 commit comments

Comments
 (0)