diff --git a/.gitignore b/.gitignore index 839a56d..b46dc54 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) vendor/ + +builds/ \ No newline at end of file diff --git a/README.md b/README.md index cac54bf..410bf30 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,15 @@ gclone ==== -English [简体中文](https://github.com/donwa/gclone/blob/master/README_zh.md) +English [简体中文](https://github.com/tomyummmm/gclone/blob/master/README_zh.md) A modified version of the [rclone](//github.com/rclone/rclone) -Provide dynamic replacement sa file support for google drive operation +Provide dynamic replacement SA file support for google drive operation. +All other functions are the same as rclone. +This version includes PR#38 in original gclone [filepath end with slash /](https://github.com/donwa/gclone/pull/38) -## installation -``` -bash <(wget -qO- https://git.io/gclone.sh) -``` ``` // View version information @@ -19,34 +17,67 @@ gclone version ``` ## Instructions -### 1.service_account_file_path Configuration -add `service_account_file_path` Configuration.For dynamic replacement service_account_file(sa file). Replace configuration when `rateLimitExceeded` error occurs +### 1. service_account_file_path Configuration +Add `service_account_file_path` Configuration. For dynamic replacement service_account_file(SA file). Replaces SA file when `rateLimitExceeded` error occurs to bypass 750GB limit. + `rclone.conf` example: ``` [gc] type = drive scope = drive service_account_file = /root/accounts/1.json -service_account_file_path = /root/accounts/ +service_account_file_path = /root/accounts/ <------- (Important) Add this in configuration root_folder_id = root ``` -`/root/accounts/` Folder contains multiple access and edit permissions ***service account file(*.json)***. - -### 2.Support incoming id +`/root/accounts/` Folder contains multiple access and edit permissions ***service account file (x.json)***. +Once set up, when `gclone` sees `rateLimitExceeded` error, it will automatically change SA file, seamlessly bypassing the limit in real-time. + +### 2. Use gc-local.bat provided for ease of use and operation of gclone. (Windows only) +Read the instructions [here](https://telegra.ph/How-to-use-GC-Remote-and-Localbat-08-21) written by me. +You can find the original [repository](https://github.com/tomyummmm/gclone-batch-file) written by me. + +## Features +### 1. Supports Folder ID If the original rclone is across team disks or shared folders, multiple configuration drive letters are required for operation. -gclone supports incoming id operation + +gclone supports Folder ID operations ``` -gclone copy gc:{folde_id1} gc:{folde_id2} --drive-server-side-across-configs +gclone copy gc:{folder_id1} gc:{folder_id2} --drive-server-side-across-configs ``` -folde_id1 can be:Common directory, shared directory, team disk. +folder_id1 can be: common directory, shared directory, team drive. ``` -gclone copy gc:{folde_id1} gc:{folde_id2}/media/ --drive-server-side-across-configs - +gclone copy gc:{folder_id1} gc:{folder_id2}/media/ --drive-server-side-across-configs ``` ``` -gclone copy gc:{share_fiel_id} gc:{folde_id2} --drive-server-side-across-configs +gclone copy gc:{shared_folder_id} gc:{folder_id2} --drive-server-side-across-configs ``` +### 2. Direct Copy with file ID +`id` operations: common directory, shared directory, team drive. +``` +gclone copy gc:{shared_file_id} gc:{folder_id2} --drive-server-side-across-configs +``` + +Supports {Folder ID} proceeding filepaths +``` +gclone copy gc:{shared_file_id} gc:{Team Drive ID}/media/ --drive-server-side-across-configs +``` + + +## Code edited +### All additional code that makes gclone run is encased. Most of the code edited is in drive.go inside \backend\drive +``` +//------------------------------------------------------------ +// 如果存在 ServiceAccountFilePath,调用 changeSvc, 重试 +// If ServiceAccountFilePath exists, call changeSvc and try again +if(f.opt.ServiceAccountFilePath != ""){ + f.waitChangeSvc.Lock() + f.changeSvc() + f.waitChangeSvc.Unlock() + return true, err +} +//------------------------------------------------------------ +``` \ No newline at end of file diff --git a/README_zh.md b/README_zh.md index 3a9aebf..c78bf89 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,16 +1,13 @@ gclone ==== - [English](https://github.com/donwa/gclone/blob/master/README.md) 简体中文 +简体中文 [English](https://github.com/tomyummmm/gclone/blob/master/README.md) 一个[rclone](//github.com/rclone/rclone) 的修改版. 为Google Drive操作增加自动切换账户和命令行根目录id操作支持. 其他功能与原版rclone相同. -## 安装 -``` -bash <(wget -qO- https://git.io/gclone.sh) -``` + ``` // 查看版本信息 @@ -20,6 +17,7 @@ gclone version ## 操作说明 ### 1.service_account_file_path配置 添加`service_account_file_path`配置.用于动态替换service_account_file(sa文件).实现`rateLimitExceeded`错误时,替换当前用户,绕过750G限制. + `rclone.conf`文件示例: ``` [gc] @@ -29,7 +27,7 @@ service_account_file = /root/accounts/1.json service_account_file_path = /root/accounts/ <------- (核心)添加了这个配置 root_folder_id = root ``` -其中`/root/accounts/`文件夹中存放了多个访问和编辑权限相同的service account file(*.json). +其中`/root/accounts/`文件夹中存放了多个访问和编辑权限相同的***service account file (x.json)***. 配置完成后.只要是`gclone`对`gc:`进行操作,出现`rateLimitExceeded`错误时,都会自动更换sa文件,实现无缝绕过限制. diff --git a/backend/all/all.go b/backend/all/all.go index 4ead136..a0902bc 100644 --- a/backend/all/all.go +++ b/backend/all/all.go @@ -10,7 +10,7 @@ import ( _ "github.com/rclone/rclone/backend/cache" _ "github.com/rclone/rclone/backend/chunker" _ "github.com/rclone/rclone/backend/crypt" - _ "github.com/donwa/gclone/backend/drive" + _ "github.com/tomyummmm/gclone/backend/drive" _ "github.com/rclone/rclone/backend/dropbox" _ "github.com/rclone/rclone/backend/fichier" _ "github.com/rclone/rclone/backend/ftp" @@ -31,10 +31,12 @@ import ( _ "github.com/rclone/rclone/backend/putio" _ "github.com/rclone/rclone/backend/qingstor" _ "github.com/rclone/rclone/backend/s3" + _ "github.com/rclone/rclone/backend/seafile" _ "github.com/rclone/rclone/backend/sftp" _ "github.com/rclone/rclone/backend/sharefile" _ "github.com/rclone/rclone/backend/sugarsync" _ "github.com/rclone/rclone/backend/swift" + _ "github.com/rclone/rclone/backend/tardigrade" _ "github.com/rclone/rclone/backend/union" _ "github.com/rclone/rclone/backend/webdav" _ "github.com/rclone/rclone/backend/yandex" diff --git a/backend/drive/drive.go b/backend/drive/drive.go index 754e5f9..6f2c1ae 100644 --- a/backend/drive/drive.go +++ b/backend/drive/drive.go @@ -18,18 +18,18 @@ import ( "math/rand" "mime" "net/http" - "net/url" - "os" "path" "sort" "strconv" "strings" "sync" + "sync/atomic" "text/template" "time" "github.com/pkg/errors" "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/cache" "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configstruct" @@ -37,9 +37,11 @@ import ( "github.com/rclone/rclone/fs/fserrors" "github.com/rclone/rclone/fs/fshttp" "github.com/rclone/rclone/fs/hash" + "github.com/rclone/rclone/fs/operations" "github.com/rclone/rclone/fs/walk" "github.com/rclone/rclone/lib/dircache" "github.com/rclone/rclone/lib/encoder" + "github.com/rclone/rclone/lib/env" "github.com/rclone/rclone/lib/oauthutil" "github.com/rclone/rclone/lib/pacer" "github.com/rclone/rclone/lib/readers" @@ -55,6 +57,8 @@ const ( rcloneClientID = "202264815644.apps.googleusercontent.com" rcloneEncryptedClientSecret = "eX8GpZTVx3vxMWVkuuBdDWmAUE6rGhTwVrvG9GhllYccSdj2-mvHVg" driveFolderType = "application/vnd.google-apps.folder" + shortcutMimeType = "application/vnd.google-apps.shortcut" + shortcutMimeTypeDangling = "application/vnd.google-apps.shortcut.dangling" // synthetic mime type for internal use timeFormatIn = time.RFC3339 timeFormatOut = "2006-01-02T15:04:05.000000000Z07:00" defaultMinSleep = fs.Duration(100 * time.Millisecond) @@ -66,7 +70,9 @@ const ( // 1<<18 is the minimum size supported by the Google uploader, and there is no maximum. minChunkSize = 256 * fs.KibiByte defaultChunkSize = 8 * fs.MebiByte - partialFields = "id,name,size,md5Checksum,trashed,modifiedTime,createdTime,mimeType,parents,webViewLink" + partialFields = "id,name,size,md5Checksum,trashed,explicitlyTrashed,modifiedTime,createdTime,mimeType,parents,webViewLink,shortcutDetails,exportLinks" + listRGrouping = 50 // number of IDs to search at once when using ListR + listRInputBuffer = 1000 // size of input buffer when using ListR ) // Globals @@ -152,12 +158,24 @@ func driveScopesContainsAppFolder(scopes []string) bool { return false } +func driveOAuthOptions() []fs.Option { + opts := []fs.Option{} + for _, opt := range oauthutil.SharedOptions { + if opt.Name == config.ConfigClientID { + opt.Help = "Google Application Client Id\nSetting your own is recommended.\nSee https://rclone.org/drive/#making-your-own-client-id for how to create your own.\nIf you leave this blank, it will use an internal key which is low performance." + } + opts = append(opts, opt) + } + return opts +} + // Register with Fs func init() { fs.Register(&fs.RegInfo{ Name: "drive", Description: "Google Drive", NewFs: NewFs, + CommandHelp: commandHelp, Config: func(name string, m configmap.Mapper) { ctx := context.TODO() // Parse config into Options struct @@ -176,7 +194,7 @@ func init() { } if opt.ServiceAccountFile == "" { - err = oauthutil.Config("drive", name, m, driveConfig) + err = oauthutil.Config("drive", name, m, driveConfig, nil) if err != nil { log.Fatalf("Failed to configure token: %v", err) } @@ -186,13 +204,7 @@ func init() { log.Fatalf("Failed to configure team drive: %v", err) } }, - Options: []fs.Option{{ - Name: config.ConfigClientID, - Help: "Google Application Client Id\nSetting your own is recommended.\nSee https://rclone.org/drive/#making-your-own-client-id for how to create your own.\nIf you leave this blank, it will use an internal key which is low performance.", - }, { - Name: config.ConfigClientSecret, - Help: "Google Application Client Secret\nSetting your own is recommended.", - }, { + Options: append(driveOAuthOptions(), []fs.Option{{ Name: "scope", Help: "Scope that rclone should use when requesting access from drive.", Examples: []fs.OptionExample{{ @@ -218,17 +230,16 @@ Leave blank normally. Fill in to access "Computers" folders (see docs), or for rclone to use a non root folder as its starting point. - -Note that if this is blank, the first time rclone runs it will fill it -in with the ID of the root folder. `, }, { Name: "service_account_file", - Help: "Service Account Credentials JSON file path \nLeave blank normally.\nNeeded only if you want use SA instead of interactive login.", + Help: "Service Account Credentials JSON file path \nLeave blank normally.\nNeeded only if you want use SA instead of interactive login." + env.ShellExpandHelp, }, { + //------------------------------------------------------------ Name: "service_account_file_path", - Help: "Service Account Credentials JSON file path .\n", - },{ + Help: "Service Account Credentials JSON file path.\n" + env.ShellExpandHelp, + //------------------------------------------------------------ + }, { Name: "service_account_credentials", Help: "Service Account Credentials JSON blob\nLeave blank normally.\nNeeded only if you want use SA instead of interactive login.", Hide: fs.OptionHideConfigurator, @@ -264,7 +275,7 @@ videos. Setting this flag will cause Google photos and videos to return a blank MD5 checksum. -Google photos are identifed by being in the "photos" space. +Google photos are identified by being in the "photos" space. Corrupted checksums are caused by Google modifying the image/video but not updating the checksum.`, @@ -278,14 +289,19 @@ Instructs rclone to operate on your "Shared with me" folder (where Google Drive lets you access the files and folders others have shared with you). -This works both with the "list" (lsd, lsl, etc) and the "copy" -commands (copy, sync, etc), and with all other commands too.`, +This works both with the "list" (lsd, lsl, etc.) and the "copy" +commands (copy, sync, etc.), and with all other commands too.`, Advanced: true, }, { Name: "trashed_only", Default: false, Help: "Only show files that are in the trash.\nThis will show trashed files in their original directory structure.", Advanced: true, + }, { + Name: "starred_only", + Default: false, + Help: "Only show files that are starred.", + Advanced: true, }, { Name: "formats", Default: "", @@ -349,22 +365,13 @@ date is used.`, }, { Name: "impersonate", Default: "", - Help: "Impersonate this user when using a service account.", + Help: `Impersonate this user when using a service account.`, Advanced: true, }, { Name: "alternate_export", Default: false, - Help: `Use alternate export URLs for google documents export., - -If this option is set this instructs rclone to use an alternate set of -export URLs for drive documents. Users have reported that the -official export URLs can't export large documents, whereas these -unofficial ones can. - -See rclone issue [#2243](https://github.com/rclone/rclone/issues/2243) for background, -[this google drive issue](https://issuetracker.google.com/issues/36761333) and -[this helpful post](https://www.labnol.org/internet/direct-links-for-google-drive/28356/).`, - Advanced: true, + Help: "Deprecated: no longer needed", + Hide: fs.OptionHideBoth, }, { Name: "upload_cutoff", Default: defaultChunkSize, @@ -401,7 +408,7 @@ will download it anyway.`, Default: false, Help: `Show sizes as storage quota usage, not actual size. -Show the size of a file as the the storage quota used. This is the +Show the size of a file as the storage quota used. This is the current version plus any older versions that have been set to keep forever. @@ -433,9 +440,9 @@ need to use --ignore size also.`, }, { Name: "server_side_across_configs", Default: false, - Help: `Allow server side operations (eg copy) to work across different drive configs. + Help: `Allow server-side operations (e.g. copy) to work across different drive configs. -This can be useful if you wish to do a server side copy between two +This can be useful if you wish to do a server-side copy between two different Google drives. Note that this isn't enabled by default because it isn't easy to tell if it will work between any two configurations.`, @@ -471,6 +478,16 @@ Google don't document so it may break in the future. See: https://github.com/rclone/rclone/issues/3857 `, Advanced: true, + }, { + Name: "skip_shortcuts", + Help: `If set skip shortcut files + +Normally rclone dereferences shortcut files making them appear as if +they are the original file (see [the shortcuts section](#shortcuts)). +If this flag is set then rclone will ignore shortcut files completely. +`, + Advanced: true, + Default: false, }, { Name: config.ConfigEncoding, Help: config.ConfigEncodingHelp, @@ -478,7 +495,7 @@ See: https://github.com/rclone/rclone/issues/3857 // Encode invalid UTF-8 bytes as json doesn't handle them properly. // Don't encode / as it's a valid name character in drive. Default: encoder.EncodeInvalidUtf8, - }}, + }}...), }) // register duplicate MIME types first @@ -500,8 +517,10 @@ type Options struct { Scope string `config:"scope"` RootFolderID string `config:"root_folder_id"` ServiceAccountFile string `config:"service_account_file"` - // 添加一个变量 + //------------------------------------------------------------ + // 添加一个变量 Add a Variable ServiceAccountFilePath string `config:"service_account_file_path"` + //------------------------------------------------------------ ServiceAccountCredentials string `config:"service_account_credentials"` TeamDriveID string `config:"team_drive"` AuthOwnerOnly bool `config:"auth_owner_only"` @@ -510,6 +529,7 @@ type Options struct { SkipChecksumGphotos bool `config:"skip_checksum_gphotos"` SharedWithMe bool `config:"shared_with_me"` TrashedOnly bool `config:"trashed_only"` + StarredOnly bool `config:"starred_only"` Extensions string `config:"formats"` ExportExtensions string `config:"export_formats"` ImportExtensions string `config:"import_formats"` @@ -518,7 +538,6 @@ type Options struct { UseSharedDate bool `config:"use_shared_date"` ListChunk int64 `config:"list_chunk"` Impersonate string `config:"impersonate"` - AlternateExport bool `config:"alternate_export"` UploadCutoff fs.SizeSuffix `config:"upload_cutoff"` ChunkSize fs.SizeSuffix `config:"chunk_size"` AcknowledgeAbuse bool `config:"acknowledge_abuse"` @@ -530,6 +549,7 @@ type Options struct { ServerSideAcrossConfigs bool `config:"server_side_across_configs"` DisableHTTP2 bool `config:"disable_http2"` StopOnUploadLimit bool `config:"stop_on_upload_limit"` + SkipShortcuts bool `config:"skip_shortcuts"` Enc encoder.MultiEncoder `config:"encoding"` } @@ -548,12 +568,18 @@ type Fs struct { exportExtensions []string // preferred extensions to download docs importMimeTypes []string // MIME types to convert to docs isTeamDrive bool // true if this is a team drive + fileFields googleapi.Field // fields to fetch file info with + m configmap.Mapper + grouping int32 // number of IDs to search at once in ListR - read with atomic + listRmu *sync.Mutex // protects listRempties + listRempties map[string]struct{} // IDs of supposedly empty directories which triggered grouping disable //------------------------------------------------------------ + //Add more variables ServiceAccountFiles map[string]int waitChangeSvc sync.Mutex FileObj *fs.Object FileName string - + //------------------------------------------------------------ } type baseObject struct { @@ -563,6 +589,7 @@ type baseObject struct { modifiedDate string // RFC3339 time it was last modified mimeType string // The object MIME type bytes int64 // size of the object + parents int // number of parents } type documentObject struct { baseObject @@ -623,37 +650,49 @@ func (f *Fs) shouldRetry(err error) (bool, error) { if len(gerr.Errors) > 0 { reason := gerr.Errors[0].Reason if reason == "rateLimitExceeded" || reason == "userRateLimitExceeded" { + //------------------------------------------------------------ // 如果存在 ServiceAccountFilePath,调用 changeSvc, 重试 + // If ServiceAccountFilePath exists, call changeSvc and try again if(f.opt.ServiceAccountFilePath != ""){ f.waitChangeSvc.Lock() f.changeSvc() f.waitChangeSvc.Unlock() return true, err } + //------------------------------------------------------------ if f.opt.StopOnUploadLimit && gerr.Errors[0].Message == "User rate limit exceeded." { fs.Errorf(f, "Received upload limit error: %v", err) return false, fserrors.FatalError(err) } return true, err + } else if f.opt.StopOnUploadLimit && reason == "teamDriveFileLimitExceeded" { + fs.Errorf(f, "Received team drive file limit error: %v", err) + return false, fserrors.FatalError(err) } } } return false, err } +//------------------------------------------------------------ // 替换 f.svc 函数 +// Replace f.svc function func (f *Fs) changeSvc(){ opt := &f.opt; - /** - * 获取sa文件列表 - */ + // 获取sa文件列表 + // Obtain list of SA if(opt.ServiceAccountFilePath != "" && len(f.ServiceAccountFiles) == 0){ f.ServiceAccountFiles = make(map[string]int) - dir_list, e := ioutil.ReadDir(opt.ServiceAccountFilePath) + dir_list, e := ioutil.ReadDir(env.ShellExpand(opt.ServiceAccountFilePath)) if e != nil { - fmt.Println("read ServiceAccountFilePath Files error") + fmt.Println("ServiceAccountFilePath: ", opt.ServiceAccountFilePath) + fmt.Println("ShellExpand ServiceAccountFilePath: ", env.ShellExpand(opt.ServiceAccountFilePath)) + fmt.Println("read ServiceAccountFilePath error") } for i, v := range dir_list { + if (!strings.HasSuffix(opt.ServiceAccountFilePath, "/")) { + opt.ServiceAccountFilePath += "/" + } filePath := fmt.Sprintf("%s%s", opt.ServiceAccountFilePath, v.Name()) if(".json" == path.Ext(filePath)){ //fmt.Println(filePath) @@ -662,12 +701,12 @@ func (f *Fs) changeSvc(){ } } // 如果读取文件夹后还是0 , 退出 + // If it is still 0 after reading the folder, exit if(len(f.ServiceAccountFiles) <= 0){ return ; } - /** - * 从sa文件列表 随机取一个,并删除列表中的元素 - */ + // 从sa文件列表 随机取一个,并删除列表中的元素 + // Randomly select a SA from the list, remove it from the list. r := rand.Intn(len(f.ServiceAccountFiles)) for k := range f.ServiceAccountFiles { if r == 0 { @@ -676,12 +715,12 @@ func (f *Fs) changeSvc(){ r-- } // 从库存中删除 + // Remove the SA from the array delete(f.ServiceAccountFiles, opt.ServiceAccountFile) - /** - * 创建 client 和 svc - */ - loadedCreds, _ := ioutil.ReadFile(os.ExpandEnv(opt.ServiceAccountFile)) + //创建 client 和 svc + // Create client and svc + loadedCreds, _ := ioutil.ReadFile(env.ShellExpand(opt.ServiceAccountFile)) opt.ServiceAccountCredentials = string(loadedCreds) oAuthClient, err := getServiceAccountClient(opt, []byte(opt.ServiceAccountCredentials)) if err != nil { @@ -691,6 +730,7 @@ func (f *Fs) changeSvc(){ f.svc, err = drive.New(f.client) fmt.Println("gclone sa file:", opt.ServiceAccountFile) } +//------------------------------------------------------------ // parseParse parses a drive 'url' func parseDrivePath(path string) (root string, err error) { @@ -712,17 +752,21 @@ func containsString(slice []string, s string) bool { return false } -// getRootID returns the canonical ID for the "root" ID -func (f *Fs) getRootID() (string, error) { - var info *drive.File - var err error - err = f.pacer.CallNoRetry(func() (bool, error) { - info, err = f.svc.Files.Get("root"). - Fields("id"). +// getFile returns drive.File for the ID passed and fields passed in +func (f *Fs) getFile(ID string, fields googleapi.Field) (info *drive.File, err error) { + err = f.pacer.Call(func() (bool, error) { + info, err = f.svc.Files.Get(ID). + Fields(fields). SupportsAllDrives(true). Do() return f.shouldRetry(err) }) + return info, err +} + +// getRootID returns the canonical ID for the "root" ID +func (f *Fs) getRootID() (string, error) { + info, err := f.getFile("root", "id") if err != nil { return "", errors.Wrap(err, "couldn't find root directory ID") } @@ -734,16 +778,16 @@ func (f *Fs) getRootID() (string, error) { // If the user fn ever returns true then it early exits with found = true // // Search params: https://developers.google.com/drive/search-parameters -func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directoriesOnly, filesOnly, includeAll bool, fn listFn) (found bool, err error) { - //f.changeSvc() +func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directoriesOnly, filesOnly, trashedOnly, includeAll bool, fn listFn) (found bool, err error) { var query []string if !includeAll { - q := "trashed=" + strconv.FormatBool(f.opt.TrashedOnly) + q := "trashed=" + strconv.FormatBool(trashedOnly) if f.opt.TrashedOnly { q = fmt.Sprintf("(mimeType='%s' or %s)", driveFolderType, q) } query = append(query, q) } + // Search with sharedWithMe will always return things listed in "Shared With Me" (without any parents) // We must not filter with parent when we try list "ROOT" with drive-shared-with-me // If we need to list file inside those shared folders, we must search it without sharedWithMe @@ -755,8 +799,16 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie if parentsQuery.Len() > 1 { _, _ = parentsQuery.WriteString(" or ") } - if f.opt.SharedWithMe && dirID == f.rootFolderID { - _, _ = parentsQuery.WriteString("sharedWithMe=true") + if (f.opt.SharedWithMe || f.opt.StarredOnly) && dirID == f.rootFolderID { + if f.opt.SharedWithMe { + _, _ = parentsQuery.WriteString("sharedWithMe=true") + } + if f.opt.StarredOnly { + if f.opt.SharedWithMe { + _, _ = parentsQuery.WriteString(" and ") + } + _, _ = parentsQuery.WriteString("starred=true") + } } else { _, _ = fmt.Fprintf(parentsQuery, "'%s' in parents", dirID) } @@ -789,7 +841,7 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie query = append(query, titleQuery.String()) } if directoriesOnly { - query = append(query, fmt.Sprintf("mimeType='%s'", driveFolderType)) + query = append(query, fmt.Sprintf("(mimeType='%s' or mimeType='%s')", driveFolderType, shortcutMimeType)) } if filesOnly { query = append(query, fmt.Sprintf("mimeType!='%s'", driveFolderType)) @@ -813,22 +865,7 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie list.Spaces("appDataFolder") } - var fields = partialFields - - if f.opt.AuthOwnerOnly { - fields += ",owners" - } - if f.opt.UseSharedDate { - fields += ",sharedWithMeTime" - } - if f.opt.SkipChecksumGphotos { - fields += ",spaces" - } - if f.opt.SizeAsQuota { - fields += ",quotaBytesUsed" - } - - fields = fmt.Sprintf("files(%s),nextPageToken,incompleteSearch", fields) + fields := fmt.Sprintf("files(%s),nextPageToken,incompleteSearch", f.fileFields) OUTER: for { @@ -845,6 +882,24 @@ OUTER: } for _, item := range files.Files { item.Name = f.opt.Enc.ToStandardName(item.Name) + if isShortcut(item) { + // ignore shortcuts if directed + if f.opt.SkipShortcuts { + continue + } + // skip file shortcuts if directory only + if directoriesOnly && item.ShortcutDetails.TargetMimeType != driveFolderType { + continue + } + // skip directory shortcuts if file only + if filesOnly && item.ShortcutDetails.TargetMimeType == driveFolderType { + continue + } + item, err = f.resolveShortcut(item) + if err != nil { + return false, errors.Wrap(err, "list") + } + } // Check the case of items is correct since // the `=` operator is case insensitive. if title != "" && title != item.Name { @@ -963,55 +1018,32 @@ func configTeamDrive(ctx context.Context, opt *Options, m configmap.Mapper, name if !config.Confirm(false) { return nil } - client, err := createOAuthClient(opt, name, m) + f, err := newFs(name, "", m) if err != nil { - return errors.Wrap(err, "config team drive failed to create oauth client") - } - svc, err := drive.New(client) - if err != nil { - return errors.Wrap(err, "config team drive failed to make drive client") + return errors.Wrap(err, "failed to make Fs to list teamdrives") } fmt.Printf("Fetching team drive list...\n") - var driveIDs, driveNames []string - listTeamDrives := svc.Teamdrives.List().PageSize(100) - listFailed := false - var defaultFs Fs // default Fs with default Options - for { - var teamDrives *drive.TeamDriveList - err = newPacer(opt).Call(func() (bool, error) { - teamDrives, err = listTeamDrives.Context(ctx).Do() - return defaultFs.shouldRetry(err) - }) - if err != nil { - fmt.Printf("Listing team drives failed: %v\n", err) - listFailed = true - break - } - for _, drive := range teamDrives.TeamDrives { - driveIDs = append(driveIDs, drive.Id) - driveNames = append(driveNames, drive.Name) - } - if teamDrives.NextPageToken == "" { - break - } - listTeamDrives.PageToken(teamDrives.NextPageToken) + teamDrives, err := f.listTeamDrives(ctx) + if err != nil { + return err } - var driveID string - if !listFailed && len(driveIDs) == 0 { + if len(teamDrives) == 0 { fmt.Printf("No team drives found in your account") - } else { - driveID = config.Choose("Enter a Team Drive ID", driveIDs, driveNames, true) + return nil + } + var driveIDs, driveNames []string + for _, teamDrive := range teamDrives { + driveIDs = append(driveIDs, teamDrive.Id) + driveNames = append(driveNames, teamDrive.Name) } + driveID := config.Choose("Enter a Team Drive ID", driveIDs, driveNames, true) m.Set("team_drive", driveID) + m.Set("root_folder_id", "") opt.TeamDriveID = driveID + opt.RootFolderID = "" return nil } -// newPacer makes a pacer configured for drive -func newPacer(opt *Options) *fs.Pacer { - return fs.NewPacer(pacer.NewGoogleDrive(pacer.MinSleep(opt.PacerMinSleep), pacer.Burst(opt.PacerBurst))) -} - // getClient makes an http client according to the options func getClient(opt *Options) *http.Client { t := fshttp.NewTransportCustom(fs.Config, func(t *http.Transport) { @@ -1043,7 +1075,7 @@ func createOAuthClient(opt *Options, name string, m configmap.Mapper) (*http.Cli // try loading service account credentials from env variable, then from a file if len(opt.ServiceAccountCredentials) == 0 && opt.ServiceAccountFile != "" { - loadedCreds, err := ioutil.ReadFile(os.ExpandEnv(opt.ServiceAccountFile)) + loadedCreds, err := ioutil.ReadFile(env.ShellExpand(opt.ServiceAccountFile)) if err != nil { return nil, errors.Wrap(err, "error opening service account credentials file") } @@ -1094,33 +1126,14 @@ func (f *Fs) setUploadCutoff(cs fs.SizeSuffix) (old fs.SizeSuffix, err error) { return } -// NewFs constructs an Fs from the path, container:path -func NewFs(name, path string, m configmap.Mapper) (fs.Fs, error) { - ctx := context.Background() +// newFs partially constructs Fs from the path +// +// It constructs a valid Fs but doesn't attempt to figure out whether +// it is a file or a directory. +func newFs(name, path string, m configmap.Mapper) (*Fs, error) { // Parse config into Options struct opt := new(Options) err := configstruct.Set(m, opt) - //----------------------------------------------------------- - maybeIsFile := false - // 添加 {id} 作为根目录功能 - if(path != "" && path[0:1] == "{"){ - idIndex := strings.Index(path,"}") - if(idIndex > 0){ - RootId := path[1:idIndex]; - name += RootId - //opt.ServerSideAcrossConfigs = true - if(len(RootId) == 33){ - maybeIsFile = true - opt.RootFolderID = RootId; - }else{ - opt.RootFolderID = RootId; - opt.TeamDriveID = RootId; - } - path = path[idIndex+1:] - } - } - - //----------------------------------------------------------- if err != nil { return nil, err } @@ -1138,18 +1151,24 @@ func NewFs(name, path string, m configmap.Mapper) (fs.Fs, error) { return nil, errors.Wrap(err, "drive: failed when making oauth client") } + fs.Debugf("path in newFs: ", path) root, err := parseDrivePath(path) if err != nil { return nil, err } f := &Fs{ - name: name, - root: root, - opt: *opt, - pacer: newPacer(opt), + name: name, + root: root, + opt: *opt, + pacer: fs.NewPacer(pacer.NewGoogleDrive(pacer.MinSleep(opt.PacerMinSleep), pacer.Burst(opt.PacerBurst))), + m: m, + grouping: listRGrouping, + listRmu: new(sync.Mutex), + listRempties: make(map[string]struct{}), } f.isTeamDrive = opt.TeamDriveID != "" + f.fileFields = f.getFileFields() f.features = (&fs.Features{ DuplicateFiles: true, ReadMimeType: true, @@ -1172,15 +1191,58 @@ func NewFs(name, path string, m configmap.Mapper) (fs.Fs, error) { } } + return f, nil +} + +// NewFs constructs an Fs from the path, container:path +func NewFs(name, path string, m configmap.Mapper) (fs.Fs, error) { + ctx := context.Background() + //------------------------------------------------------------ + maybeIsFile := false + idIndex := 0 + RootId := "" + // 添加 {id} 作为根目录功能 + if(path != "" && path[0:1] == "{"){ + idIndex = strings.Index(path,"}") + fs.Debugf("path", path) + fs.Debugf(nil, "idIndex: %d", idIndex) + if(idIndex > 0){ + RootId = path[1:idIndex]; + name += RootId + fs.Debugf("RootId", RootId) + fs.Debugf("name", name) + path = path[idIndex+1:] + fs.Debugf("path after removal", path) + } + } + //------------------------------------------------------------ + + f, err := newFs(name, path, m) + if err != nil { + return nil, err + } - // set root folder for a team drive or query the user root folder - if opt.RootFolderID != "" { - // override root folder if set or cached in the config - f.rootFolderID = opt.RootFolderID + //------------------------------------------------------------ + if (idIndex > 0){ + if(len(RootId) == 33){ + maybeIsFile = true + f.opt.RootFolderID = RootId; + }else{ + f.opt.RootFolderID = RootId; + f.opt.TeamDriveID = RootId; + } + } + //------------------------------------------------------------ + + // Set the root folder ID + if f.opt.RootFolderID != "" { + // use root_folder ID if set + f.rootFolderID = f.opt.RootFolderID } else if f.isTeamDrive { + // otherwise use team_drive if set f.rootFolderID = f.opt.TeamDriveID } else { - // Look up the root ID and cache it in the config + // otherwise look up the actual root ID rootID, err := f.getRootID() if err != nil { if gerr, ok := errors.Cause(err).(*googleapi.Error); ok && gerr.Code == 404 { @@ -1192,32 +1254,33 @@ func NewFs(name, path string, m configmap.Mapper) (fs.Fs, error) { } } f.rootFolderID = rootID - m.Set("root_folder_id", rootID) + fs.Debugf(f, "root_folder_id = %q - save this in the config to speed up startup", rootID) } - f.dirCache = dircache.New(root, f.rootFolderID, f) + f.dirCache = dircache.New(f.root, f.rootFolderID, f) // Parse extensions - if opt.Extensions != "" { - if opt.ExportExtensions != defaultExportExtensions { + if f.opt.Extensions != "" { + if f.opt.ExportExtensions != defaultExportExtensions { return nil, errors.New("only one of 'formats' and 'export_formats' can be specified") } - opt.Extensions, opt.ExportExtensions = "", opt.Extensions + f.opt.Extensions, f.opt.ExportExtensions = "", f.opt.Extensions } - f.exportExtensions, _, err = parseExtensions(opt.ExportExtensions, defaultExportExtensions) + f.exportExtensions, _, err = parseExtensions(f.opt.ExportExtensions, defaultExportExtensions) if err != nil { return nil, err } - _, f.importMimeTypes, err = parseExtensions(opt.ImportExtensions) + _, f.importMimeTypes, err = parseExtensions(f.opt.ImportExtensions) if err != nil { return nil, err } - //------------------------------------------------------ + + //------------------------------------------------------------ if(maybeIsFile){ - file,err := f.svc.Files.Get(opt.RootFolderID).Fields("name","id","size","mimeType").SupportsAllDrives(true).Do() + file,err := f.svc.Files.Get(f.opt.RootFolderID).Fields("name","id","size","mimeType").SupportsAllDrives(true).Do() if err == nil{ - //fmt.Println("file.MimeType", file.MimeType) + fs.Debugf("file.MimeType", file.MimeType) if( "application/vnd.google-apps.folder" != file.MimeType && file.MimeType != ""){ tempF := *f newRoot := "" @@ -1234,13 +1297,13 @@ func NewFs(name, path string, m configmap.Mapper) (fs.Fs, error) { } } } - //------------------------------------------------------ + //------------------------------------------------------------ // Find the current root err = f.dirCache.FindRoot(ctx, false) if err != nil { // Assume it is a file - newRoot, remote := dircache.SplitPath(root) + newRoot, remote := dircache.SplitPath(f.root) tempF := *f tempF.dirCache = dircache.New(newRoot, f.rootFolderID, &tempF) tempF.root = newRoot @@ -1284,10 +1347,29 @@ func (f *Fs) newBaseObject(remote string, info *drive.File) baseObject { modifiedDate: modifiedDate, mimeType: info.MimeType, bytes: size, + parents: len(info.Parents), } } -// newRegularObject creates a fs.Object for a normal drive.File +// getFileFields gets the fields for a normal file Get or List +func (f *Fs) getFileFields() (fields googleapi.Field) { + fields = partialFields + if f.opt.AuthOwnerOnly { + fields += ",owners" + } + if f.opt.UseSharedDate { + fields += ",sharedWithMeTime" + } + if f.opt.SkipChecksumGphotos { + fields += ",spaces" + } + if f.opt.SizeAsQuota { + fields += ",quotaBytesUsed" + } + return fields +} + +// newRegularObject creates an fs.Object for a normal drive.File func (f *Fs) newRegularObject(remote string, info *drive.File) fs.Object { // wipe checksum if SkipChecksumGphotos and file is type Photo or Video if f.opt.SkipChecksumGphotos { @@ -1300,31 +1382,19 @@ func (f *Fs) newRegularObject(remote string, info *drive.File) fs.Object { } return &Object{ baseObject: f.newBaseObject(remote, info), - url: fmt.Sprintf("%sfiles/%s?alt=media", f.svc.BasePath, info.Id), + url: fmt.Sprintf("%sfiles/%s?alt=media", f.svc.BasePath, actualID(info.Id)), md5sum: strings.ToLower(info.Md5Checksum), v2Download: f.opt.V2DownloadMinSize != -1 && info.Size >= int64(f.opt.V2DownloadMinSize), } } -// newDocumentObject creates a fs.Object for a google docs drive.File +// newDocumentObject creates an fs.Object for a google docs drive.File func (f *Fs) newDocumentObject(remote string, info *drive.File, extension, exportMimeType string) (fs.Object, error) { mediaType, _, err := mime.ParseMediaType(exportMimeType) if err != nil { return nil, err } - url := fmt.Sprintf("%sfiles/%s/export?mimeType=%s", f.svc.BasePath, info.Id, url.QueryEscape(mediaType)) - if f.opt.AlternateExport { - switch info.MimeType { - case "application/vnd.google-apps.drawing": - url = fmt.Sprintf("https://docs.google.com/drawings/d/%s/export/%s", info.Id, extension[1:]) - case "application/vnd.google-apps.document": - url = fmt.Sprintf("https://docs.google.com/document/d/%s/export?format=%s", info.Id, extension[1:]) - case "application/vnd.google-apps.spreadsheet": - url = fmt.Sprintf("https://docs.google.com/spreadsheets/d/%s/export?format=%s", info.Id, extension[1:]) - case "application/vnd.google-apps.presentation": - url = fmt.Sprintf("https://docs.google.com/presentation/d/%s/export/%s", info.Id, extension[1:]) - } - } + url := info.ExportLinks[mediaType] baseObject := f.newBaseObject(remote+extension, info) baseObject.bytes = -1 baseObject.mimeType = exportMimeType @@ -1336,7 +1406,7 @@ func (f *Fs) newDocumentObject(remote string, info *drive.File, extension, expor }, nil } -// newLinkObject creates a fs.Object that represents a link a google docs drive.File +// newLinkObject creates an fs.Object that represents a link a google docs drive.File func (f *Fs) newLinkObject(remote string, info *drive.File, extension, exportMimeType string) (fs.Object, error) { t := linkTemplate(exportMimeType) if t == nil { @@ -1362,9 +1432,9 @@ func (f *Fs) newLinkObject(remote string, info *drive.File, extension, exportMim }, nil } -// newObjectWithInfo creates a fs.Object for any drive.File +// newObjectWithInfo creates an fs.Object for any drive.File // -// When the drive.File cannot be represented as a fs.Object it will return (nil, nil). +// When the drive.File cannot be represented as an fs.Object it will return (nil, nil). func (f *Fs) newObjectWithInfo(remote string, info *drive.File) (fs.Object, error) { // If item has MD5 sum or a length it is a file stored on drive if info.Md5Checksum != "" || info.Size > 0 { @@ -1375,28 +1445,46 @@ func (f *Fs) newObjectWithInfo(remote string, info *drive.File) (fs.Object, erro return f.newObjectWithExportInfo(remote, info, extension, exportName, exportMimeType, isDocument) } -// newObjectWithExportInfo creates a fs.Object for any drive.File and the result of findExportFormat +// newObjectWithExportInfo creates an fs.Object for any drive.File and the result of findExportFormat // -// When the drive.File cannot be represented as a fs.Object it will return (nil, nil). +// When the drive.File cannot be represented as an fs.Object it will return (nil, nil). func (f *Fs) newObjectWithExportInfo( remote string, info *drive.File, - extension, exportName, exportMimeType string, isDocument bool) (fs.Object, error) { + extension, exportName, exportMimeType string, isDocument bool) (o fs.Object, err error) { + // Note that resolveShortcut will have been called already if + // we are being called from a listing. However the drive.Item + // will have been resolved so this will do nothing. + info, err = f.resolveShortcut(info) + if err != nil { + return nil, errors.Wrap(err, "new object") + } switch { + case info.MimeType == driveFolderType: + return nil, fs.ErrorNotAFile + case info.MimeType == shortcutMimeType: + // We can only get here if f.opt.SkipShortcuts is set + // and not from a listing. This is unlikely. + fs.Debugf(remote, "Ignoring shortcut as skip shortcuts is set") + return nil, fs.ErrorObjectNotFound + case info.MimeType == shortcutMimeTypeDangling: + // Pretend a dangling shortcut is a regular object + // It will error if used, but appear in listings so it can be deleted + return f.newRegularObject(remote, info), nil case info.Md5Checksum != "" || info.Size > 0: // If item has MD5 sum or a length it is a file stored on drive return f.newRegularObject(remote, info), nil case f.opt.SkipGdocs: fs.Debugf(remote, "Skipping google document type %q", info.MimeType) - return nil, nil + return nil, fs.ErrorObjectNotFound default: // If item MimeType is in the ExportFormats then it is a google doc if !isDocument { fs.Debugf(remote, "Ignoring unknown document type %q", info.MimeType) - return nil, nil + return nil, fs.ErrorObjectNotFound } if extension == "" { fs.Debugf(remote, "No export formats found for %q", info.MimeType) - return nil, nil + return nil, fs.ErrorObjectNotFound } if isLinkMimeType(exportMimeType) { return f.newLinkObject(remote, info, extension, exportMimeType) @@ -1408,11 +1496,11 @@ func (f *Fs) newObjectWithExportInfo( // NewObject finds the Object at remote. If it can't be found // it returns the error fs.ErrorObjectNotFound. func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) { - //------------------------------------ + //------------------------------------------------------------ if(f.FileObj != nil){ return *f.FileObj, nil } - //------------------------------------- + //------------------------------------------------------------ info, extension, exportName, exportMimeType, isDocument, err := f.getRemoteInfoWithExport(ctx, remote) if err != nil { return nil, err @@ -1433,7 +1521,8 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) { // FindLeaf finds a directory of name leaf in the folder with ID pathID func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) { // Find the leaf in pathID - found, err = f.list(ctx, []string{pathID}, leaf, true, false, false, func(item *drive.File) bool { + pathID = actualID(pathID) + found, err = f.list(ctx, []string{pathID}, leaf, true, false, f.opt.TrashedOnly, false, func(item *drive.File) bool { if !f.opt.SkipGdocs { _, exportName, _, isDocument := f.findExportFormat(item) if exportName == leaf { @@ -1458,6 +1547,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, leaf = f.opt.Enc.FromStandardName(leaf) // fmt.Println("Making", path) // Define the metadata for the directory we are going to create. + pathID = actualID(pathID) createInfo := &drive.File{ Name: leaf, Description: leaf, @@ -1618,17 +1708,14 @@ func (f *Fs) findImportFormat(mimeType string) string { // This should return ErrDirNotFound if the directory isn't // found. func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { - err = f.dirCache.FindRoot(ctx, false) - if err != nil { - return nil, err - } directoryID, err := f.dirCache.FindDir(ctx, dir, false) if err != nil { return nil, err } + directoryID = actualID(directoryID) var iErr error - _, err = f.list(ctx, []string{directoryID}, "", false, false, false, func(item *drive.File) bool { + _, err = f.list(ctx, []string{directoryID}, "", false, false, f.opt.TrashedOnly, false, func(item *drive.File) bool { entry, err := f.itemToDirEntry(path.Join(dir, item.Name), item) if err != nil { iErr = err @@ -1684,20 +1771,22 @@ func (s listRSlices) Less(i, j int) bool { return s.dirs[i] < s.dirs[j] } -// listRRunner will read dirIDs from the in channel, perform the file listing an call cb with each DirEntry. +// listRRunner will read dirIDs from the in channel, perform the file listing and call cb with each DirEntry. // // In each cycle it will read up to grouping entries from the in channel without blocking. // If an error occurs it will be send to the out channel and then return. Once the in channel is closed, // nil is send to the out channel and the function returns. -func (f *Fs) listRRunner(ctx context.Context, wg *sync.WaitGroup, in <-chan listREntry, out chan<- error, cb func(fs.DirEntry) error, grouping int) { +func (f *Fs) listRRunner(ctx context.Context, wg *sync.WaitGroup, in chan listREntry, out chan<- error, cb func(fs.DirEntry) error, sendJob func(listREntry)) { var dirs []string var paths []string + var grouping int32 for dir := range in { dirs = append(dirs[:0], dir.id) paths = append(paths[:0], dir.path) + grouping = atomic.LoadInt32(&f.grouping) waitloop: - for i := 1; i < grouping; i++ { + for i := int32(1); i < grouping; i++ { select { case d, ok := <-in: if !ok { @@ -1710,9 +1799,16 @@ func (f *Fs) listRRunner(ctx context.Context, wg *sync.WaitGroup, in <-chan list } listRSlices{dirs, paths}.Sort() var iErr error - _, err := f.list(ctx, dirs, "", false, false, false, func(item *drive.File) bool { + foundItems := false + _, err := f.list(ctx, dirs, "", false, false, f.opt.TrashedOnly, false, func(item *drive.File) bool { + // shared with me items have no parents when at the root + if f.opt.SharedWithMe && len(item.Parents) == 0 && len(paths) == 1 && paths[0] == "" { + item.Parents = dirs + } for _, parent := range item.Parents { var i int + foundItems = true + earlyExit := false // If only one item in paths then no need to search for the ID // assuming google drive is doing its job properly. // @@ -1720,8 +1816,11 @@ func (f *Fs) listRRunner(ctx context.Context, wg *sync.WaitGroup, in <-chan list if len(paths) == 1 { // don't check parents at root because // - shared with me items have no parents at the root - // - if using a root alias, eg "root" or "appDataFolder" the ID won't match + // - if using a root alias, e.g. "root" or "appDataFolder" the ID won't match i = 0 + // items at root can have more than one parent so we need to put + // the item in just once. + earlyExit = true } else { // only handle parents that are in the requested dirs list if not at root i = sort.SearchStrings(dirs, parent) @@ -1741,9 +1840,54 @@ func (f *Fs) listRRunner(ctx context.Context, wg *sync.WaitGroup, in <-chan list iErr = err return true } + + // If didn't check parents then insert only once + if earlyExit { + break + } } return false }) + // Found no items in more than one directory. Retry these as + // individual directories This is to work around a bug in google + // drive where (A in parents) or (B in parents) returns nothing + // sometimes. See #3114, #4289 and + // https://issuetracker.google.com/issues/149522397 + if len(dirs) > 1 && !foundItems { + if atomic.SwapInt32(&f.grouping, 1) != 1 { + fs.Debugf(f, "Disabling ListR to work around bug in drive as multi listing (%d) returned no entries", len(dirs)) + } + f.listRmu.Lock() + for i := range dirs { + // Requeue the jobs + job := listREntry{id: dirs[i], path: paths[i]} + sendJob(job) + // Make a note of these dirs - if they all turn + // out to be empty then we can re-enable grouping + f.listRempties[dirs[i]] = struct{}{} + } + f.listRmu.Unlock() + fs.Debugf(f, "Recycled %d entries", len(dirs)) + } + // If using a grouping of 1 and dir was empty then check to see if it + // is part of the group that caused grouping to be disabled. + if grouping == 1 && len(dirs) == 1 && !foundItems { + f.listRmu.Lock() + if _, found := f.listRempties[dirs[0]]; found { + // Remove the ID + delete(f.listRempties, dirs[0]) + // If no empties left => all the directories that + // triggered the grouping being set to 1 were actually + // empty so must have made a mistake + if len(f.listRempties) == 0 { + if atomic.SwapInt32(&f.grouping, listRGrouping) != listRGrouping { + fs.Debugf(f, "Re-enabling ListR as previous detection was in error") + } + } + } + f.listRmu.Unlock() + } + for range dirs { wg.Done() } @@ -1778,39 +1922,47 @@ func (f *Fs) listRRunner(ctx context.Context, wg *sync.WaitGroup, in <-chan list // Don't implement this unless you have a more efficient way // of listing recursively that doing a directory traversal. func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) { - const ( - grouping = 50 - inputBuffer = 1000 - ) - - err = f.dirCache.FindRoot(ctx, false) - if err != nil { - return err - } directoryID, err := f.dirCache.FindDir(ctx, dir, false) if err != nil { return err } + directoryID = actualID(directoryID) mu := sync.Mutex{} // protects in and overflow wg := sync.WaitGroup{} - in := make(chan listREntry, inputBuffer) + in := make(chan listREntry, listRInputBuffer) out := make(chan error, fs.Config.Checkers) list := walk.NewListRHelper(callback) overflow := []listREntry{} listed := 0 - cb := func(entry fs.DirEntry) error { + // Send a job to the input channel if not closed. If the job + // won't fit then queue it in the overflow slice. + // + // This will not block if the channel is full. + sendJob := func(job listREntry) { mu.Lock() defer mu.Unlock() - if d, isDir := entry.(*fs.Dir); isDir && in != nil { - select { - case in <- listREntry{d.ID(), d.Remote()}: - wg.Add(1) - default: - overflow = append(overflow, listREntry{d.ID(), d.Remote()}) - } + if in == nil { + return + } + wg.Add(1) + select { + case in <- job: + default: + overflow = append(overflow, job) + wg.Add(-1) } + } + + // Send the entry to the caller, queueing any directories as new jobs + cb := func(entry fs.DirEntry) error { + if d, isDir := entry.(*fs.Dir); isDir { + job := listREntry{actualID(d.ID()), d.Remote()} + sendJob(job) + } + mu.Lock() + defer mu.Unlock() listed++ return list.Add(entry) } @@ -1819,7 +1971,7 @@ func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) ( in <- listREntry{directoryID, dir} for i := 0; i < fs.Config.Checkers; i++ { - go f.listRRunner(ctx, &wg, in, out, cb, grouping) + go f.listRRunner(ctx, &wg, in, out, cb, sendJob) } go func() { // wait until the all directories are processed @@ -1828,9 +1980,9 @@ func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) ( for len(overflow) > 0 { mu.Lock() l := len(overflow) - // only fill half of the channel to prevent entries beeing put into overflow again - if l > inputBuffer/2 { - l = inputBuffer / 2 + // only fill half of the channel to prevent entries being put into overflow again + if l > listRInputBuffer/2 { + l = listRInputBuffer / 2 } wg.Add(l) for _, d := range overflow[:l] { @@ -1885,10 +2037,94 @@ func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) ( return nil } -// itemToDirEntry converts a drive.File to a fs.DirEntry. -// When the drive.File cannot be represented as a fs.DirEntry +const shortcutSeparator = '\t' + +// joinID adds an actual drive ID to the shortcut ID it came from +// +// directoryIDs in the dircache are these composite directory IDs so +// we must always unpack them before use. +func joinID(actual, shortcut string) string { + return actual + string(shortcutSeparator) + shortcut +} + +// splitID separates an actual ID and a shortcut ID from a composite +// ID. If there was no shortcut ID then it will return "" for it. +func splitID(compositeID string) (actualID, shortcutID string) { + i := strings.IndexRune(compositeID, shortcutSeparator) + if i < 0 { + return compositeID, "" + } + return compositeID[:i], compositeID[i+1:] +} + +// isShortcutID returns true if compositeID refers to a shortcut +func isShortcutID(compositeID string) bool { + return strings.IndexRune(compositeID, shortcutSeparator) >= 0 +} + +// actualID returns an actual ID from a composite ID +func actualID(compositeID string) (actualID string) { + actualID, _ = splitID(compositeID) + return actualID +} + +// shortcutID returns a shortcut ID from a composite ID if available, +// or the actual ID if not. +func shortcutID(compositeID string) (shortcutID string) { + actualID, shortcutID := splitID(compositeID) + if shortcutID != "" { + return shortcutID + } + return actualID +} + +// isShortcut returns true of the item is a shortcut +func isShortcut(item *drive.File) bool { + return item.MimeType == shortcutMimeType && item.ShortcutDetails != nil +} + +// Dereference shortcut if required. It returns the newItem (which may +// be just item). +// +// If we return a new item then the ID will be adjusted to be a +// composite of the actual ID and the shortcut ID. This is to make +// sure that we have decided in all use places what we are doing with +// the ID. +// +// Note that we assume shortcuts can't point to shortcuts. Google +// drive web interface doesn't offer the option to create a shortcut +// to a shortcut. The documentation is silent on the issue. +func (f *Fs) resolveShortcut(item *drive.File) (newItem *drive.File, err error) { + if f.opt.SkipShortcuts || item.MimeType != shortcutMimeType { + return item, nil + } + if item.ShortcutDetails == nil { + fs.Errorf(nil, "Expecting shortcutDetails in %v", item) + return item, nil + } + newItem, err = f.getFile(item.ShortcutDetails.TargetId, f.fileFields) + if err != nil { + if gerr, ok := errors.Cause(err).(*googleapi.Error); ok && gerr.Code == 404 { + // 404 means dangling shortcut, so just return the shortcut with the mime type mangled + fs.Logf(nil, "Dangling shortcut %q detected", item.Name) + item.MimeType = shortcutMimeTypeDangling + return item, nil + } + return nil, errors.Wrap(err, "failed to resolve shortcut") + } + // make sure we use the Name, Parents and Trashed from the original item + newItem.Name = item.Name + newItem.Parents = item.Parents + newItem.Trashed = item.Trashed + // the new ID is a composite ID + newItem.Id = joinID(newItem.Id, item.Id) + return newItem, nil +} + +// itemToDirEntry converts a drive.File to an fs.DirEntry. +// When the drive.File cannot be represented as an fs.DirEntry // (nil, nil) is returned. -func (f *Fs) itemToDirEntry(remote string, item *drive.File) (fs.DirEntry, error) { +func (f *Fs) itemToDirEntry(remote string, item *drive.File) (entry fs.DirEntry, err error) { switch { case item.MimeType == driveFolderType: // cache the directory ID for later lookups @@ -1899,7 +2135,11 @@ func (f *Fs) itemToDirEntry(remote string, item *drive.File) (fs.DirEntry, error case f.opt.AuthOwnerOnly && !isAuthOwned(item): // ignore object default: - return f.newObjectWithInfo(remote, item) + entry, err = f.newObjectWithInfo(remote, item) + if err == fs.ErrorObjectNotFound { + return nil, nil + } + return entry, err } return nil, nil } @@ -1908,10 +2148,11 @@ func (f *Fs) itemToDirEntry(remote string, item *drive.File) (fs.DirEntry, error // // Used to create new objects func (f *Fs) createFileInfo(ctx context.Context, remote string, modTime time.Time) (*drive.File, error) { - leaf, directoryID, err := f.dirCache.FindRootAndPath(ctx, remote, true) + leaf, directoryID, err := f.dirCache.FindPath(ctx, remote, true) if err != nil { return nil, err } + directoryID = actualID(directoryID) leaf = f.opt.Enc.FromStandardName(leaf) // Define the metadata for the file we are going to create. @@ -1930,10 +2171,10 @@ func (f *Fs) createFileInfo(ctx context.Context, remote string, modTime time.Tim // // The new object may have been created if an error is returned func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { - exisitingObj, err := f.NewObject(ctx, src.Remote()) + existingObj, err := f.NewObject(ctx, src.Remote()) switch err { case nil: - return exisitingObj, exisitingObj.Update(ctx, in, src, options...) + return existingObj, existingObj.Update(ctx, in, src, options...) case fs.ErrorObjectNotFound: // Not found so create it return f.PutUnchecked(ctx, in, src, options...) @@ -2015,14 +2256,26 @@ func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, // MergeDirs merges the contents of all the directories passed // in into the first one and rmdirs the other directories. func (f *Fs) MergeDirs(ctx context.Context, dirs []fs.Directory) error { + if len(dirs) < 2 { + return nil + } + newDirs := dirs[:0] + for _, dir := range dirs { + if isShortcutID(dir.ID()) { + fs.Infof(dir, "skipping shortcut directory") + continue + } + newDirs = append(newDirs, dir) + } + dirs = newDirs if len(dirs) < 2 { return nil } dstDir := dirs[0] for _, srcDir := range dirs[1:] { - // list the the objects + // list the objects infos := []*drive.File{} - _, err := f.list(ctx, []string{srcDir.ID()}, "", false, false, true, func(info *drive.File) bool { + _, err := f.list(ctx, []string{srcDir.ID()}, "", false, false, f.opt.TrashedOnly, true, func(info *drive.File) bool { infos = append(infos, info) return false }) @@ -2048,7 +2301,7 @@ func (f *Fs) MergeDirs(ctx context.Context, dirs []fs.Directory) error { } // rmdir (into trash) the now empty source directory fs.Infof(srcDir, "removing empty directory") - err = f.rmdir(ctx, srcDir.ID(), true) + err = f.delete(ctx, srcDir.ID(), true) if err != nil { return errors.Wrapf(err, "MergeDirs move failed to rmdir %q", srcDir) } @@ -2058,30 +2311,24 @@ func (f *Fs) MergeDirs(ctx context.Context, dirs []fs.Directory) error { // Mkdir creates the container if it doesn't exist func (f *Fs) Mkdir(ctx context.Context, dir string) error { - err := f.dirCache.FindRoot(ctx, true) - if err != nil { - return err - } - if dir != "" { - _, err = f.dirCache.FindDir(ctx, dir, true) - } + _, err := f.dirCache.FindDir(ctx, dir, true) return err } -// Rmdir deletes a directory unconditionally by ID -func (f *Fs) rmdir(ctx context.Context, directoryID string, useTrash bool) error { +// delete a file or directory unconditionally by ID +func (f *Fs) delete(ctx context.Context, id string, useTrash bool) error { return f.pacer.Call(func() (bool, error) { var err error if useTrash { info := drive.File{ Trashed: true, } - _, err = f.svc.Files.Update(directoryID, &info). + _, err = f.svc.Files.Update(id, &info). Fields(""). SupportsAllDrives(true). Do() } else { - err = f.svc.Files.Delete(directoryID). + err = f.svc.Files.Delete(id). Fields(""). SupportsAllDrives(true). Do() @@ -2090,40 +2337,48 @@ func (f *Fs) rmdir(ctx context.Context, directoryID string, useTrash bool) error }) } -// Rmdir deletes a directory -// -// Returns an error if it isn't empty -func (f *Fs) Rmdir(ctx context.Context, dir string) error { +// purgeCheck removes the dir directory, if check is set then it +// refuses to do so if it has anything in +func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error { root := path.Join(f.root, dir) dc := f.dirCache directoryID, err := dc.FindDir(ctx, dir, false) if err != nil { return err } + directoryID, shortcutID := splitID(directoryID) + // if directory is a shortcut remove it regardless + if shortcutID != "" { + return f.delete(ctx, shortcutID, f.opt.UseTrash) + } var trashedFiles = false - found, err := f.list(ctx, []string{directoryID}, "", false, false, true, func(item *drive.File) bool { - if !item.Trashed { - fs.Debugf(dir, "Rmdir: contains file: %q", item.Name) - return true + if check { + found, err := f.list(ctx, []string{directoryID}, "", false, false, f.opt.TrashedOnly, true, func(item *drive.File) bool { + if !item.Trashed { + fs.Debugf(dir, "Rmdir: contains file: %q", item.Name) + return true + } + fs.Debugf(dir, "Rmdir: contains trashed file: %q", item.Name) + trashedFiles = true + return false + }) + if err != nil { + return err + } + if found { + return errors.Errorf("directory not empty") } - fs.Debugf(dir, "Rmdir: contains trashed file: %q", item.Name) - trashedFiles = true - return false - }) - if err != nil { - return err - } - if found { - return errors.Errorf("directory not empty") } if root != "" { // trash the directory if it had trashed files // in or the user wants to trash, otherwise // delete it. - err = f.rmdir(ctx, directoryID, trashedFiles || f.opt.UseTrash) + err = f.delete(ctx, directoryID, trashedFiles || f.opt.UseTrash) if err != nil { return err } + } else if check { + return errors.New("can't purge root directory") } f.dirCache.FlushDir(dir) if err != nil { @@ -2132,12 +2387,19 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error { return nil } +// Rmdir deletes a directory +// +// Returns an error if it isn't empty +func (f *Fs) Rmdir(ctx context.Context, dir string) error { + return f.purgeCheck(ctx, dir, true) +} + // Precision of the object storage system func (f *Fs) Precision() time.Duration { return time.Millisecond } -// Copy src to this remote using server side copy operations. +// Copy src to this remote using server-side copy operations. // // This is stored with the remote path given // @@ -2149,11 +2411,13 @@ func (f *Fs) Precision() time.Duration { func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) { var srcObj *baseObject ext := "" + isDoc := false switch src := src.(type) { case *Object: srcObj = &src.baseObject case *documentObject: srcObj, ext = &src.baseObject, src.ext() + isDoc = true case *linkObject: srcObj, ext = &src.baseObject, src.ext() default: @@ -2161,6 +2425,12 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, return nil, fs.ErrorCantCopy } + // Look to see if there is an existing object before we remove + // the extension from the remote + existingObject, _ := f.NewObject(ctx, remote) + + // Adjust the remote name to be without the extension if we + // are about to create a doc. if ext != "" { if !strings.HasSuffix(remote, ext) { fs.Debugf(src, "Can't copy - not same document type") @@ -2169,17 +2439,30 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, remote = remote[:len(remote)-len(ext)] } - // Look to see if there is an existing object - existingObject, _ := f.NewObject(ctx, remote) - createInfo, err := f.createFileInfo(ctx, remote, src.ModTime(ctx)) if err != nil { return nil, err } + if isDoc { + // preserve the description on copy for docs + info, err := f.getFile(actualID(srcObj.id), "description") + if err != nil { + return nil, errors.Wrap(err, "failed to read description for Google Doc") + } + createInfo.Description = info.Description + } else { + // don't overwrite the description on copy for files + // this should work for docs but it doesn't - it is probably a bug in Google Drive + createInfo.Description = "" + } + + // get the ID of the thing to copy - this is the shortcut if available + id := shortcutID(srcObj.id) + var info *drive.File err = f.pacer.Call(func() (bool, error) { - info, err = f.svc.Files.Copy(srcObj.id, createInfo). + info, err = f.svc.Files.Copy(id, createInfo). Fields(partialFields). SupportsAllDrives(true). KeepRevisionForever(f.opt.KeepRevisionForever). @@ -2193,6 +2476,22 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, if err != nil { return nil, err } + // Google docs aren't preserving their mod time after copy, so set them explicitly + // See: https://github.com/rclone/rclone/issues/4517 + // + // FIXME remove this when google fixes the problem! + if isDoc { + // A short sleep is needed here in order to make the + // change effective, without it is is ignored. This is + // probably some eventual consistency nastiness. + sleepTime := 2 * time.Second + fs.Debugf(f, "Sleeping for %v before setting the modtime to work around drive bug - see #4517", sleepTime) + time.Sleep(sleepTime) + err = newObject.SetModTime(ctx, src.ModTime(ctx)) + if err != nil { + return nil, err + } + } if existingObject != nil { err = existingObject.Remove(ctx) if err != nil { @@ -2207,43 +2506,64 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, // Optional interface: Only implement this if you have a way of // deleting all the files quicker than just running Remove() on the // result of List() -func (f *Fs) Purge(ctx context.Context) error { - if f.root == "" { - return errors.New("can't purge root directory") - } +func (f *Fs) Purge(ctx context.Context, dir string) error { if f.opt.TrashedOnly { return errors.New("Can't purge with --drive-trashed-only. Use delete if you want to selectively delete files") } - err := f.dirCache.FindRoot(ctx, false) - if err != nil { - return err - } - err = f.pacer.Call(func() (bool, error) { - if f.opt.UseTrash { - info := drive.File{ - Trashed: true, + return f.purgeCheck(ctx, dir, false) +} + +type cleanupResult struct { + Errors int +} + +func (r cleanupResult) Error() string { + return fmt.Sprintf("%d errors during cleanup - see log", r.Errors) +} + +func (f *Fs) cleanupTeamDrive(ctx context.Context, dir string, directoryID string) (r cleanupResult, err error) { + _, err = f.list(ctx, []string{directoryID}, "", false, false, true, false, func(item *drive.File) bool { + remote := path.Join(dir, item.Name) + if item.ExplicitlyTrashed { // description is wrong - can also be set for folders - no need to recurse them + err := f.delete(ctx, item.Id, false) + if err != nil { + r.Errors++ + fs.Errorf(remote, "%v", err) } - _, err = f.svc.Files.Update(f.dirCache.RootID(), &info). - Fields(""). - SupportsAllDrives(true). - Do() - } else { - err = f.svc.Files.Delete(f.dirCache.RootID()). - Fields(""). - SupportsAllDrives(true). - Do() + return false } - return f.shouldRetry(err) + + if item.MimeType == driveFolderType { + if !isShortcutID(item.Id) { + rNew, _ := f.cleanupTeamDrive(ctx, remote, item.Id) + r.Errors += rNew.Errors + } + return false + } + return false }) - f.dirCache.ResetRoot() if err != nil { - return err + err = errors.Wrap(err, "failed to list directory") + r.Errors++ + fs.Errorf(dir, "%v", err) } - return nil + if r.Errors != 0 { + return r, r + } + return r, nil } // CleanUp empties the trash func (f *Fs) CleanUp(ctx context.Context) error { + if f.isTeamDrive { + directoryID, err := f.dirCache.FindDir(ctx, "", false) + if err != nil { + return err + } + directoryID = actualID(directoryID) + _, err = f.cleanupTeamDrive(ctx, "", directoryID) + return err + } err := f.pacer.Call(func() (bool, error) { err := f.svc.Files.EmptyTrash().Context(ctx).Do() return f.shouldRetry(err) @@ -2295,7 +2615,7 @@ func (f *Fs) About(ctx context.Context) (*fs.Usage, error) { usage := &fs.Usage{ Used: fs.NewUsageValue(q.UsageInDrive), // bytes in use Trashed: fs.NewUsageValue(q.UsageInDriveTrash), // bytes in trash - Other: fs.NewUsageValue(q.Usage - q.UsageInDrive), // other usage eg gmail in drive + Other: fs.NewUsageValue(q.Usage - q.UsageInDrive), // other usage e.g. gmail in drive } if q.Limit > 0 { usage.Total = fs.NewUsageValue(q.Limit) // quota of bytes that can be used @@ -2304,7 +2624,7 @@ func (f *Fs) About(ctx context.Context) (*fs.Usage, error) { return usage, nil } -// Move src to this remote using server side move operations. +// Move src to this remote using server-side move operations. // // This is stored with the remote path given // @@ -2340,6 +2660,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, if err != nil { return nil, err } + srcParentID = actualID(srcParentID) // Temporary Object under construction dstInfo, err := f.createFileInfo(ctx, remote, src.ModTime(ctx)) @@ -2352,7 +2673,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, // Do the move var info *drive.File err = f.pacer.Call(func() (bool, error) { - info, err = f.svc.Files.Update(srcObj.id, dstInfo). + info, err = f.svc.Files.Update(shortcutID(srcObj.id), dstInfo). RemoveParents(srcParentID). AddParents(dstParents). Fields(partialFields). @@ -2368,17 +2689,18 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, } // PublicLink adds a "readable by anyone with link" permission on the given file or folder. -func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err error) { +func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration, unlink bool) (link string, err error) { id, err := f.dirCache.FindDir(ctx, remote, false) if err == nil { fs.Debugf(f, "attempting to share directory '%s'", remote) + id = shortcutID(id) } else { fs.Debugf(f, "attempting to share single file '%s'", remote) o, err := f.NewObject(ctx, remote) if err != nil { return "", err } - id = o.(fs.IDer).ID() + id = shortcutID(o.(fs.IDer).ID()) } permission := &drive.Permission{ @@ -2403,7 +2725,7 @@ func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err er } // DirMove moves src, srcRemote to this remote at dstRemote -// using server side move operations. +// using server-side move operations. // // Will only be called if src.Fs().Name() == f.Name() // @@ -2416,79 +2738,22 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } - srcPath := path.Join(srcFs.root, srcRemote) - dstPath := path.Join(f.root, dstRemote) - - // Refuse to move to or from the root - if srcPath == "" || dstPath == "" { - fs.Debugf(src, "DirMove error: Can't move root") - return errors.New("can't move root directory") - } - - // find the root src directory - err := srcFs.dirCache.FindRoot(ctx, false) - if err != nil { - return err - } - - // find the root dst directory - if dstRemote != "" { - err = f.dirCache.FindRoot(ctx, true) - if err != nil { - return err - } - } else { - if f.dirCache.FoundRoot() { - return fs.ErrorDirExists - } - } - // Find ID of dst parent, creating subdirs if necessary - var leaf, dstDirectoryID string - findPath := dstRemote - if dstRemote == "" { - findPath = f.root - } - leaf, dstDirectoryID, err = f.dirCache.FindPath(ctx, findPath, true) + srcID, srcDirectoryID, srcLeaf, dstDirectoryID, dstLeaf, err := f.dirCache.DirMove(ctx, srcFs.dirCache, srcFs.root, srcRemote, f.root, dstRemote) if err != nil { return err } + _ = srcLeaf - // Check destination does not exist - if dstRemote != "" { - _, err = f.dirCache.FindDir(ctx, dstRemote, false) - if err == fs.ErrorDirNotFound { - // OK - } else if err != nil { - return err - } else { - return fs.ErrorDirExists - } - } - - // Find ID of src parent - var srcDirectoryID string - if srcRemote == "" { - srcDirectoryID, err = srcFs.dirCache.RootParentID() - } else { - _, srcDirectoryID, err = srcFs.dirCache.FindPath(ctx, srcRemote, false) - } - if err != nil { - return err - } - - // Find ID of src - srcID, err := srcFs.dirCache.FindDir(ctx, srcRemote, false) - if err != nil { - return err - } + dstDirectoryID = actualID(dstDirectoryID) + srcDirectoryID = actualID(srcDirectoryID) // Do the move patch := drive.File{ - Name: leaf, + Name: dstLeaf, } err = f.pacer.Call(func() (bool, error) { - _, err = f.svc.Files.Update(srcID, &patch). + _, err = f.svc.Files.Update(shortcutID(srcID), &patch). RemoveParents(srcDirectoryID). AddParents(dstDirectoryID). Fields(""). @@ -2665,6 +2930,406 @@ func (f *Fs) Hashes() hash.Set { return hash.Set(hash.MD5) } +func (f *Fs) changeChunkSize(chunkSizeString string) (err error) { + chunkSizeInt, err := strconv.ParseInt(chunkSizeString, 10, 64) + if err != nil { + return errors.Wrap(err, "couldn't convert chunk size to int") + } + chunkSize := fs.SizeSuffix(chunkSizeInt) + if chunkSize == f.opt.ChunkSize { + return nil + } + err = checkUploadChunkSize(chunkSize) + if err == nil { + f.opt.ChunkSize = chunkSize + } + return err +} + +func (f *Fs) changeServiceAccountFile(file string) (err error) { + fs.Debugf(nil, "Changing Service Account File from %s to %s", f.opt.ServiceAccountFile, file) + if file == f.opt.ServiceAccountFile { + return nil + } + oldSvc := f.svc + oldv2Svc := f.v2Svc + oldOAuthClient := f.client + oldFile := f.opt.ServiceAccountFile + oldCredentials := f.opt.ServiceAccountCredentials + defer func() { + // Undo all the changes instead of doing selective undo's + if err != nil { + f.svc = oldSvc + f.v2Svc = oldv2Svc + f.client = oldOAuthClient + f.opt.ServiceAccountFile = oldFile + f.opt.ServiceAccountCredentials = oldCredentials + } + }() + f.opt.ServiceAccountFile = file + f.opt.ServiceAccountCredentials = "" + oAuthClient, err := createOAuthClient(&f.opt, f.name, f.m) + if err != nil { + return errors.Wrap(err, "drive: failed when making oauth client") + } + f.client = oAuthClient + f.svc, err = drive.New(f.client) + if err != nil { + return errors.Wrap(err, "couldn't create Drive client") + } + if f.opt.V2DownloadMinSize >= 0 { + f.v2Svc, err = drive_v2.New(f.client) + if err != nil { + return errors.Wrap(err, "couldn't create Drive v2 client") + } + } + return nil +} + +// Create a shortcut from (f, srcPath) to (dstFs, dstPath) +// +// Will not overwrite existing files +func (f *Fs) makeShortcut(ctx context.Context, srcPath string, dstFs *Fs, dstPath string) (o fs.Object, err error) { + srcFs := f + srcPath = strings.Trim(srcPath, "/") + dstPath = strings.Trim(dstPath, "/") + if dstPath == "" { + return nil, errors.New("shortcut destination can't be root directory") + } + + // Find source + var srcID string + isDir := false + if srcPath == "" { + // source is root directory + srcID, err = f.dirCache.RootID(ctx, false) + if err != nil { + return nil, err + } + isDir = true + } else if srcObj, err := srcFs.NewObject(ctx, srcPath); err != nil { + if err != fs.ErrorNotAFile { + return nil, errors.Wrap(err, "can't find source") + } + // source was a directory + srcID, err = srcFs.dirCache.FindDir(ctx, srcPath, false) + if err != nil { + return nil, errors.Wrap(err, "failed to find source dir") + } + isDir = true + } else { + // source was a file + srcID = srcObj.(*Object).id + } + srcID = actualID(srcID) // link to underlying object not to shortcut + + // Find destination + _, err = dstFs.NewObject(ctx, dstPath) + if err != fs.ErrorObjectNotFound { + if err == nil { + err = errors.New("existing file") + } else if err == fs.ErrorNotAFile { + err = errors.New("existing directory") + } + return nil, errors.Wrap(err, "not overwriting shortcut target") + } + + // Create destination shortcut + createInfo, err := dstFs.createFileInfo(ctx, dstPath, time.Now()) + if err != nil { + return nil, errors.Wrap(err, "shortcut destination failed") + } + createInfo.MimeType = shortcutMimeType + createInfo.ShortcutDetails = &drive.FileShortcutDetails{ + TargetId: srcID, + } + + var info *drive.File + err = dstFs.pacer.Call(func() (bool, error) { + info, err = dstFs.svc.Files.Create(createInfo). + Fields(partialFields). + SupportsAllDrives(true). + KeepRevisionForever(dstFs.opt.KeepRevisionForever). + Do() + return dstFs.shouldRetry(err) + }) + if err != nil { + return nil, errors.Wrap(err, "shortcut creation failed") + } + if isDir { + return nil, nil + } + return dstFs.newObjectWithInfo(dstPath, info) +} + +// List all team drives +func (f *Fs) listTeamDrives(ctx context.Context) (drives []*drive.TeamDrive, err error) { + drives = []*drive.TeamDrive{} + listTeamDrives := f.svc.Teamdrives.List().PageSize(100) + var defaultFs Fs // default Fs with default Options + for { + var teamDrives *drive.TeamDriveList + err = f.pacer.Call(func() (bool, error) { + teamDrives, err = listTeamDrives.Context(ctx).Do() + return defaultFs.shouldRetry(err) + }) + if err != nil { + return drives, errors.Wrap(err, "listing team drives failed") + } + drives = append(drives, teamDrives.TeamDrives...) + if teamDrives.NextPageToken == "" { + break + } + listTeamDrives.PageToken(teamDrives.NextPageToken) + } + return drives, nil +} + +type unTrashResult struct { + Untrashed int + Errors int +} + +func (r unTrashResult) Error() string { + return fmt.Sprintf("%d errors while untrashing - see log", r.Errors) +} + +// Restore the trashed files from dir, directoryID recursing if needed +func (f *Fs) unTrash(ctx context.Context, dir string, directoryID string, recurse bool) (r unTrashResult, err error) { + directoryID = actualID(directoryID) + fs.Debugf(dir, "finding trash to restore in directory %q", directoryID) + _, err = f.list(ctx, []string{directoryID}, "", false, false, f.opt.TrashedOnly, true, func(item *drive.File) bool { + remote := path.Join(dir, item.Name) + if item.ExplicitlyTrashed { + fs.Infof(remote, "restoring %q", item.Id) + if operations.SkipDestructive(ctx, remote, "restore") { + return false + } + update := drive.File{ + ForceSendFields: []string{"Trashed"}, // necessary to set false value + Trashed: false, + } + err := f.pacer.Call(func() (bool, error) { + _, err := f.svc.Files.Update(item.Id, &update). + SupportsAllDrives(true). + Fields("trashed"). + Do() + return f.shouldRetry(err) + }) + if err != nil { + err = errors.Wrap(err, "failed to restore") + r.Errors++ + fs.Errorf(remote, "%v", err) + } else { + r.Untrashed++ + } + } + if recurse && item.MimeType == "application/vnd.google-apps.folder" { + if !isShortcutID(item.Id) { + rNew, _ := f.unTrash(ctx, remote, item.Id, recurse) + r.Untrashed += rNew.Untrashed + r.Errors += rNew.Errors + } + } + return false + }) + if err != nil { + err = errors.Wrap(err, "failed to list directory") + r.Errors++ + fs.Errorf(dir, "%v", err) + } + if r.Errors != 0 { + return r, r + } + return r, nil +} + +// Untrash dir +func (f *Fs) unTrashDir(ctx context.Context, dir string, recurse bool) (r unTrashResult, err error) { + directoryID, err := f.dirCache.FindDir(ctx, dir, false) + if err != nil { + r.Errors++ + return r, err + } + return f.unTrash(ctx, dir, directoryID, true) +} + +var commandHelp = []fs.CommandHelp{{ + Name: "get", + Short: "Get command for fetching the drive config parameters", + Long: `This is a get command which will be used to fetch the various drive config parameters + +Usage Examples: + + rclone backend get drive: [-o service_account_file] [-o chunk_size] + rclone rc backend/command command=get fs=drive: [-o service_account_file] [-o chunk_size] +`, + Opts: map[string]string{ + "chunk_size": "show the current upload chunk size", + "service_account_file": "show the current service account file", + }, +}, { + Name: "set", + Short: "Set command for updating the drive config parameters", + Long: `This is a set command which will be used to update the various drive config parameters + +Usage Examples: + + rclone backend set drive: [-o service_account_file=sa.json] [-o chunk_size=67108864] + rclone rc backend/command command=set fs=drive: [-o service_account_file=sa.json] [-o chunk_size=67108864] +`, + Opts: map[string]string{ + "chunk_size": "update the current upload chunk size", + "service_account_file": "update the current service account file", + }, +}, { + Name: "shortcut", + Short: "Create shortcuts from files or directories", + Long: `This command creates shortcuts from files or directories. + +Usage: + + rclone backend shortcut drive: source_item destination_shortcut + rclone backend shortcut drive: source_item -o target=drive2: destination_shortcut + +In the first example this creates a shortcut from the "source_item" +which can be a file or a directory to the "destination_shortcut". The +"source_item" and the "destination_shortcut" should be relative paths +from "drive:" + +In the second example this creates a shortcut from the "source_item" +relative to "drive:" to the "destination_shortcut" relative to +"drive2:". This may fail with a permission error if the user +authenticated with "drive2:" can't read files from "drive:". +`, + Opts: map[string]string{ + "target": "optional target remote for the shortcut destination", + }, +}, { + Name: "drives", + Short: "List the shared drives available to this account", + Long: `This command lists the shared drives (teamdrives) available to this +account. + +Usage: + + rclone backend drives drive: + +This will return a JSON list of objects like this + + [ + { + "id": "0ABCDEF-01234567890", + "kind": "drive#teamDrive", + "name": "My Drive" + }, + { + "id": "0ABCDEFabcdefghijkl", + "kind": "drive#teamDrive", + "name": "Test Drive" + } + ] + +`, +}, { + Name: "untrash", + Short: "Untrash files and directories", + Long: `This command untrashes all the files and directories in the directory +passed in recursively. + +Usage: + +This takes an optional directory to trash which make this easier to +use via the API. + + rclone backend untrash drive:directory + rclone backend -i untrash drive:directory subdir + +Use the -i flag to see what would be restored before restoring it. + +Result: + + { + "Untrashed": 17, + "Errors": 0 + } +`, +}} + +// Command the backend to run a named command +// +// The command run is name +// args may be used to read arguments from +// opts may be used to read optional arguments from +// +// The result should be capable of being JSON encoded +// If it is a string or a []string it will be shown to the user +// otherwise it will be JSON encoded and shown to the user like that +func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[string]string) (out interface{}, err error) { + switch name { + case "get": + out := make(map[string]string) + if _, ok := opt["service_account_file"]; ok { + out["service_account_file"] = f.opt.ServiceAccountFile + } + if _, ok := opt["chunk_size"]; ok { + out["chunk_size"] = fmt.Sprintf("%s", f.opt.ChunkSize) + } + return out, nil + case "set": + out := make(map[string]map[string]string) + if serviceAccountFile, ok := opt["service_account_file"]; ok { + serviceAccountMap := make(map[string]string) + serviceAccountMap["previous"] = f.opt.ServiceAccountFile + if err = f.changeServiceAccountFile(serviceAccountFile); err != nil { + return out, err + } + f.m.Set("service_account_file", serviceAccountFile) + serviceAccountMap["current"] = f.opt.ServiceAccountFile + out["service_account_file"] = serviceAccountMap + } + if chunkSize, ok := opt["chunk_size"]; ok { + chunkSizeMap := make(map[string]string) + chunkSizeMap["previous"] = fmt.Sprintf("%s", f.opt.ChunkSize) + if err = f.changeChunkSize(chunkSize); err != nil { + return out, err + } + chunkSizeString := fmt.Sprintf("%s", f.opt.ChunkSize) + f.m.Set("chunk_size", chunkSizeString) + chunkSizeMap["current"] = chunkSizeString + out["chunk_size"] = chunkSizeMap + } + return out, nil + case "shortcut": + if len(arg) != 2 { + return nil, errors.New("need exactly 2 arguments") + } + dstFs := f + target, ok := opt["target"] + if ok { + targetFs, err := cache.Get(target) + if err != nil { + return nil, errors.Wrap(err, "couldn't find target") + } + dstFs, ok = targetFs.(*Fs) + if !ok { + return nil, errors.New("target is not a drive backend") + } + } + return f.makeShortcut(ctx, arg[0], dstFs, arg[1]) + case "drives": + return f.listTeamDrives(ctx) + case "untrash": + dir := "" + if len(arg) > 0 { + dir = arg[0] + } + return f.unTrashDir(ctx, dir, true) + default: + return nil, fs.ErrorCommandNotFound + } +} + // ------------------------------------------------------------ // Fs returns the parent Fs @@ -2718,15 +3383,16 @@ func (f *Fs) getRemoteInfo(ctx context.Context, remote string) (info *drive.File // getRemoteInfoWithExport returns a drive.File and the export settings for the remote func (f *Fs) getRemoteInfoWithExport(ctx context.Context, remote string) ( info *drive.File, extension, exportName, exportMimeType string, isDocument bool, err error) { - leaf, directoryID, err := f.dirCache.FindRootAndPath(ctx, remote, false) + leaf, directoryID, err := f.dirCache.FindPath(ctx, remote, false) if err != nil { if err == fs.ErrorDirNotFound { return nil, "", "", "", false, fs.ErrorObjectNotFound } return nil, "", "", "", false, err } + directoryID = actualID(directoryID) - found, err := f.list(ctx, []string{directoryID}, leaf, false, true, false, func(item *drive.File) bool { + found, err := f.list(ctx, []string{directoryID}, leaf, false, false, f.opt.TrashedOnly, false, func(item *drive.File) bool { if !f.opt.SkipGdocs { extension, exportName, exportMimeType, isDocument = f.findExportFormat(item) if exportName == leaf { @@ -2776,7 +3442,7 @@ func (o *baseObject) SetModTime(ctx context.Context, modTime time.Time) error { var info *drive.File err := o.fs.pacer.Call(func() (bool, error) { var err error - info, err = o.fs.svc.Files.Update(o.id, updateInfo). + info, err = o.fs.svc.Files.Update(actualID(o.id), updateInfo). Fields(partialFields). SupportsAllDrives(true). Do() @@ -2827,7 +3493,7 @@ func (o *baseObject) httpResponse(ctx context.Context, url, method string, optio return req, res, nil } -// openDocumentFile represents an documentObject open for reading. +// openDocumentFile represents a documentObject open for reading. // Updates the object size after read successfully. type openDocumentFile struct { o *documentObject // Object we are reading for @@ -2902,10 +3568,13 @@ func (o *baseObject) open(ctx context.Context, url string, options ...fs.OpenOpt // Open an object for read func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) { + if o.mimeType == shortcutMimeTypeDangling { + return nil, errors.New("can't read dangling shortcut") + } if o.v2Download { var v2File *drive_v2.File err = o.fs.pacer.Call(func() (bool, error) { - v2File, err = o.fs.v2Svc.Files.Get(o.id). + v2File, err = o.fs.v2Svc.Files.Get(actualID(o.id)). Fields("downloadUrl"). SupportsAllDrives(true). Do() @@ -2984,7 +3653,7 @@ func (o *baseObject) update(ctx context.Context, updateInfo *drive.File, uploadM if size >= 0 && size < int64(o.fs.opt.UploadCutoff) { // Don't retry, return a retry error instead err = o.fs.pacer.CallNoRetry(func() (bool, error) { - info, err = o.fs.svc.Files.Update(o.id, updateInfo). + info, err = o.fs.svc.Files.Update(actualID(o.id), updateInfo). Media(in, googleapi.ContentType(uploadMimeType)). Fields(partialFields). SupportsAllDrives(true). @@ -3004,6 +3673,26 @@ func (o *baseObject) update(ctx context.Context, updateInfo *drive.File, uploadM // // The new object may have been created if an error is returned func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error { + // If o is a shortcut + if isShortcutID(o.id) { + // Delete it first + err := o.fs.delete(ctx, shortcutID(o.id), o.fs.opt.UseTrash) + if err != nil { + return err + } + // Then put the file as a new file + newObj, err := o.fs.PutUnchecked(ctx, in, src, options...) + if err != nil { + return err + } + // Update the object + if newO, ok := newObj.(*Object); ok { + *o = *newO + } else { + fs.Debugf(newObj, "Failed to update object %T from new object %T", o, newObj) + } + return nil + } srcMimeType := fs.MimeType(ctx, src) updateInfo := &drive.File{ MimeType: srcMimeType, @@ -3074,25 +3763,10 @@ func (o *linkObject) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo // Remove an object func (o *baseObject) Remove(ctx context.Context) error { - var err error - err = o.fs.pacer.Call(func() (bool, error) { - if o.fs.opt.UseTrash { - info := drive.File{ - Trashed: true, - } - _, err = o.fs.svc.Files.Update(o.id, &info). - Fields(""). - SupportsAllDrives(true). - Do() - } else { - err = o.fs.svc.Files.Delete(o.id). - Fields(""). - SupportsAllDrives(true). - Do() - } - return o.fs.shouldRetry(err) - }) - return err + if o.parents > 1 { + return errors.New("can't delete safely - has multiple parents") + } + return o.fs.delete(ctx, shortcutID(o.id), o.fs.opt.UseTrash) } // MimeType of an Object if known, "" otherwise @@ -3154,6 +3828,7 @@ var ( _ fs.Copier = (*Fs)(nil) _ fs.Mover = (*Fs)(nil) _ fs.DirMover = (*Fs)(nil) + _ fs.Commander = (*Fs)(nil) _ fs.DirCacheFlusher = (*Fs)(nil) _ fs.ChangeNotifier = (*Fs)(nil) _ fs.PutUncheckeder = (*Fs)(nil) diff --git a/backend/drive/drive_internal_test.go b/backend/drive/drive_internal_test.go index 22755e4..334191b 100644 --- a/backend/drive/drive_internal_test.go +++ b/backend/drive/drive_internal_test.go @@ -10,12 +10,16 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/pkg/errors" _ "github.com/rclone/rclone/backend/local" "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/fs/operations" + "github.com/rclone/rclone/fstest" "github.com/rclone/rclone/fstest/fstests" + "github.com/rclone/rclone/lib/random" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/api/drive/v3" @@ -268,6 +272,142 @@ func (f *Fs) InternalTestDocumentLink(t *testing.T) { } } +// TestIntegration/FsMkdir/FsPutFiles/Internal/Shortcuts +func (f *Fs) InternalTestShortcuts(t *testing.T) { + const ( + // from fstest/fstests/fstests.go + existingDir = "hello? sausage" + existingFile = `hello? sausage/êé/Hello, 世界/ " ' @ < > & ? + ≠/z.txt` + existingSubDir = "êé" + ) + ctx := context.Background() + srcObj, err := f.NewObject(ctx, existingFile) + require.NoError(t, err) + srcHash, err := srcObj.Hash(ctx, hash.MD5) + require.NoError(t, err) + assert.NotEqual(t, "", srcHash) + t.Run("Errors", func(t *testing.T) { + _, err := f.makeShortcut(ctx, "", f, "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "can't be root") + + _, err = f.makeShortcut(ctx, "notfound", f, "dst") + assert.Error(t, err) + assert.Contains(t, err.Error(), "can't find source") + + _, err = f.makeShortcut(ctx, existingFile, f, existingFile) + assert.Error(t, err) + assert.Contains(t, err.Error(), "not overwriting") + assert.Contains(t, err.Error(), "existing file") + + _, err = f.makeShortcut(ctx, existingFile, f, existingDir) + assert.Error(t, err) + assert.Contains(t, err.Error(), "not overwriting") + assert.Contains(t, err.Error(), "existing directory") + }) + t.Run("File", func(t *testing.T) { + dstObj, err := f.makeShortcut(ctx, existingFile, f, "shortcut.txt") + require.NoError(t, err) + require.NotNil(t, dstObj) + assert.Equal(t, "shortcut.txt", dstObj.Remote()) + dstHash, err := dstObj.Hash(ctx, hash.MD5) + require.NoError(t, err) + assert.Equal(t, srcHash, dstHash) + require.NoError(t, dstObj.Remove(ctx)) + }) + t.Run("Dir", func(t *testing.T) { + dstObj, err := f.makeShortcut(ctx, existingDir, f, "shortcutdir") + require.NoError(t, err) + require.Nil(t, dstObj) + entries, err := f.List(ctx, "shortcutdir") + require.NoError(t, err) + require.Equal(t, 1, len(entries)) + require.Equal(t, "shortcutdir/"+existingSubDir, entries[0].Remote()) + require.NoError(t, f.Rmdir(ctx, "shortcutdir")) + }) + t.Run("Command", func(t *testing.T) { + _, err := f.Command(ctx, "shortcut", []string{"one"}, nil) + require.Error(t, err) + require.Contains(t, err.Error(), "need exactly 2 arguments") + + _, err = f.Command(ctx, "shortcut", []string{"one", "two"}, map[string]string{ + "target": "doesnotexistremote:", + }) + require.Error(t, err) + require.Contains(t, err.Error(), "couldn't find target") + + _, err = f.Command(ctx, "shortcut", []string{"one", "two"}, map[string]string{ + "target": ".", + }) + require.Error(t, err) + require.Contains(t, err.Error(), "target is not a drive backend") + + dstObjI, err := f.Command(ctx, "shortcut", []string{existingFile, "shortcut2.txt"}, map[string]string{ + "target": fs.ConfigString(f), + }) + require.NoError(t, err) + dstObj := dstObjI.(*Object) + assert.Equal(t, "shortcut2.txt", dstObj.Remote()) + dstHash, err := dstObj.Hash(ctx, hash.MD5) + require.NoError(t, err) + assert.Equal(t, srcHash, dstHash) + require.NoError(t, dstObj.Remove(ctx)) + + dstObjI, err = f.Command(ctx, "shortcut", []string{existingFile, "shortcut3.txt"}, nil) + require.NoError(t, err) + dstObj = dstObjI.(*Object) + assert.Equal(t, "shortcut3.txt", dstObj.Remote()) + dstHash, err = dstObj.Hash(ctx, hash.MD5) + require.NoError(t, err) + assert.Equal(t, srcHash, dstHash) + require.NoError(t, dstObj.Remove(ctx)) + }) +} + +// TestIntegration/FsMkdir/FsPutFiles/Internal/UnTrash +func (f *Fs) InternalTestUnTrash(t *testing.T) { + ctx := context.Background() + + // Make some objects, one in a subdir + contents := random.String(100) + file1 := fstest.NewItem("trashDir/toBeTrashed", contents, time.Now()) + _, obj1 := fstests.PutTestContents(ctx, t, f, &file1, contents, false) + file2 := fstest.NewItem("trashDir/subdir/toBeTrashed", contents, time.Now()) + _, _ = fstests.PutTestContents(ctx, t, f, &file2, contents, false) + + // Check objects + checkObjects := func() { + fstest.CheckListingWithRoot(t, f, "trashDir", []fstest.Item{ + file1, + file2, + }, []string{ + "trashDir/subdir", + }, f.Precision()) + } + checkObjects() + + // Make sure we are using the trash + require.Equal(t, true, f.opt.UseTrash) + + // Remove the object and the dir + require.NoError(t, obj1.Remove(ctx)) + require.NoError(t, f.Purge(ctx, "trashDir/subdir")) + + // Check objects gone + fstest.CheckListingWithRoot(t, f, "trashDir", []fstest.Item{}, []string{}, f.Precision()) + + // Restore the object and directory + r, err := f.unTrashDir(ctx, "trashDir", true) + require.NoError(t, err) + assert.Equal(t, unTrashResult{Errors: 0, Untrashed: 2}, r) + + // Check objects restored + checkObjects() + + // Remove the test dir + require.NoError(t, f.Purge(ctx, "trashDir")) +} + func (f *Fs) InternalTest(t *testing.T) { // These tests all depend on each other so run them as nested tests t.Run("DocumentImport", func(t *testing.T) { @@ -282,6 +422,8 @@ func (f *Fs) InternalTest(t *testing.T) { }) }) }) + t.Run("Shortcuts", f.InternalTestShortcuts) + t.Run("UnTrash", f.InternalTestUnTrash) } var _ fstests.InternalTester = (*Fs)(nil) diff --git a/cmd/copy/copy.go b/cmd/copy/copy.go index 53fabd9..e80edc6 100644 --- a/cmd/copy/copy.go +++ b/cmd/copy/copy.go @@ -2,6 +2,7 @@ package copy import ( "context" + "github.com/rclone/rclone/cmd" "github.com/rclone/rclone/fs/config/flags" "github.com/rclone/rclone/fs/operations" @@ -21,7 +22,7 @@ func init() { var commandDefinition = &cobra.Command{ Use: "copy source:path dest:path", - Short: `Copy files from source to dest, skipping already copied`, + Short: `Copy files from source to dest, skipping already copied.`, Long: ` Copy the source to the destination. Doesn't transfer unchanged files, testing by size and modification time or @@ -65,12 +66,14 @@ option when copying a small number of files into a large destination can speed transfers up greatly. For example, if you have many files in /path/to/src but only a few of -them change every day, you can to copy all the files which have -changed recently very efficiently like this: +them change every day, you can copy all the files which have changed +recently very efficiently like this: rclone copy --max-age 24h --no-traverse /path/to/src remote: -**Note**: Use the ` + "`-P`" + `/` + "`--progress`" + ` flag to view real-time transfer statistics +**Note**: Use the ` + "`-P`" + `/` + "`--progress`" + ` flag to view real-time transfer statistics. + +**Note**: Use the ` + "`--dry-run` or the `--interactive`/`-i`" + ` flag to test without copying anything. `, Run: func(command *cobra.Command, args []string) { cmd.CheckArgs(2, 2, command, args) diff --git a/gc-local-mod-1.1.bat b/gc-local-mod-1.1.bat new file mode 100644 index 0000000..5af2265 --- /dev/null +++ b/gc-local-mod-1.1.bat @@ -0,0 +1,298 @@ +@echo off +title gclone-mod-1.1 is the best, batch file edited by Tomyummmm, original by RoshanConnor Yo yo + +color 0b +echo Hey Sexy! Wanna clone some TBs? +echo This version uses gclone-mod-1.1, updated and maintained by me (Tomyummmm) here: https://github.com/tomyummmm/gclone +echo ---------------------------------------------------------------------------------------------------------------------- +echo Configured Team Drives +gclone-mod-1.1 listremotes +echo ---------------------------------------------------------------------------------------------------------------------- +echo off + + +:menu +echo. +echo 1) COPY - Copy files from source to dest, skipping already copied. +echo 2) MOVE - Move files from source to dest. +echo 3) SYNC - Make source and dest identical, modifying destination only. +echo 4) SIZE - Return the total size and number of objects in remote:path. +echo 5) CHECK - Check if the files in the source and destination match. +echo 6) LIST +echo 7) DELETE / PURGE - Remove path / contents. +echo 8) DEDUPE - Interactively find duplicate files and delete/rename them. +echo 9) REMOVE EMPTY FOLDERS +echo 10) EMPTY TRASH +echo N) NCDU - Explore a remote with a text based user interface. +echo M) MD5SUM - Produces an md5sum file for all the objects in the path. +echo C) CONFIG - Enter an interactive configuration session. +echo A) ADVANCED - For experienced users only, command line. +echo Q) EXIT +echo. +set /P option="Choose your Mode: " +if %option% == 1 (goto copy) +if %option% == 2 (goto move) +if %option% == 3 (goto sync) +if %option% == 4 (goto size) +if %option% == 5 (goto check) +if %option% == 6 (goto list) +if %option% == 7 (goto delete) +if %option% == 8 (goto dedp) +if %option% == 9 (goto rmdi) +if %option% == 10 (goto empt) +if /I %option% == N (goto ncdu) +if /I %option% == M (goto md5) +if /I %option% == C (goto config) +if /I %option% == A (goto adv) +if /I %option% == Q (EXIT) +echo Invalid input! +goto menu +echo. + + +:copy +echo. +set /P src="[Enter Source Folder / TeamDrive] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder / TeamDrive] " +gclone-mod-1.1 copy %src% %dst% --transfers 50 --tpslimit-burst 50 --checkers 10 -vP --stats-one-line --stats=10s --ignore-existing --drive-server-side-across-configs --drive-chunk-size 128M --drive-acknowledge-abuse --drive-keep-revision-forever --fast-list +echo. +pause +goto menu + + + +:move +echo. +set /P src="[Enter Source Folder / TeamDrive] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder / TeamDrive] " +gclone-mod-1.1 move %src% %dst% --transfers 50 --tpslimit-burst 50 --checkers 10 -vP --stats-one-line --stats=10s --ignore-existing --drive-server-side-across-configs --drive-chunk-size 128M --drive-acknowledge-abuse --drive-keep-revision-forever --fast-list +echo. +pause +goto menu + + +:sync +echo. +set /P src="[Enter Source Folder / TeamDrive] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder / TeamDrive] " +gclone-mod-1.1 sync %src% %dst% --transfers 50 --tpslimit-burst 50 --checkers 10 -vP --stats-one-line --stats=10s --drive-server-side-across-configs --drive-chunk-size 128M --drive-acknowledge-abuse --drive-keep-revision-forever --fast-list +echo. +pause +goto menu + + +:check +echo. +echo 1) size Only compare the sizes, not the hashes as well. Use this for a quick check. +echo 2) default Compares sizes and hashes (MD5 or SHA1) and logs a report of files which don't match. Very slow. +echo 3) download Download the data from both remotes and check them against each other on the fly. This can be useful for remotes that don't support hashes or if you really want to check all the data. +echo. +set /P checktype="Type of Check? " +set /P src="[Enter Source Folder / TeamDrive] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder / TeamDrive] " +if %checktype% == 1 (gclone-mod-1.1 check %src% %dst% -P --drive-server-side-across-configs --fast-list --size-only) +if %checktype% == 2 (gclone-mod-1.1 check %src% %dst% -P --drive-server-side-across-configs --fast-list) +if %checktype% == 3 (gclone-mod-1.1 check %src% %dst% -P --drive-server-side-across-configs --fast-list --download) +echo. +pause +goto menu + + +:size +echo. +set /P src="[Enter Folder / TeamDrive] " +gclone-mod-1.1 size %src% --fast-list +echo. +pause +goto menu + + +:list +echo. +echo ---------------------------------------------------------------------------------------------------------------------- +echo Configured Team Drives +gclone-mod-1.1 listremotes +echo ---------------------------------------------------------------------------------------------------------------------- +echo. +echo 1) ls List the objects in the path with size and path. +echo 2) lsd List all directories/containers/buckets in the path. +echo 3) lsf List directories and objects in remote:path formatted for parsing +echo 4) lsjson List directories and objects in the path in JSON format. +echo 5) lsl List the objects in path with modification time, size and path. +echo 6) tree List the contents of the remote in a tree like fashion. +echo Q) Return to menu +echo. +set /P listtype="Type of List? " +if /I %listtype% == Q (goto menu) +set /P remote="[Enter Folder / TeamDrive] " +echo. +if %listtype% == 1 (gclone-mod-1.1 ls %remote%) +if %listtype% == 2 (gclone-mod-1.1 lsd %remote%) +if %listtype% == 3 (gclone-mod-1.1 lsf %remote%) +if %listtype% == 4 (gclone-mod-1.1 lsjson %remote%) +if %listtype% == 5 (gclone-mod-1.1 lsl %remote%) +if %listtype% == 6 (gclone-mod-1.1 tree %remote%) +echo. +pause +goto menu + + +:delete +echo. +echo 1) delete Remove the contents of path. +echo 2) deletefile Remove a single file from remote. +echo 3) purge Remove the path and all of its contents. +echo. +set /P deletetype="Type of Delete? " +set /P remote="[Enter Folder / TeamDrive] " +echo. +if %deletetype% == 1 (gclone-mod-1.1 delete %remote% -vP --stats-one-line --stats=15s --fast-list) +if %deletetype% == 2 (gclone-mod-1.1 deletefile %remote% -vP --stats-one-line --stats=15s --fast-list) +if %deletetype% == 3 (gclone-mod-1.1 purge %remote% -vP --stats-one-line --stats=15s --fast-list) +echo. +pause +goto menu + + +:dedp +echo. +set /P src="[Enter Folder / TeamDrive] " +set /P choice="Do you want to do a dry run? (y/n) " +if /I %choice%==y (goto drd) +if /I %choice%==n (goto nodrd) +echo. + + +:drd +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 dedupe --dedupe-mode newest %src% -v --dry-run --fast-list +echo ---------------------------------------------------------------------------------------------------------------------- +echo off +echo. +set /P choice="Proceed with Dedupe? (y/n) " +if /I %choice%==y (goto nodrd) +if /I %choice%==n (goto menu) +echo. + + + +:nodrd +echo ---------------------------------------------------------------------------------------------------------------------- +set /P choice="Do you want to PERMANENTLY delete the duplicates? (y - Permanent / n - Send to trash bin) " +if /I %choice%==y (gclone-mod-1.1 dedupe --dedupe-mode newest %src% -v --drive-use-trash=false --fast-list) +if /I %choice%==n (gclone-mod-1.1 dedupe --dedupe-mode newest %src% -v --fast-list) +echo. +pause +goto menu + + +:rmdi +echo. +set /P src="[Enter Folder / TeamDrive] " +set /P choice="Do you want to do a dry run? (y/n) " +if /I %choice%==y (goto drr) +if /I %choice%==n (goto nodrr) +echo. + + +:drr +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 rmdirs %src% -v --fast-list --dry-run +echo ---------------------------------------------------------------------------------------------------------------------- +echo off +echo. +set /P choice="Proceed with Removal of Empty Folders? (y/n) " +if /I %choice%==y (goto nodrr) +if /I %choice%==n (goto menu) +echo. + + +:nodrr +echo ---------------------------------------------------------------------------------------------------------------------- +set /P choice=Do you want to PERMANENTLY delete empty folders? (y - Permanent / n - Send to trash bin) +if /I %choice%==y (gclone-mod-1.1 rmdirs %src% -v --drive-use-trash=false --fast-list) +if /I %choice%==n (gclone-mod-1.1 rmdirs %src% -v --fast-list) +echo. +pause +goto menu + + +:empt +echo. +set /P src="[Enter TeamDrive ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P choice="Do you want to do a dry run? (y/n) " +if /I %choice%==y (goto emptdr) +if /I %choice%==n (goto emptnodr) + + +:emptdr +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 delete %src% -vP --drive-trashed-only --drive-use-trash=false --fast-list --dry-run +echo ---------------------------------------------------------------------------------------------------------------------- +echo off +echo. +set /P choice="Proceed to empty trash? (y/n) " +if /I %choice%==y (goto emptnodr) +if /I %choice%==n (goto menu) + + +:emptnodr +set /P choice="Are you sure? (y/n) " +if /I %choice%==y (gclone-mod-1.1 delete %src% -vP --drive-trashed-only --drive-use-trash=false --fast-list) +if /I %choice%==n (goto menu) +echo. +pause +goto menu + +:ncdu +echo. +set /P src="[Enter Folder / TeamDrive] " +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 ncdu %src% --fast-list +echo ---------------------------------------------------------------------------------------------------------------------- +echo. +goto menu + + +:md5 +echo. +echo ---------------------------------------------------------------------------------------------------------------------- +set /P remote="[Enter Folder / TeamDrive] " +echo. +gclone-mod-1.1 md5sum %remote% --fast-list +echo ---------------------------------------------------------------------------------------------------------------------- +echo. +pause +goto menu + + +:config +echo. +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 config +echo ---------------------------------------------------------------------------------------------------------------------- +echo. +goto menu + +:adv +setlocal EnableDelayedExpansion +echo. +echo ---------------------------------------------------------------------------------------------------------------------- +echo Command Line Interface for gclone-mod-1.1 +echo Enter your commands and flags, gclone-mod-1.1 is automatically typed for you. e.g. --help OR ls remote: +echo This batch script is gc-local, source and destination is remote:path or remote:{folder id}. +echo Enter Q to return to menu. +echo. +gclone-mod-1.1 listremotes +echo. +set /P command="[Enter command / flags] " +if /I "!command!"=="Q" (goto menu) +echo. +gclone-mod-1.1 !command! +echo. +goto adv \ No newline at end of file diff --git a/gc-remote-mod-1.1.bat b/gc-remote-mod-1.1.bat new file mode 100644 index 0000000..ad2781d --- /dev/null +++ b/gc-remote-mod-1.1.bat @@ -0,0 +1,284 @@ +@echo off +title gclone-mod-1.1 is the best, batch file edited by Tomyummmm, original by RoshanConnor Yo yo + +@ECHO off + (echo [GC]) > rclone.conf + (echo type = drive) >> rclone.conf + (echo scope = drive) >> rclone.conf + (echo service_account_file = accounts\1.json) >> rclone.conf + (echo service_account_file_path = accounts\) >> rclone.conf +echo. +color 0b +echo Hey Sexy! Wanna clone some TBs? +echo This version uses gclone-mod-1.1, updated and maintained by me (Tomyummmm) here: https://github.com/tomyummmm/gclone +echo ---------------------------------------------------------------------------------------------------------------------- +echo Configured Team Drives +gclone-mod-1.1 listremotes +echo ---------------------------------------------------------------------------------------------------------------------- +echo off + + +:menu +echo. +echo 1) COPY - Copy files from source to dest, skipping already copied. +echo 2) MOVE - Move files from source to dest. +echo 3) SYNC - Make source and dest identical, modifying destination only. +echo 4) SIZE - Return the total size and number of objects in remote:path. +echo 5) CHECK - Check if the files in the source and destination match. +echo 6) LIST +echo 7) DELETE / PURGE - Remove path / contents. +echo 8) DEDUPE - Interactively find duplicate files and delete/rename them. +echo 9) REMOVE EMPTY FOLDERS +echo 10) EMPTY TRASH +echo N) NCDU - Explore a remote with a text based user interface. +echo M) MD5SUM - Produces an md5sum file for all the objects in the path. +echo A) ADVANCED - For experienced users only, command line. +echo Q) EXIT +echo. +set /P option="Choose your Mode: " +if %option% == 1 (goto copy) +if %option% == 2 (goto move) +if %option% == 3 (goto sync) +if %option% == 4 (goto size) +if %option% == 5 (goto check) +if %option% == 6 (goto list) +if %option% == 7 (goto delete) +if %option% == 8 (goto dedp) +if %option% == 9 (goto rmdi) +if %option% == 10 (goto empt) +if /I %option% == N (goto ncdu) +if /I %option% == M (goto md5) +if /I %option% == A (goto adv) +if /I %option% == Q (EXIT) +echo Invalid input! +goto menu +echo. + + +:copy +echo. +set /P src="[Enter Source Folder ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder ID] " +gclone-mod-1.1 copy GC:{%src%} GC:{%dst%} --transfers 50 --tpslimit-burst 50 --checkers 10 -vP --stats-one-line --stats=10s --ignore-existing --drive-server-side-across-configs --drive-chunk-size 128M --drive-acknowledge-abuse --drive-keep-revision-forever --fast-list +echo. +pause +goto menu + + +:move +echo. +set /P src="[Enter Source Folder ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder ID] " +gclone-mod-1.1 move GC:{%src%} GC:{%dst%} --transfers 50 --tpslimit-burst 50 --checkers 10 -vP --stats-one-line --stats=10s --ignore-existing --drive-server-side-across-configs --drive-chunk-size 128M --drive-acknowledge-abuse --drive-keep-revision-forever --fast-list +echo. +pause +goto menu + + +:sync +echo. +set /P src="[Enter Source Folder ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder ID] " +gclone-mod-1.1 sync GC:{%src%} GC:{%dst%} --transfers 50 --tpslimit-burst 50 --checkers 10 -vP --stats-one-line --stats=10s --drive-server-side-across-configs --drive-chunk-size 128M --drive-acknowledge-abuse --drive-keep-revision-forever --fast-list +echo. +pause +goto menu + + +:check +echo. +echo 1) size Only compare the sizes, not the hashes as well. Use this for a quick check. +echo 2) default Compares sizes and hashes (MD5 or SHA1) and logs a report of files which don't match. Very slow. +echo 3) download Download the data from both remotes and check them against each other on the fly. This can be useful for remotes that don't support hashes or if you really want to check all the data. +echo. +set /P checktype="Type of Check? " +set /P src="[Enter Source Folder ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P dst="[Enter Destination Folder ID] " +if %checktype% == 1 (gclone-mod-1.1 check GC:{%src%} GC:{%dst%} -P --drive-server-side-across-configs --fast-list --size-only) +if %checktype% == 2 (gclone-mod-1.1 check GC:{%src%} GC:{%dst%} -P --drive-server-side-across-configs --fast-list) +if %checktype% == 3 (gclone-mod-1.1 check GC:{%src%} GC:{%dst%} -P --drive-server-side-across-configs --fast-list --download) +echo. +pause +goto menu + +:size +echo. +set /P src="[Enter Folder ID] " +gclone-mod-1.1 size GC:{%src%} --fast-list +echo. +pause +goto menu + + +:list +echo. +echo 1) ls List the objects in the path with size and path. +echo 2) lsd List all directories/containers/buckets in the path. +echo 3) lsf List directories and objects in remote:path formatted for parsing +echo 4) lsjson List directories and objects in the path in JSON format. +echo 5) lsl List the objects in path with modification time, size and path. +echo 6) tree List the contents of the remote in a tree like fashion. +echo. +set /P listtype="Type of List? " +set /P remote="[Enter Folder ID] " +echo. +if %listtype% == 1 (gclone-mod-1.1 ls GC:{%remote%}) +if %listtype% == 2 (gclone-mod-1.1 lsd GC:{%remote%}) +if %listtype% == 3 (gclone-mod-1.1 lsf GC:{%remote%}) +if %listtype% == 4 (gclone-mod-1.1 lsjson GC:{%remote%}) +if %listtype% == 5 (gclone-mod-1.1 lsl GC:{%remote%}) +if %listtype% == 6 (gclone-mod-1.1 tree GC:{%remote%}) +echo. +pause +goto menu + + +:delete +echo. +echo 1) delete Remove the contents of path. +echo 2) deletefile Remove a single file from remote. +echo 3) purge Remove the path and all of its contents. +echo. +set /P deletetype="Type of Delete? " +set /P remote="[Enter Folder ID] " +echo. +if %deletetype% == 1 (gclone-mod-1.1 delete GC:{%remote%} -vP --stats-one-line --stats=15s --fast-list) +if %deletetype% == 2 (gclone-mod-1.1 deletefile GC:{%remote%} -vP --stats-one-line --stats=15s --fast-list) +if %deletetype% == 3 (gclone-mod-1.1 purge GC:{%remote%} -vP --stats-one-line --stats=15s --fast-list) +echo. +pause +goto menu + + +:dedp +echo. +set /P src="[Enter Folder ID] " +set /P choice="Do you want to do a dry run? (y/n) " +if /I %choice%==y (goto drd) +if /I %choice%==n (goto nodrd) +echo. + + +:drd +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 dedupe --dedupe-mode newest GC:{%src%} -v --dry-run --fast-list +echo ---------------------------------------------------------------------------------------------------------------------- +echo off +echo. +set /P choice="Proceed with Dedupe? (y/n) " +if /I %choice%==y (goto nodrd) +if /I %choice%==n (goto menu) +echo. + + + +:nodrd +echo ---------------------------------------------------------------------------------------------------------------------- +set /P choice=Do you want to PERMANENTLY delete the duplicates? (y - Permanent / n - Send to trash bin) +if /I %choice%==y (gclone-mod-1.1 dedupe --dedupe-mode newest GC:{%src%} -v --drive-use-trash=false --fast-list) +if /I %choice%==n (gclone-mod-1.1 dedupe --dedupe-mode newest GC:{%src%} -v --fast-list) +echo. +pause +goto menu + + +:rmdi +echo. +set /P src="[Enter Folder ID] " +set /P choice="Do you want to do a dry run? (y/n) " +if /I %choice%==y (goto drr) +if /I %choice%==n (goto nodrr) +echo. + + +:drr +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 rmdirs GC:{%src%} -v --stats-one-line --stats=15s --fast-list --dry-run +echo ---------------------------------------------------------------------------------------------------------------------- +echo off +echo. +set /P choice="Proceed with Removal of Empty Folders? (y/n) " +if /I %choice%==y (goto nodrr) +if /I %choice%==n (goto menu) +echo. + + +:nodrr +echo ---------------------------------------------------------------------------------------------------------------------- +set /P choice="Do you want to PERMANENTLY delete empty folders? (y - Permanent / n - Send to trash bin) " +if /I %choice%==y (gclone-mod-1.1 rmdirs GC:{%src%} -v --stats-one-line --stats=15s --drive-use-trash=false --fast-list) +if /I %choice%==n (gclone-mod-1.1 rmdirs GC:{%src%} -v --stats-one-line --stats=15s --fast-list) +echo. +pause +goto menu + + +:empt +echo. +set /P src="[Enter TeamDrive ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +set /P choice="Do you want to do a dry run? (y/n) " +if /I %choice%==y (goto emptdr) +if /I %choice%==n (goto emptnodr) + + +:emptdr +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 delete GC:{%src%} -vP --drive-trashed-only --drive-use-trash=false --fast-list --dry-run +echo ---------------------------------------------------------------------------------------------------------------------- +echo off +echo. +set /P choice="Proceed to empty trash? (y/n) " +if /I %choice%==y (goto emptnodr) +if /I %choice%==n (goto menu) + + +:emptnodr +set /P choice="Are you sure? (y/n) " +if /I %choice%==y (gclone-mod-1.1 delete GC:{%src%} -vP --drive-trashed-only --drive-use-trash=false --fast-list) +if /I %choice%==n (goto menu) +echo. +pause +goto menu + + +:ncdu +echo. +set /P src="[Enter Folder ID] " +echo ---------------------------------------------------------------------------------------------------------------------- +gclone-mod-1.1 ncdu GC:{%src%} --fast-list +echo ---------------------------------------------------------------------------------------------------------------------- +echo. +goto menu + + +:md5 +echo. +echo ---------------------------------------------------------------------------------------------------------------------- +set /P remote="[Enter Folder ID] " +echo. +gclone-mod-1.1 md5sum GC:{%remote%} --fast-list +echo ---------------------------------------------------------------------------------------------------------------------- +echo. +pause +goto menu + +:adv +setlocal EnableDelayedExpansion +echo. +echo ---------------------------------------------------------------------------------------------------------------------- +echo Command Line Interface for gclone-mod-1.1 +echo Enter your commands and flags, gclone-mod-1.1 is automatically typed for you. e.g. --help OR ls remote: +echo This batch script is gc-remote, source and destination is GC:{folder id}. +echo Enter Q to return to menu. +echo. +set /P command="[Enter command / flags] " +if /I "!command!"=="Q" (goto menu) +echo. +gclone-mod-1.1 !command! +echo. +goto adv \ No newline at end of file diff --git a/gclone.go b/gclone.go index bec0824..cf3a009 100644 --- a/gclone.go +++ b/gclone.go @@ -1,15 +1,15 @@ package main import ( - _ "github.com/donwa/gclone/backend/all" // import all backends + _ "github.com/tomyummmm/gclone/backend/all" // import all backends "github.com/rclone/rclone/cmd" - _ "github.com/donwa/gclone/cmd/copy" + _ "github.com/tomyummmm/gclone/cmd/copy" _ "github.com/rclone/rclone/cmd/all" // import all commands "github.com/rclone/rclone/fs" _ "github.com/rclone/rclone/lib/plugin" // import plugins ) func main() { - fs.Version = fs.Version+"-mod1.3.1" + fs.Version = fs.Version+"-mod1.1" cmd.Main() } diff --git a/go.mod b/go.mod index d326540..da78f28 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ -module github.com/donwa/gclone +module github.com/tomyummmm/gclone + +go 1.15 require ( - github.com/pkg/errors v0.8.1 - github.com/rclone/rclone v1.51.0 - github.com/spf13/cobra v0.0.5 - github.com/stretchr/testify v1.4.0 - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 - google.golang.org/api v0.13.0 + github.com/pkg/errors v0.9.1 + github.com/rclone/rclone v1.53.3 + github.com/spf13/cobra v1.1.1 + github.com/stretchr/testify v1.6.1 + golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 + google.golang.org/api v0.36.0 ) - -go 1.13 diff --git a/go.sum b/go.sum index 63a1e1c..8f43fcb 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -bazil.org/fuse v0.0.0-20191225233854-3a99aca11732 h1:gaB1+kZCJDExjlrdy37gIwxV0M7v81EzIFKQZ5o5YV0= -bazil.org/fuse v0.0.0-20191225233854-3a99aca11732/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 h1:SRsZGA7aFnCZETmov57jwPrWuTmaZK6+4R4v5FUe1/c= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -7,26 +7,45 @@ cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6A cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.47.0 h1:1JUtpcY9E7+eTospEwWS2QXP3DEn7poB3E2j0jN74mM= -cloud.google.com/go v0.47.0/go.mod h1:5p3Ky/7f3N10VBkhuR5LFtddroTiMyjZV/Kj5qOQFxU= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.59.0/go.mod h1:qJxNOVCRTxHfwLhvDxxSI9vQc1zI59b9pEglp1Iv60E= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0 h1:eWRCuwubtDrCJG0oSUMgnsbD4CmPFQF2ei4OFbXvwww= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gitea.com/goftp/file-driver v0.0.0-20190712091345-f79c2ed973f8/go.mod h1:ghdogu0Da3rwYCSJ20JPgTiMcDpzeRbzvuFIOOW3G7w= -gitea.com/goftp/file-driver v0.0.0-20190812052443-efcdcba68b34 h1:3wshUWDKHcy8hrNafCS4rtuAdON2KYsuznc05zdHTrQ= -gitea.com/goftp/file-driver v0.0.0-20190812052443-efcdcba68b34/go.mod h1:6+f1gclV97PmaVmE4YJbH3KIKnl+r3/HWR0zD/z1CG4= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o= -github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= +github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs= +github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.6.0 h1:UCTq22yE3RPgbU/8u4scfnnzuCW6pwQ9n+uBtV78ouo= -github.com/Azure/go-autorest/autorest/adal v0.6.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw= +github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= @@ -38,15 +57,24 @@ github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1Gn github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= -github.com/Unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e h1:ZaFHdRwv6wJQMYsg5qITIsqWRqZRvUETiq0xxrl+8fc= -github.com/Unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw= +github.com/Unknwon/goconfig v0.0.0-20191126170842-860a72fb44fd h1:+CYOsXi89xOqBkj7CuEJjA2It+j+R3ngUZEydr6mtkw= +github.com/Unknwon/goconfig v0.0.0-20191126170842-860a72fb44fd/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw= github.com/a8m/tree v0.0.0-20181222104329-6a0b80129de4 h1:mK1/QgFPU4osbhjJ26B1w738kjQHaGJcon8uCLMS8fk= github.com/a8m/tree v0.0.0-20181222104329-6a0b80129de4/go.mod h1:FSdwKX97koS5efgm8WevNf7XS3PqtyFkKDDXrz778cg= +github.com/aalpar/deheap v0.0.0-20200318053559-9a0c2883bd56 h1:hJO00l0f92EcQn8Ygc9Y0oP++eESKvcyp+KedtfT5SQ= +github.com/aalpar/deheap v0.0.0-20200318053559-9a0c2883bd56/go.mod h1:EJFoWbcEEVK22GYKONJjtMNamGYe6p+3x1Pr6zV5gFs= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anacrolix/dms v1.1.0 h1:vbBXZS7T5FaZm+9p1pdmVVo9tN3qdc27bKSETdeT3xo= github.com/anacrolix/dms v1.1.0/go.mod h1:msPKAoppoNRfrYplJqx63FZ+VipDZ4Xsj3KzIQxyU7k= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= @@ -54,233 +82,466 @@ github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54g github.com/anacrolix/ffprobe v1.0.0/go.mod h1:BIw+Bjol6CWjm/CRWrVLk2Vy+UYlkgmBZ05vpSYqZPw= github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY= github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/aws-sdk-go v1.25.31 h1:14mdh3HsTgRekePPkYcCbAaEXJknc3mN7f4XfsiMMDA= -github.com/aws/aws-sdk-go v1.25.31/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/billziss-gh/cgofuse v1.2.0 h1:FMdQSygSBpD4yEPENJcmvfCdmNWMVkPLlD7wWdl/7IA= -github.com/billziss-gh/cgofuse v1.2.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM= +github.com/aws/aws-sdk-go v1.32.11 h1:1nYF+Tfccn/hnAZsuwPPMSCVUVnx3j6LKOpx/WhgH0A= +github.com/aws/aws-sdk-go v1.32.11/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/billziss-gh/cgofuse v1.4.0 h1:kju2jDmdNuDDCrxPob2ggmZr5Mj/odCjU1Y8kx0Th9E= +github.com/billziss-gh/cgofuse v1.4.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.1/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/calebcase/tmpfile v1.0.2-0.20200602150926-3af473ef8439/go.mod h1:iErLeG/iqJr8LaQ/gYRv4GXdqssi3jg4iSzvrA06/lw= +github.com/calebcase/tmpfile v1.0.2 h1:1AGuhKiUu4J6wxz6lxuF6ck3f8G2kaV6KSEny0RGCig= +github.com/calebcase/tmpfile v1.0.2/go.mod h1:iErLeG/iqJr8LaQ/gYRv4GXdqssi3jg4iSzvrA06/lw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/djherbis/times v1.2.0 h1:xANXjsC/iBqbO00vkWlYwPWgBgEVU6m6AFYg0Pic+Mc= -github.com/djherbis/times v1.2.0/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dropbox/dropbox-sdk-go-unofficial v5.4.0+incompatible h1:9jnukMIowLSo3SY7+GTwxmYJv4QC0LxXbo97zHWCyoc= -github.com/dropbox/dropbox-sdk-go-unofficial v5.4.0+incompatible/go.mod h1:lr+LhMM3F6Y3lW1T9j2U5l7QeuWm87N9+PPXo3yH4qY= +github.com/dropbox/dropbox-sdk-go-unofficial v5.6.0+incompatible h1:DtumzkLk2zZ2SeElEr+VNz+zV7l+BTe509cV4sKPXbM= +github.com/dropbox/dropbox-sdk-go-unofficial v5.6.0+incompatible/go.mod h1:lr+LhMM3F6Y3lW1T9j2U5l7QeuWm87N9+PPXo3yH4qY= github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= -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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss= -github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= -github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= +github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= +github.com/hanwen/go-fuse/v2 v2.0.3 h1:kpV28BKeSyVgZREItBLnaVBvOEwv2PuhNdKetwnvNHo= +github.com/hanwen/go-fuse/v2 v2.0.3/go.mod h1:0EQM6aH2ctVpvZ6a+onrQ/vaykxh2GH7hy3e13vzTUY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY= -github.com/jlaffaye/ftp v0.0.0-20191025175106-a59fe673c9b2 h1:WY3P4euRv9s8F2rpZUK1jnk4ZMiV3O2ltdnoZK/GTUU= -github.com/jlaffaye/ftp v0.0.0-20191025175106-a59fe673c9b2/go.mod h1:PwUeyujmhaGohgOf0kJKxPfk3HcRv8QD/wAUN44go4k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jlaffaye/ftp v0.0.0-20200720194710-13949d38913e h1:itZyHiOkiB8mIGouegRNLM9LttGQ3yrgRmp/J/6H/0g= +github.com/jlaffaye/ftp v0.0.0-20200720194710-13949d38913e/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jzelinskie/whirlpool v0.0.0-20170603002051-c19460b8caa6 h1:RyOL4+OIUc6u5ac2LclitlZvFES6k+sg18fBMfxFUUs= github.com/jzelinskie/whirlpool v0.0.0-20170603002051-c19460b8caa6/go.mod h1:KmHnJWQrgEvbuy0vcvj00gtMqbvNn1L+3YUZLK/B92c= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.10.11/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koofr/go-httpclient v0.0.0-20190818202018-e0dc8fd921dc h1:Y+Dob6xos1WxridFN8sIiWPOlEQzgeF9ij9dXOsXK44= -github.com/koofr/go-httpclient v0.0.0-20190818202018-e0dc8fd921dc/go.mod h1:3xszwh+rNrYk1r9SStc4iJ326gne1OaBcrdB1ACsbzI= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koofr/go-httpclient v0.0.0-20200420163713-93aa7c75b348 h1:Lrn8srO9JDBCf2iPjqy62stl49UDwoOxZ9/NGVi+fnk= +github.com/koofr/go-httpclient v0.0.0-20200420163713-93aa7c75b348/go.mod h1:JBLy//Q5jzU3XSMxdONTD5EIj1LhTPktosxG2Bw1iho= github.com/koofr/go-koofrclient v0.0.0-20190724113126-8e5366da203a h1:02cx9xF4W2FQ1oh8CK9dWV5BnZK2mUtcbr9xR+bZiKk= github.com/koofr/go-koofrclient v0.0.0-20190724113126-8e5366da203a/go.mod h1:MRAz4Gsxd+OzrZ0owwrUHc0zLESL+1Y5syqK/sJxK2A= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190805055040-f9202b1cfdeb h1:hXqqXzQtJbENrsb+rsIqkVqcg4FUJL0SQFGw08Dgivw= -github.com/mattn/go-ieproxy v0.0.0-20190805055040-f9202b1cfdeb/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.11-0.20191112051248-2a2f0ea997f9 h1:tM1L+QoyOIq/0KiBQ4y/jUW0jxB5kz35bz+PSoQYjq8= -github.com/mattn/go-isatty v0.0.11-0.20191112051248-2a2f0ea997f9/go.mod h1:cxQpGCW53krnBJYXw0m6SYdk+OIHR4jbEstSUj/+MQ4= -github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= +github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +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.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +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= +github.com/minio/minio-go/v6 v6.0.46/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg= +github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncw/go-acd v0.0.0-20171120105400-887eb06ab6a2 h1:VlXvEx6JbFp7F9iz92zXP2Ew+9VupSpfybr+TxmjdH0= github.com/ncw/go-acd v0.0.0-20171120105400-887eb06ab6a2/go.mod h1:MLIrzg7gp/kzVBxRE1olT7CWYMCklcUWU+ekoxOD9x0= -github.com/ncw/swift v1.0.49 h1:eQaKIjSt/PXLKfYgzg01nevmO+CMXfXGRhB1gOhDs7E= -github.com/ncw/swift v1.0.49/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/ncw/swift v1.0.52 h1:ACF3JufDGgeKp/9mrDgQlEgS8kRYC4XKcuzj/8EJjQU= +github.com/ncw/swift v1.0.52/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= -github.com/nsf/termbox-go v0.0.0-20191229070316-58d4fcbce2a7 h1:OkWEy7aQeQTbgdrcGi9bifx+Y6bMM7ae7y42hDFaBvA= -github.com/nsf/termbox-go v0.0.0-20191229070316-58d4fcbce2a7/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 h1:lh3PyZvY+B9nFliSGTn5uFuqQQJGuNrD0MLCokv09ag= +github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd h1:+iAPaTbi1gZpcpDwe/BW1fx7Xoesv69hLNGPheoyhBs= github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd/go.mod h1:4soZNh0zW0LtYGdQ416i0jO0EIqMGcbtaspRS4BDvRQ= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= -github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M= -github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pengsrc/go-shared v0.2.1-0.20190131101655-1999055a4a14 h1:XeOYlK9W1uCmhjJSsY78Mcuh7MVkNjTzmHx1yBzizSU= github.com/pengsrc/go-shared v0.2.1-0.20190131101655-1999055a4a14/go.mod h1:jVblp62SafmidSkvWrXyxAme3gaTfEtWwRPGz5cpvHg= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.11.0 h1:4Zv0OGbpkg4yNuUtH0s8rvoYxRCNyT29NVUo6pgPmxI= +github.com/pkg/sftp v1.11.0/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/putdotio/go-putio v0.0.0-20190822121956-19b9c636c877 h1:sKIa5MAIViLAnQbEo+uiDi2FMowy8KcdZW8XZpmyNxs= -github.com/putdotio/go-putio v0.0.0-20190822121956-19b9c636c877/go.mod h1:EWtDL88jJLLWZzywr0QaPO+mGP8gFpvl8dcox8qTk3Y= -github.com/rclone/rclone v1.51.0 h1:tna+E5mF9BxC6ZVpAfy/k2/cDHej/5JtPA0Ao2BB58I= -github.com/rclone/rclone v1.51.0/go.mod h1:H4jaCoYf6554GT/f8HZ6IwnNbkCXI9HyXwMn8+FLivs= -github.com/rfjakob/eme v0.0.0-20171028163933-2222dbd4ba46 h1:w2CpS5muK+jyydnmlkqpAhzKmHmMBzBkfYUDjQNS1Dk= -github.com/rfjakob/eme v0.0.0-20171028163933-2222dbd4ba46/go.mod h1:U2bmx0hDj8EyDdcxmD5t3XHDnBFnyNNc22n1R4008eM= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8 h1:Y258uzXU/potCYnQd1r6wlAnoMB68BiCkCcCnKx1SH8= +github.com/putdotio/go-putio/putio v0.0.0-20200123120452-16d982cac2b8/go.mod h1:bSJjRokAHHOhA+XFxplld8w2R/dXLH7Z3BZ532vhFwU= +github.com/rclone/rclone v1.53.3 h1:5U3jfInvxs3me4xk1tKd5luJYAiQbhgTJxYTnJcAqN4= +github.com/rclone/rclone v1.53.3/go.mod h1:cJ7ZV0fRywUjlZBaLynHsrfRF40bm4DnozkyM8LxFiY= +github.com/rfjakob/eme v1.1.1 h1:t+CgvcOn+eDvj2xdglxsSnkgg8LM8jwdxnV7OnsrTn0= +github.com/rfjakob/eme v1.1.1/go.mod h1:U2bmx0hDj8EyDdcxmD5t3XHDnBFnyNNc22n1R4008eM= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk= github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e h1:VAzdS5Nw68fbf5RZ8RDVlUvPXNU6Z3jtPCK/qvm4FoQ= -github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= -github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= -github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spacemonkeygo/monkit/v3 v3.0.4/go.mod h1:JcK1pCbReQsOsMKF/POFSZCq7drXFybgGmbc27tuwes= +github.com/spacemonkeygo/monkit/v3 v3.0.5/go.mod h1:JcK1pCbReQsOsMKF/POFSZCq7drXFybgGmbc27tuwes= +github.com/spacemonkeygo/monkit/v3 v3.0.7-0.20200515175308-072401d8c752 h1:WcQDknqg0qajLNYKv3mXgbkWlYs5rPgZehGJFWePHVI= +github.com/spacemonkeygo/monkit/v3 v3.0.7-0.20200515175308-072401d8c752/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4= +github.com/spacemonkeygo/monotime v0.0.0-20180824235756-e3f48a95f98a/go.mod h1:ul4bvvnCOPZgq8w0nTkSmWVg/hauVpFS97Am1YM1XXo= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/t3rm1n4l/go-mega v0.0.0-20200117211730-79a813bb328d h1:GsRmok9VXIEc5B6SmRWuuO9hx4x2YBqFqgOXGt9Xs94= -github.com/t3rm1n4l/go-mega v0.0.0-20200117211730-79a813bb328d/go.mod h1:XWL4vDyd3JKmJx+hZWUVgCNmmhZ2dTBcaNDcxH465s0= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/t3rm1n4l/go-mega v0.0.0-20200416171014-ffad7fcb44b8 h1:IGJQmLBLYBdAknj21W3JsVof0yjEXfy1Q0K3YZebDOg= +github.com/t3rm1n4l/go-mega v0.0.0-20200416171014-ffad7fcb44b8/go.mod h1:XWL4vDyd3JKmJx+hZWUVgCNmmhZ2dTBcaNDcxH465s0= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k= +github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20191102193632-94c173a94d60 h1:Ud2neINE1YFEwrcJ4EqnbRZlm9R3T8SuFKeqjIw7k44= -github.com/youmark/pkcs8 v0.0.0-20191102193632-94c173a94d60/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yunify/qingstor-sdk-go/v3 v3.1.1 h1:jQkY9N+zSL8h8CqgrDQpXe8/mqJOx8vgGjk6O//RA/4= -github.com/yunify/qingstor-sdk-go/v3 v3.1.1/go.mod h1:KciFNuMu6F4WLk9nGwwK69sCGKLCdd9f97ac/wfumS4= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +github.com/youmark/pkcs8 v0.0.0-20200520070018-fad002e585ce h1:F5MEHq8k6JiE10MNYaQjbKRdF1xWkOavn9aoSrHqGno= +github.com/youmark/pkcs8 v0.0.0-20200520070018-fad002e585ce/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yunify/qingstor-sdk-go/v3 v3.2.0 h1:9sB2WZMgjwSUNZhrgvaNGazVltoFUUfuS9f0uCWtTr8= +github.com/yunify/qingstor-sdk-go/v3 v3.2.0/go.mod h1:KciFNuMu6F4WLk9nGwwK69sCGKLCdd9f97ac/wfumS4= +github.com/zeebo/admission/v3 v3.0.1/go.mod h1:BP3isIv9qa2A7ugEratNq1dnl2oZRXaQUGdU7WXKtbw= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g= +github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo= +github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54/go.mod h1:EI8LcOBDlSL3POyqwC1eJhOYlMBMidES+613EtmmT5w= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -goftp.io/server v0.0.0-20190712054601-1149070ae46b/go.mod h1:xreggPYu7ZuNe9PfbxiQca7bYGwU44IvlCCg3KzWJtQ= -goftp.io/server v0.0.0-20190812034929-9b3874d17690/go.mod h1:99FISrRpwKfaL4Ey/dX8N48WToveng/s2OXR5sJ3cnc= -goftp.io/server v0.0.0-20190812052725-72a57b186803 h1:I2IgXYRuOZ6LceE7VY6aSnYuUy6Wot3WFhqI5WsAHXQ= -goftp.io/server v0.0.0-20190812052725-72a57b186803/go.mod h1:eDjthxa5tFTS2JVry2jHt1g9y3J0Vgu2Nd+lmNWev7Y= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +goftp.io/server v0.4.0 h1:hqsVdwd1/l6QtYxD9pxca9mEAJYZ7+FPCnmeXKXHQNw= +goftp.io/server v0.4.0/go.mod h1:hFZeR656ErRt3ojMKt7H10vQ5nuWV1e0YeUTeorlR6k= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0= -golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -290,13 +551,26 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -304,27 +578,59 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191109021931-daa7c04131f5 h1:bHNaocaoJxYBo5cw41UyTMLjYlb8wPY7+WFrnklbHOM= -golang.org/x/net v0.0.0-20191109021931-daa7c04131f5/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -333,18 +639,50 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/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-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -359,21 +697,73 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200820180210-c8f393745106/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a h1:+77BOOi9CMFjpy3D2P/OnfSSmC/Hx/fGAQJUAQaM2gc= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -382,30 +772,98 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200623002339-fbb79eadd5eb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e h1:wYR00/Ht+i/79g/gzhdehBgLIJCklKoc8Q/NebdzzpY= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +storj.io/common v0.0.0-20200729140050-4c1ddac6fa63 h1:BkRvlginTJGi0yAkpN+4ZKm2YpG63bDSDFLQtXYxxdg= +storj.io/common v0.0.0-20200729140050-4c1ddac6fa63/go.mod h1:ILr54ISCqCQ6MmIwT7eaR/fEGrBfgfxiPt8nmpWqnUM= +storj.io/drpc v0.0.14 h1:GCBdymTt1BRw4oHmmUZZlxYXLVRxxYj6x3Ivide2J+I= +storj.io/drpc v0.0.14/go.mod h1:82nfl+6YwRwF6UG31cEWWUqv/FaKvP5SGqUvoqTxCMA= +storj.io/uplink v1.2.0 h1:7gGfkTv7zT9ivSCMqu7QirUMdHVaeBnlZgqgWhRKEd4= +storj.io/uplink v1.2.0/go.mod h1:U7VFTdoZiBgmijKdvB5dUfi6V5Ew9PAHhUVyy8rJWBs=