From ad4583fe85ac28bcf659c71e8a63820a908946e9 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 9 Mar 2022 13:35:52 +0300 Subject: [PATCH] [#1185] neofs-cli: Add progress bar to object put/get Signed-off-by: Evgenii Stratonikov --- cmd/neofs-cli/internal/client/client.go | 10 ++++++ cmd/neofs-cli/modules/object.go | 46 +++++++++++++++++++++++-- go.mod | 1 + go.sum | 10 ++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/cmd/neofs-cli/internal/client/client.go b/cmd/neofs-cli/internal/client/client.go index ffeb7a033..949074560 100644 --- a/cmd/neofs-cli/internal/client/client.go +++ b/cmd/neofs-cli/internal/client/client.go @@ -464,6 +464,13 @@ type GetObjectPrm struct { objectAddressPrm rawPrm payloadWriterPrm + headerCallback func(*object.Object) +} + +// SetHeaderCallback sets callback which is called on the object after the header is received, +// but before the payload is written. +func (p *GetObjectPrm) SetHeaderCallback(f func(*object.Object)) { + p.headerCallback = f } // GetObjectRes groups resulting values of GetObject operation. @@ -527,6 +534,9 @@ func GetObject(prm GetObjectPrm) (*GetObjectRes, error) { _, err = rdr.Close() return nil, fmt.Errorf("read object header: %w", err) } + if prm.headerCallback != nil { + prm.headerCallback(&hdr) + } sz := hdr.PayloadSize() if sz > maxPayloadBufferSize { diff --git a/cmd/neofs-cli/modules/object.go b/cmd/neofs-cli/modules/object.go index 31e86c6d4..de8121bce 100644 --- a/cmd/neofs-cli/modules/object.go +++ b/cmd/neofs-cli/modules/object.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/cheggaaa/pb" objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object" internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client" "github.com/nspcc-dev/neofs-sdk-go/checksum" @@ -123,6 +124,8 @@ const ( const putExpiresOnFlag = "expires-on" +const noProgressFlag = "no-progress" + var putExpiredOn uint64 func initObjectPutCmd() { @@ -141,6 +144,7 @@ func initObjectPutCmd() { flags.Bool("disable-filename", false, "Do not set well-known filename attribute") flags.Bool("disable-timestamp", false, "Do not set well-known timestamp attribute") flags.Uint64VarP(&putExpiredOn, putExpiresOnFlag, "e", 0, "Last epoch in the life of the object") + flags.Bool(noProgressFlag, false, "Do not show progress bar") } func initObjectDeleteCmd() { @@ -169,6 +173,7 @@ func initObjectGetCmd() { flags.String("file", "", "File to write object payload to. Default: stdout.") flags.String("header", "", "File to write header to. Default: stdout.") flags.Bool(rawFlag, false, rawFlagDesc) + flags.Bool(noProgressFlag, false, "Do not show progress bar") } func initObjectSearchCmd() { @@ -446,11 +451,31 @@ func putObject(cmd *cobra.Command, _ []string) { prepareSessionPrmWithOwner(cmd, sessionObjectCtxAddress, key, ownerID, &prm) prepareObjectPrm(cmd, &prm) prm.SetHeader(obj) - prm.SetPayloadReader(f) + + var p *pb.ProgressBar + + noProgress, _ := cmd.Flags().GetBool(noProgressFlag) + if noProgress { + prm.SetPayloadReader(f) + } else { + fi, err := f.Stat() + if err != nil { + cmd.PrintErrf("Failed to get file size, progress bar is disabled: %v\n", err) + prm.SetPayloadReader(f) + } else { + p = pb.New64(fi.Size()) + p.Output = cmd.OutOrStdout() + prm.SetPayloadReader(p.NewProxyReader(f)) + p.Start() + } + } res, err := internalclient.PutObject(prm) exitOnErr(cmd, errf("rpc error: %w", err)) + if p != nil { + p.Finish() + } cmd.Printf("[%s] Object successfully stored\n", filename) cmd.Printf(" ID: %s\n CID: %s\n", res.ID(), cid) } @@ -498,7 +523,21 @@ func getObject(cmd *cobra.Command, _ []string) { prepareSessionPrm(cmd, objAddr, &prm) prepareObjectPrmRaw(cmd, &prm) prm.SetAddress(objAddr) - prm.SetPayloadWriter(out) + + var p *pb.ProgressBar + noProgress, _ := cmd.Flags().GetBool(noProgressFlag) + + if filename == "" || noProgress { + prm.SetPayloadWriter(out) + } else { + p = pb.New64(0) + p.Output = cmd.OutOrStdout() + prm.SetPayloadWriter(p.NewProxyWriter(out)) + prm.SetHeaderCallback(func(o *object.Object) { + p.SetTotal64(int64(o.PayloadSize())) + p.Start() + }) + } res, err := internalclient.GetObject(prm) if err != nil { @@ -510,6 +549,9 @@ func getObject(cmd *cobra.Command, _ []string) { } if filename != "" { + if p != nil { + p.Finish() + } cmd.Printf("[%s] Object successfully saved\n", filename) } diff --git a/go.mod b/go.mod index 4663fa772..0ea2d1e1f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/nspcc-dev/neofs-node go 1.16 require ( + github.com/cheggaaa/pb v1.0.29 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 github.com/golang/snappy v0.0.3 // indirect diff --git a/go.sum b/go.sum index df3745fae..4e0e84926 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -128,6 +130,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= @@ -306,12 +310,17 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -701,6 +710,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=