From 94e277d7596346c122aded2c1c2200fab5d5ff8e Mon Sep 17 00:00:00 2001 From: a-roussos Date: Fri, 9 Feb 2018 22:48:32 +0200 Subject: [PATCH] about: add new command 'about' to get quota info from a remote Implemented for drive only. Relates to #1138 and #1564. --- backend/cache/cache.go | 13 ++++++++++ backend/drive/drive.go | 23 +++++++++++++++++ cmd/about/about.go | 27 ++++++++++++++++++++ cmd/all/all.go | 1 + docs/content/docs.md | 1 + docs/content/drive.md | 8 ++++++ docs/content/overview.md | 50 +++++++++++++++++++++---------------- fs/fs.go | 15 +++++++++++ fs/operations/operations.go | 13 ++++++++++ 9 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 cmd/about/about.go diff --git a/backend/cache/cache.go b/backend/cache/cache.go index da180d902..c0e7ec2ea 100644 --- a/backend/cache/cache.go +++ b/backend/cache/cache.go @@ -1413,6 +1413,18 @@ func (f *Fs) CleanUp() error { return do() } +// About gets quota information from the Fs +func (f *Fs) About() error { + f.CleanUpCache(false) + + do := f.Fs.Features().About + if do == nil { + return nil + } + + return do() +} + // Stats returns stats about the cache storage func (f *Fs) Stats() (map[string]map[string]interface{}, error) { return f.cache.Stats() @@ -1551,4 +1563,5 @@ var ( _ fs.Wrapper = (*Fs)(nil) _ fs.ListRer = (*Fs)(nil) _ fs.ChangeNotifier = (*Fs)(nil) + _ fs.Abouter = (*Fs)(nil) ) diff --git a/backend/drive/drive.go b/backend/drive/drive.go index 88c8eb601..94e8e19ae 100644 --- a/backend/drive/drive.go +++ b/backend/drive/drive.go @@ -1050,6 +1050,29 @@ func (f *Fs) CleanUp() error { return nil } +// About gets quota information +func (f *Fs) About() error { + var about *drive.About + var err error + err = f.pacer.Call(func() (bool, error) { + about, err = f.svc.About.Get().Fields("storageQuota").Do() + return shouldRetry(err) + }) + if err != nil { + fs.Errorf(f, "Failed to get Drive storageQuota: %v", err) + return nil + } + quota := float64(about.StorageQuota.Limit) / (1 << 30) + usagetotal := float64(about.StorageQuota.Usage) / (1 << 30) + usagedrive := float64(about.StorageQuota.UsageInDrive) / (1 << 30) + usagetrash := float64(about.StorageQuota.UsageInDriveTrash) / (1 << 30) + fmt.Printf("Quota: %.0f GiB | Used: %.1f GiB (Trash: %.1f GiB) | Available: %.1f GiB | Usage: %d%%\n", + quota, usagedrive, usagetrash, quota-usagedrive, int((usagedrive/quota)*100)) + fmt.Printf("Space used in other Google services (such as Gmail): %.2f GiB\n", + usagetotal-usagedrive) + return nil +} + // Move src to this remote using server side move operations. // // This is stored with the remote path given diff --git a/cmd/about/about.go b/cmd/about/about.go new file mode 100644 index 000000000..b5c58358a --- /dev/null +++ b/cmd/about/about.go @@ -0,0 +1,27 @@ +package about + +import ( + "github.com/ncw/rclone/cmd" + "github.com/ncw/rclone/fs/operations" + "github.com/spf13/cobra" +) + +func init() { + cmd.Root.AddCommand(commandDefintion) +} + +var commandDefintion = &cobra.Command{ + Use: "about remote:", + Short: `Get quota information from the remote.`, + Long: ` +Get quota information from the remote, like bytes used/free/quota and bytes +used in the trash. Not supported by all remotes. +`, + Run: func(command *cobra.Command, args []string) { + cmd.CheckArgs(1, 1, command, args) + fsrc := cmd.NewFsSrc(args) + cmd.Run(true, false, command, func() error { + return operations.About(fsrc) + }) + }, +} diff --git a/cmd/all/all.go b/cmd/all/all.go index 388a86c8a..79b6900be 100644 --- a/cmd/all/all.go +++ b/cmd/all/all.go @@ -4,6 +4,7 @@ package all import ( // Active commands _ "github.com/ncw/rclone/cmd" + _ "github.com/ncw/rclone/cmd/about" _ "github.com/ncw/rclone/cmd/authorize" _ "github.com/ncw/rclone/cmd/cachestats" _ "github.com/ncw/rclone/cmd/cat" diff --git a/docs/content/docs.md b/docs/content/docs.md index 1f86f798e..363302353 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -98,6 +98,7 @@ The main rclone commands with most used first * [rclone moveto](/commands/rclone_moveto/) - Move file or directory from source to dest. * [rclone obscure](/commands/rclone_obscure/) - Obscure password for use in the rclone.conf * [rclone cryptcheck](/commands/rclone_cryptcheck/) - Check the integrity of a crypted remote. +* [rclone about](/commands/rclone_about/) - Get quota information from the remote. See the [commands index](/commands/) for the full list. diff --git a/docs/content/drive.md b/docs/content/drive.md index fa15614c1..00a751f29 100644 --- a/docs/content/drive.md +++ b/docs/content/drive.md @@ -338,6 +338,14 @@ If you wish to empty your trash you can use the `rclone cleanup remote:` command which will permanently delete all your trashed files. This command does not take any path arguments. +### Quota information ### + +To view your current quota you can use the `rclone about remote:` +command which will display your usage limit (quota), the usage in Google +Drive, the size of all files in the Trash and the space used by other +Google services such as Gmail. This command does not take any path +arguments. + ### Specific options ### Here are the command line options specific to this cloud storage diff --git a/docs/content/overview.md b/docs/content/overview.md index 5851e3541..67e3458cd 100644 --- a/docs/content/overview.md +++ b/docs/content/overview.md @@ -119,27 +119,27 @@ All the remotes support a basic set of features, but there are some optional features supported by some remotes used to make some operations more efficient. -| Name | Purge | Copy | Move | DirMove | CleanUp | ListR | StreamUpload | LinkSharing | -| ---------------------------- |:-----:|:----:|:----:|:-------:|:-------:|:-----:|:------------:|:------------:| -| Amazon Drive | Yes | No | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Amazon S3 | No | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Backblaze B2 | No | No | No | No | Yes | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Box | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Dropbox | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes | Yes | -| FTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Google Cloud Storage | Yes | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Google Drive | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | -| HTTP | No | No | No | No | No | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Hubic | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Microsoft Azure Blob Storage | Yes | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Microsoft OneDrive | Yes | Yes | Yes | No [#197](https://github.com/ncw/rclone/issues/197) | No [#575](https://github.com/ncw/rclone/issues/575) | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Openstack Swift | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| pCloud | Yes | Yes | Yes | Yes | Yes | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| QingStor | No | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| SFTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ‡ | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| Yandex Disk | Yes | No | No | No | Yes | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | -| The local filesystem | Yes | No | Yes | Yes | No | No | Yes | No | +| Name | Purge | Copy | Move | DirMove | CleanUp | ListR | StreamUpload | LinkSharing | About | +| ---------------------------- |:-----:|:----:|:----:|:-------:|:-------:|:-----:|:------------:|:------------:|:-----:| +| Amazon Drive | Yes | No | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Amazon S3 | No | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Backblaze B2 | No | No | No | No | Yes | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Box | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Dropbox | Yes | Yes | Yes | Yes | No [#575](https://github.com/ncw/rclone/issues/575) | No | Yes | Yes | No | +| FTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Google Cloud Storage | Yes | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Google Drive | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes | +| HTTP | No | No | No | No | No | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Hubic | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Microsoft Azure Blob Storage | Yes | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Microsoft OneDrive | Yes | Yes | Yes | No [#197](https://github.com/ncw/rclone/issues/197) | No [#575](https://github.com/ncw/rclone/issues/575) | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Openstack Swift | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| pCloud | Yes | Yes | Yes | Yes | Yes | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| QingStor | No | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| SFTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ‡ | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| Yandex Disk | Yes | No | No | No | Yes | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No | +| The local filesystem | Yes | No | Yes | Yes | No | No | Yes | No | No | ### Purge ### @@ -202,3 +202,11 @@ file to local disk first, e.g. `rclone rcat`. Sets the necessary permissions on a file or folder and prints a link that allows others to access them, even if they don't have an account on the particular cloud provider. + +### About ### + +This is used to fetch quota information from the remote, like bytes +used/free/quota and bytes used in the trash. + +If the server can't do `About` then `rclone about` will return an +error. diff --git a/fs/fs.go b/fs/fs.go index e080f922a..63797f989 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -377,6 +377,9 @@ type Features struct { // Don't implement this unless you have a more efficient way // of listing recursively that doing a directory traversal. ListR ListRFn + + // Get quota information from the Fs + About func() error } // Disable nil's out the named feature. If it isn't found then it @@ -466,6 +469,9 @@ func (ft *Features) Fill(f Fs) *Features { if do, ok := f.(ListRer); ok { ft.ListR = do.ListR } + if do, ok := f.(Abouter); ok { + ft.About = do.About + } return ft.DisableList(Config.DisableFeatures) } @@ -519,6 +525,9 @@ func (ft *Features) Mask(f Fs) *Features { if mask.ListR == nil { ft.ListR = nil } + if mask.About == nil { + ft.About = nil + } return ft.DisableList(Config.DisableFeatures) } @@ -706,6 +715,12 @@ type RangeSeeker interface { RangeSeek(offset int64, whence int, length int64) (int64, error) } +// Abouter is an optional interface for Fs +type Abouter interface { + // About gets quota information from the Fs + About() error +} + // ObjectsChan is a channel of Objects type ObjectsChan chan Object diff --git a/fs/operations/operations.go b/fs/operations/operations.go index 6882e06c9..c0f5f4457 100644 --- a/fs/operations/operations.go +++ b/fs/operations/operations.go @@ -1049,6 +1049,19 @@ func CleanUp(f fs.Fs) error { return doCleanUp() } +// About gets quota information from the remote +func About(f fs.Fs) error { + doAbout := f.Features().About + if doAbout == nil { + return errors.Errorf("%v doesn't support about", f) + } + if fs.Config.DryRun { + fs.Logf(f, "Not running about as --dry-run set") + return nil + } + return doAbout() +} + // wrap a Reader and a Closer together into a ReadCloser type readCloser struct { io.Reader