├── Makefile ├── .gitignore ├── cmd.go ├── .github └── workflows │ └── build.yaml ├── opcmds ├── cmd_ping.go ├── cmd_connect.go ├── cmd_editconf.go ├── cmd_listpd.go └── cmd_liststores.go ├── .goreleaser.yml ├── kvcmds ├── cmd_get.go ├── cmd_del.go ├── cmd_put.go ├── cmd_bench.go ├── cmd_explain.go ├── cmd_delall.go ├── cmd_count.go ├── cmd_delete_prefix.go ├── cmd_backup.go ├── cmd_bench_ycsb.go ├── cmd_query.go ├── cmd_loadfile.go ├── cmd_scan.go └── cmd_var.go ├── cmd_flags.go ├── utils ├── variables.go └── utils.go ├── README.md ├── query └── query_txn.go ├── go.mod ├── client ├── rawkv_client.go ├── client.go └── txnkv_client.go ├── cli └── main.go ├── sample.csv ├── LICENSE └── go.sum /Makefile: -------------------------------------------------------------------------------- 1 | default: build 2 | 3 | build: export GO111MODULE=on 4 | build: 5 | ifeq ($(TAGS),) 6 | $(CGO_FLAGS) go build -o bin/tcli ./cli/main.go 7 | else 8 | $(CGO_FLAGS) go build -tags "$(TAGS)" -o bin/tcli ./cli/*.go 9 | endif 10 | 11 | check: 12 | golint -set_exit_status . 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | bin/ 17 | .idea/ 18 | release 19 | *.pprof 20 | query/testdata 21 | -------------------------------------------------------------------------------- /cmd.go: -------------------------------------------------------------------------------- 1 | package tcli 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // Cmd is an abstraction of an interactable command 8 | type Cmd interface { 9 | // Help is a help message 10 | Help() string 11 | // LongHelp is a long help message 12 | LongHelp() string 13 | // Name is the name of the command 14 | Name() string 15 | // Alias is the alias of the command 16 | Alias() []string 17 | // Handler is the handler of the command 18 | // `ishell` is stored in ctx 19 | Handler() func(ctx context.Context) 20 | // Completer 21 | // Completer() func(ctx context.Context, args []string) []string 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3.0.0 14 | - name: Set up Go 15 | uses: actions/setup-go@v3 16 | with: 17 | go-version: 1.16.x 18 | - name: Run GoReleaser 19 | uses: goreleaser/goreleaser-action@v2.9.1 20 | with: 21 | version: latest 22 | args: release --rm-dist 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GH_PUBLISH_SECRETS }} 25 | -------------------------------------------------------------------------------- /opcmds/cmd_ping.go: -------------------------------------------------------------------------------- 1 | package opcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/tcli" 7 | ) 8 | 9 | // not implemented yet 10 | 11 | type PingCmd struct{} 12 | 13 | var _ tcli.Cmd = PingCmd{} 14 | 15 | func (c PingCmd) Name() string { return ".ping" } 16 | func (c PingCmd) Alias() []string { return []string{".p"} } 17 | func (c PingCmd) Help() string { 18 | return "ping pd, usage: [.ping|.p] 192.168.1.1:2379" 19 | } 20 | 21 | func (c PingCmd) LongHelp() string { 22 | return c.Help() 23 | } 24 | 25 | func (c PingCmd) Handler() func(ctx context.Context) { 26 | return func(ctx context.Context) { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Official documentation at http://goreleaser.com 2 | project_name: tcli 3 | builds: 4 | - env: 5 | - CGO_ENABLED=0 6 | binary: tcli 7 | main: ./cli/main.go 8 | goos: 9 | - windows 10 | - linux 11 | - darwin 12 | ignore: 13 | - goarch: arm 14 | - goarch: arm64 15 | - goarch: 386 16 | ldflags: 17 | - -w 18 | - -s 19 | dist: release 20 | archives: 21 | - name_template: "{{ .Binary }}-{{ .Os }}-{{ .Arch }}" 22 | format_overrides: 23 | - goos: windows 24 | format: zip 25 | files: 26 | - README.md 27 | checksum: 28 | name_template: 'checksums.txt' 29 | snapshot: 30 | name_template: "{{ .Tag }}-next-{{.ShortCommit}}" 31 | -------------------------------------------------------------------------------- /opcmds/cmd_connect.go: -------------------------------------------------------------------------------- 1 | package opcmds 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/abiosoft/ishell" 8 | "github.com/c4pt0r/tcli" 9 | ) 10 | 11 | type ConnectCmd struct{} 12 | 13 | var _ tcli.Cmd = ConnectCmd{} 14 | 15 | func (c ConnectCmd) Name() string { return ".connect" } 16 | func (c ConnectCmd) Alias() []string { return []string{".c", ".conn"} } 17 | func (c ConnectCmd) Help() string { 18 | return "connect to a tikv cluster, usage: [.connect|.conn|.c] [pd addr], example: .c 192.168.1.1:2379" 19 | } 20 | 21 | func (c ConnectCmd) LongHelp() string { 22 | return c.Help() 23 | } 24 | 25 | func (c ConnectCmd) Handler() func(ctx context.Context) { 26 | return func(ctx context.Context) { 27 | ic := ctx.Value("ishell").(*ishell.Context) 28 | if len(ic.Args) > 0 { 29 | ic.SetPrompt(fmt.Sprintf("[%s] >>> ", ic.Args[0])) 30 | } else { 31 | ic.Println(ic.Cmd.HelpText()) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /opcmds/cmd_editconf.go: -------------------------------------------------------------------------------- 1 | package opcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/AlecAivazis/survey/v2" 7 | "github.com/c4pt0r/tcli/utils" 8 | ) 9 | 10 | // not implemented yet 11 | 12 | type ConfigEditorCmd struct{} 13 | 14 | var _ ConfigEditorCmd = ConfigEditorCmd{} 15 | 16 | func (c ConfigEditorCmd) Name() string { return ".config" } 17 | func (c ConfigEditorCmd) Alias() []string { return []string{".config"} } 18 | func (c ConfigEditorCmd) Help() string { 19 | return "edit tikv config" 20 | } 21 | 22 | func (c ConfigEditorCmd) LongHelp() string { 23 | return c.Help() 24 | } 25 | 26 | func (c ConfigEditorCmd) Handler() func(ctx context.Context) { 27 | return func(ctx context.Context) { 28 | prompt := &survey.Editor{ 29 | Message: "Edit TiKV Config File", 30 | Default: "TODO", 31 | HideDefault: true, 32 | AppendDefault: true, 33 | } 34 | var content string 35 | survey.AskOne(prompt, &content) 36 | // TODO 37 | utils.Print(content) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /opcmds/cmd_listpd.go: -------------------------------------------------------------------------------- 1 | package opcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/tcli" 7 | "github.com/c4pt0r/tcli/client" 8 | "github.com/c4pt0r/tcli/utils" 9 | ) 10 | 11 | type ListPDCmd struct{} 12 | 13 | var _ tcli.Cmd = ListPDCmd{} 14 | 15 | func (c ListPDCmd) Name() string { return ".pd" } 16 | func (c ListPDCmd) Alias() []string { return []string{".pd"} } 17 | func (c ListPDCmd) Help() string { 18 | return "list pd instances in cluster" 19 | } 20 | 21 | func (c ListPDCmd) LongHelp() string { 22 | return c.Help() 23 | } 24 | 25 | func (c ListPDCmd) Handler() func(ctx context.Context) { 26 | return func(ctx context.Context) { 27 | utils.OutputWithElapse(func() error { 28 | pds, err := client.GetTiKVClient().GetPDs() 29 | if err != nil { 30 | return err 31 | } 32 | 33 | var output [][]string = [][]string{ 34 | (client.PDInfo).TableTitle(client.PDInfo{}), 35 | } 36 | for _, pd := range pds { 37 | output = append(output, pd.Flatten()) 38 | } 39 | utils.PrintTable(output) 40 | return nil 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /opcmds/cmd_liststores.go: -------------------------------------------------------------------------------- 1 | package opcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/tcli" 7 | "github.com/c4pt0r/tcli/utils" 8 | 9 | "github.com/c4pt0r/tcli/client" 10 | ) 11 | 12 | type ListStoresCmd struct{} 13 | 14 | var _ tcli.Cmd = ListStoresCmd{} 15 | 16 | func (c ListStoresCmd) Name() string { return ".stores" } 17 | func (c ListStoresCmd) Alias() []string { return []string{".stores"} } 18 | func (c ListStoresCmd) Help() string { 19 | return "list tikv stores in cluster" 20 | } 21 | 22 | func (c ListStoresCmd) LongHelp() string { 23 | return c.Help() 24 | } 25 | 26 | func (c ListStoresCmd) Handler() func(ctx context.Context) { 27 | return func(ctx context.Context) { 28 | utils.OutputWithElapse(func() error { 29 | stores, err := client.GetTiKVClient().GetStores() 30 | if err != nil { 31 | return err 32 | } 33 | 34 | var output [][]string = [][]string{ 35 | (client.StoreInfo).TableTitle(client.StoreInfo{}), 36 | } 37 | for _, store := range stores { 38 | output = append(output, store.Flatten()) 39 | } 40 | utils.PrintTable(output) 41 | return nil 42 | }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kvcmds/cmd_get.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/tcli" 7 | "github.com/c4pt0r/tcli/utils" 8 | 9 | "github.com/c4pt0r/tcli/client" 10 | ) 11 | 12 | type GetCmd struct{} 13 | 14 | var _ tcli.Cmd = GetCmd{} 15 | 16 | func (c GetCmd) Name() string { return "get" } 17 | func (c GetCmd) Alias() []string { return []string{"g"} } 18 | func (c GetCmd) Help() string { 19 | return `get [key]` 20 | } 21 | 22 | func (c GetCmd) LongHelp() string { 23 | return c.Help() 24 | } 25 | 26 | func (c GetCmd) Handler() func(ctx context.Context) { 27 | return func(ctx context.Context) { 28 | utils.OutputWithElapse(func() error { 29 | ic := utils.ExtractIshellContext(ctx) 30 | if len(ic.Args) < 1 { 31 | utils.Print(c.LongHelp()) 32 | return nil 33 | } 34 | s := ic.RawArgs[1] 35 | // it's a hex string literal 36 | k, err := utils.GetStringLit(s) 37 | if err != nil { 38 | return err 39 | } 40 | kv, err := client.GetTiKVClient().Get(context.TODO(), client.Key(k)) 41 | if err != nil { 42 | return err 43 | } 44 | kvs := []client.KV{kv} 45 | client.KVS(kvs).Print() 46 | return nil 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kvcmds/cmd_del.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/tcli" 7 | "github.com/c4pt0r/tcli/client" 8 | "github.com/c4pt0r/tcli/utils" 9 | ) 10 | 11 | type DeleteCmd struct{} 12 | 13 | var _ tcli.Cmd = &DeleteCmd{} 14 | 15 | func (c DeleteCmd) Name() string { return "del" } 16 | func (c DeleteCmd) Alias() []string { return []string{"remove", "delete", "rm"} } 17 | func (c DeleteCmd) Help() string { 18 | return `delete a single kv pair` 19 | } 20 | 21 | func (c DeleteCmd) LongHelp() string { 22 | s := c.Help() 23 | s += ` 24 | Usage: 25 | del 26 | Alias: 27 | remove, delete, rm 28 | ` 29 | return s 30 | } 31 | 32 | func (c DeleteCmd) Handler() func(ctx context.Context) { 33 | return func(ctx context.Context) { 34 | utils.OutputWithElapse(func() error { 35 | ic := utils.ExtractIshellContext(ctx) 36 | if len(ic.Args) < 1 { 37 | utils.Print(c.LongHelp()) 38 | return nil 39 | } 40 | k, err := utils.GetStringLit(ic.RawArgs[1]) 41 | if err != nil { 42 | return err 43 | } 44 | err = client.GetTiKVClient().Delete(context.TODO(), k) 45 | if err != nil { 46 | return err 47 | } 48 | return nil 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /kvcmds/cmd_put.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/c4pt0r/tcli" 8 | "github.com/c4pt0r/tcli/utils" 9 | 10 | "github.com/c4pt0r/tcli/client" 11 | ) 12 | 13 | type PutCmd struct{} 14 | 15 | var _ tcli.Cmd = PutCmd{} 16 | 17 | func (c PutCmd) Name() string { return "put" } 18 | func (c PutCmd) Alias() []string { return []string{"put", "set"} } 19 | func (c PutCmd) Help() string { 20 | return `put [key] [value]` 21 | } 22 | 23 | func (c PutCmd) LongHelp() string { 24 | return c.Help() 25 | } 26 | 27 | func (c PutCmd) Handler() func(ctx context.Context) { 28 | return func(ctx context.Context) { 29 | utils.OutputWithElapse(func() error { 30 | ic := utils.ExtractIshellContext(ctx) 31 | if len(ic.Args) < 2 { 32 | fmt.Println(c.LongHelp()) 33 | return nil 34 | } 35 | k, err := utils.GetStringLit(ic.RawArgs[1]) 36 | if err != nil { 37 | return err 38 | } 39 | v, err := utils.GetStringLit(ic.RawArgs[2]) 40 | if err != nil { 41 | return err 42 | } 43 | err = client.GetTiKVClient().Put(context.TODO(), client.KV{K: k, V: v}) 44 | if err != nil { 45 | return err 46 | } 47 | return nil 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /kvcmds/cmd_bench.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/tcli" 7 | "github.com/c4pt0r/tcli/utils" 8 | "github.com/manifoldco/promptui" 9 | ) 10 | 11 | type BenchWorkload interface { 12 | Name() string 13 | Run(ctx context.Context) error 14 | Stop(ctx context.Context) error 15 | } 16 | 17 | var _ tcli.Cmd = BenchCmd{} 18 | 19 | type BenchCmd struct { 20 | Workloads []BenchWorkload 21 | } 22 | 23 | func NewBenchCmd(ww ...BenchWorkload) BenchCmd { 24 | var workloads []BenchWorkload 25 | for _, w := range ww { 26 | workloads = append(workloads, w) 27 | } 28 | return BenchCmd{ 29 | Workloads: workloads, 30 | } 31 | } 32 | 33 | func (c BenchCmd) Name() string { return "bench" } 34 | func (c BenchCmd) Alias() []string { return []string{"benchmark"} } 35 | 36 | func (c BenchCmd) LongHelp() string { 37 | return c.Help() 38 | } 39 | 40 | func (c BenchCmd) Help() string { 41 | return `bench [type], type: ycsb` 42 | } 43 | 44 | func (c BenchCmd) Handler() func(ctx context.Context) { 45 | return func(ctx context.Context) { 46 | var items []string 47 | for _, w := range c.Workloads { 48 | items = append(items, w.Name()) 49 | } 50 | 51 | prompt := promptui.Select{ 52 | Label: "Choose Benchmark Workload", 53 | Items: items, 54 | } 55 | i, _, err := prompt.Run() 56 | if err != nil { 57 | utils.Print(err) 58 | return 59 | } 60 | c.Workloads[i].Run(context.TODO()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cmd_flags.go: -------------------------------------------------------------------------------- 1 | package tcli 2 | 3 | ///////////////////// scan options ////////////////////// 4 | var ( 5 | ScanOptKeyOnly string = "key-only" 6 | ScanOptCountOnly string = "count-only" 7 | ScanOptLimit string = "limit" 8 | ScanOptStrictPrefix string = "strict-prefix" 9 | ) 10 | 11 | // for completer to work, keyword list 12 | var ScanOptsKeywordList = []string{ 13 | ScanOptKeyOnly, 14 | ScanOptCountOnly, 15 | ScanOptLimit, 16 | ScanOptStrictPrefix, 17 | } 18 | 19 | ///////////////////// end of scan options /////////////// 20 | 21 | //////////////// del/delp/delall options //////////////// 22 | var ( 23 | DeleteOptWithPrefix string = "prefix-mode" 24 | DeleteOptBatchSize string = "batch-size" 25 | DeleteOptLimit string = "limit" 26 | DeleteOptYes string = "yes" 27 | ) 28 | 29 | var DeleteOptsKeywordList = []string{ 30 | DeleteOptWithPrefix, 31 | DeleteOptBatchSize, 32 | DeleteOptLimit, 33 | DeleteOptYes, 34 | } 35 | 36 | //////////////// end of del/delp/delall options //////// 37 | 38 | ///////////////// loadcsv options ////////////////////// 39 | var ( 40 | LoadFileOptBatchSize string = "batch-size" 41 | LoadFileoptSkipRows string = "skip-rows" 42 | ) 43 | 44 | var LoadFileOptsKeywordList = []string{ 45 | LoadFileOptBatchSize, 46 | LoadFileoptSkipRows, 47 | } 48 | 49 | //////////////// end of loadcsv options /////////////// 50 | 51 | ///////////////// backup options ///////////////////// 52 | var ( 53 | BackupOptBatchSize string = "batch-size" 54 | ) 55 | 56 | var BackupOptsKeywordList = []string{ 57 | BackupOptBatchSize, 58 | } 59 | 60 | //////////////// end of backup options /////////////// 61 | -------------------------------------------------------------------------------- /kvcmds/cmd_explain.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/c4pt0r/kvql" 7 | "github.com/c4pt0r/tcli" 8 | "github.com/c4pt0r/tcli/client" 9 | "github.com/c4pt0r/tcli/query" 10 | "github.com/c4pt0r/tcli/utils" 11 | ) 12 | 13 | type ExplainCmd struct{} 14 | 15 | var _ tcli.Cmd = ExplainCmd{} 16 | 17 | func (c ExplainCmd) Name() string { return "explain" } 18 | func (c ExplainCmd) Alias() []string { return []string{"explain", "desc"} } 19 | func (c ExplainCmd) Help() string { 20 | return `explain query plans` 21 | } 22 | 23 | func (c ExplainCmd) LongHelp() string { 24 | s := c.Help() 25 | s += ` 26 | Usage: 27 | explain 28 | 29 | Example: 30 | explain select * where key ^= 'k' limit 10 31 | ` 32 | return s 33 | } 34 | 35 | func (c ExplainCmd) Handler() func(ctx context.Context) { 36 | return func(ctx context.Context) { 37 | utils.OutputWithElapse(func() error { 38 | ic := utils.ExtractIshellContext(ctx) 39 | if len(ic.Args) < 1 { 40 | utils.Print(c.LongHelp()) 41 | return nil 42 | } 43 | sql := getQueryString(ic) 44 | qtxn := query.NewQueryStorage(client.GetTiKVClient()) 45 | opt := kvql.NewOptimizer(sql) 46 | plan, err := opt.BuildPlan(qtxn) 47 | if err != nil { 48 | return bindQueryToError(sql, err) 49 | } 50 | ret := [][]string{ 51 | {"Plan"}, 52 | } 53 | for i, plan := range plan.Explain() { 54 | space := "" 55 | for x := 0; x < i*3; x++ { 56 | space += " " 57 | } 58 | var planStr string 59 | if i == 0 { 60 | planStr = space + plan 61 | } else { 62 | planStr = space + "`-" + plan 63 | } 64 | ret = append(ret, []string{planStr}) 65 | } 66 | utils.PrintTableNoWrap(ret) 67 | return nil 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /kvcmds/cmd_delall.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/c4pt0r/log" 9 | "github.com/c4pt0r/tcli" 10 | "github.com/c4pt0r/tcli/client" 11 | "github.com/c4pt0r/tcli/utils" 12 | ) 13 | 14 | type DeleteAllCmd struct{} 15 | 16 | var _ tcli.Cmd = DeleteAllCmd{} 17 | 18 | func (c DeleteAllCmd) Name() string { return "delall" } 19 | func (c DeleteAllCmd) Alias() []string { return []string{"dela", "removeall", "rma"} } 20 | func (c DeleteAllCmd) Help() string { 21 | return `remove all key-value pairs, DANGEROUS` 22 | } 23 | 24 | func (c DeleteAllCmd) LongHelp() string { 25 | s := c.Help() 26 | s += ` 27 | Usage: 28 | delall 29 | Options: 30 | --yes, force yes 31 | Alias: 32 | dela, removeall, rma 33 | ` 34 | return s 35 | } 36 | 37 | func (c DeleteAllCmd) Handler() func(ctx context.Context) { 38 | return func(ctx context.Context) { 39 | utils.OutputWithElapse(func() error { 40 | var yes bool 41 | if utils.HasForceYes(ctx) { 42 | yes = true 43 | } else { 44 | yes = utils.AskYesNo(fmt.Sprintf("Delete all keys, are you sure?"), "no") == 1 45 | } 46 | if yes { 47 | utils.Print("Your call") 48 | var total int 49 | // TODO limit should not be fixed 50 | for { 51 | key, cnt, err := client.GetTiKVClient().DeletePrefix(ctx, []byte(""), 1000) 52 | if err != nil { 53 | return err 54 | } 55 | if cnt == 0 { 56 | break 57 | } 58 | total += cnt 59 | log.I(fmt.Sprintf("Deleting a batch... Position: %s Count: %d, Total: %d", key, cnt, total)) 60 | } 61 | result := []client.KV{ 62 | {K: []byte("Affected Keys"), V: []byte(strconv.Itoa(total))}, 63 | } 64 | client.KVS(result).Print() 65 | } 66 | return nil 67 | }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kvcmds/cmd_count.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | 8 | "github.com/c4pt0r/tcli" 9 | "github.com/c4pt0r/tcli/client" 10 | "github.com/c4pt0r/tcli/utils" 11 | "github.com/magiconair/properties" 12 | ) 13 | 14 | type CountCmd struct{} 15 | 16 | var _ tcli.Cmd = &CountCmd{} 17 | 18 | func (c CountCmd) Name() string { return "count" } 19 | func (c CountCmd) Alias() []string { return []string{"cnt"} } 20 | func (c CountCmd) Help() string { 21 | return `count keys or keys with specific prefix` 22 | } 23 | 24 | func (c CountCmd) LongHelp() string { 25 | s := c.Help() 26 | s += ` 27 | Usage: 28 | count [key prefix | *] 29 | Alias: 30 | cnt 31 | ` 32 | return s 33 | } 34 | 35 | func (c CountCmd) Handler() func(ctx context.Context) { 36 | return func(ctx context.Context) { 37 | utils.OutputWithElapse(func() error { 38 | ic := utils.ExtractIshellContext(ctx) 39 | if len(ic.Args) < 1 { 40 | utils.Print(c.LongHelp()) 41 | return nil 42 | } 43 | prefix, err := utils.GetStringLit(ic.RawArgs[1]) 44 | if err != nil { 45 | return err 46 | } 47 | promptMsg := fmt.Sprintf("Are you going to count all keys with prefix :%s", prefix) 48 | if string(prefix) == "*" { 49 | promptMsg = "Are you going to count all keys? (may be very slow when your data is huge)" 50 | } 51 | 52 | var yes bool 53 | if utils.HasForceYes(ctx) { 54 | yes = true 55 | } else { 56 | yes = utils.AskYesNo(promptMsg, "no") == 1 57 | } 58 | if yes { 59 | scanOpt := properties.NewProperties() 60 | scanOpt.Set(tcli.ScanOptCountOnly, "true") 61 | scanOpt.Set(tcli.ScanOptKeyOnly, "true") 62 | scanOpt.Set(tcli.ScanOptStrictPrefix, "true") 63 | // count all mode 64 | if string(prefix) == "*" || bytes.Compare(prefix, []byte("\x00")) == 0 { 65 | prefix = []byte("\x00") 66 | scanOpt.Set(tcli.ScanOptStrictPrefix, "false") 67 | } 68 | _, cnt, err := client.GetTiKVClient().Scan(utils.ContextWithProp(context.TODO(), scanOpt), prefix) 69 | if err != nil { 70 | return err 71 | } 72 | utils.Print(cnt) 73 | } 74 | return nil 75 | }) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /utils/variables.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | ) 8 | 9 | var ( 10 | SysVarPrintFormatKey string = "sys.printfmt" 11 | ) 12 | 13 | var ( 14 | _varMutex sync.RWMutex 15 | 16 | _globalVariables = make(map[string][]byte) 17 | _builtinVars = [][]string{ 18 | {`head`, "\x00"}, 19 | } 20 | 21 | _globalSysVariables = make(map[string]string) 22 | _builtinSysVars = [][]string{ 23 | {SysVarPrintFormatKey, "table"}, 24 | } 25 | ) 26 | 27 | func VarGet(varname string) ([]byte, bool) { 28 | _varMutex.RLock() 29 | defer _varMutex.RUnlock() 30 | val, ok := _globalVariables[varname] 31 | return val, ok 32 | } 33 | 34 | func VarSet(varname string, val []byte) { 35 | _varMutex.Lock() 36 | defer _varMutex.Unlock() 37 | _globalVariables[varname] = append([]byte{}, val...) 38 | } 39 | 40 | func IsVar(s string) bool { 41 | return strings.HasPrefix(s, "$") 42 | } 43 | 44 | func InitBuiltinVaribles() { 45 | for _, item := range _builtinVars { 46 | VarSet(item[0], []byte(item[1])) 47 | } 48 | 49 | for _, item := range _builtinSysVars { 50 | SysVarSet(item[0], item[1]) 51 | } 52 | } 53 | func PrintGlobalVaribles() { 54 | _varMutex.RLock() 55 | defer _varMutex.RUnlock() 56 | if len(_globalVariables) > 0 { 57 | var data = [][]string{ 58 | {"Var Name", "Value"}, 59 | } 60 | for k, v := range _globalVariables { 61 | vv := fmt.Sprintf("h'%s'", Bytes2hex(v)) 62 | data = append(data, []string{k, vv}) 63 | } 64 | PrintTable(data) 65 | } 66 | } 67 | 68 | func SysVarGet(varname string) (string, bool) { 69 | _varMutex.RLock() 70 | defer _varMutex.RUnlock() 71 | val, ok := _globalSysVariables[varname] 72 | return val, ok 73 | } 74 | 75 | func SysVarSet(varname, val string) { 76 | _varMutex.Lock() 77 | defer _varMutex.Unlock() 78 | _globalSysVariables[varname] = val 79 | } 80 | 81 | func PrintSysVaribles() { 82 | _varMutex.RLock() 83 | defer _varMutex.RUnlock() 84 | if len(_globalSysVariables) > 0 { 85 | var data = [][]string{ 86 | {"System Varibles Name", "Value"}, 87 | } 88 | for k, v := range _globalSysVariables { 89 | data = append(data, []string{k, string(v)}) 90 | } 91 | PrintTable(data) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /kvcmds/cmd_delete_prefix.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/c4pt0r/tcli" 8 | "github.com/c4pt0r/tcli/client" 9 | "github.com/c4pt0r/tcli/utils" 10 | "github.com/magiconair/properties" 11 | ) 12 | 13 | type DeletePrefixCmd struct{} 14 | 15 | var _ tcli.Cmd = &DeletePrefixCmd{} 16 | 17 | func (c DeletePrefixCmd) Name() string { return "delp" } 18 | func (c DeletePrefixCmd) Alias() []string { return []string{"deletep", "removep", "rmp"} } 19 | func (c DeletePrefixCmd) Help() string { 20 | return `delete kv pairs with specific prefix` 21 | } 22 | 23 | func (c DeletePrefixCmd) LongHelp() string { 24 | s := c.Help() 25 | s += ` 26 | Usage: 27 | delp [options] 28 | Alias: 29 | deletep, removep, rmp 30 | Options: 31 | --yes, force yes 32 | --limit=, default: 1000 33 | ` 34 | return s 35 | } 36 | 37 | func (c DeletePrefixCmd) Handler() func(ctx context.Context) { 38 | return func(ctx context.Context) { 39 | utils.OutputWithElapse(func() error { 40 | ic := utils.ExtractIshellContext(ctx) 41 | if len(ic.Args) < 1 { 42 | utils.Print(c.LongHelp()) 43 | return nil 44 | } 45 | k, err := utils.GetStringLit(ic.RawArgs[1]) 46 | if err != nil { 47 | return err 48 | } 49 | opt := properties.NewProperties() 50 | if len(ic.Args) > 1 { 51 | err := utils.SetOptByString(ic.Args[1:], opt) 52 | if err != nil { 53 | return err 54 | } 55 | } 56 | opt.Set(tcli.DeleteOptWithPrefix, "true") 57 | limit := opt.GetInt(tcli.DeleteOptLimit, 1000) 58 | 59 | var yes bool 60 | if utils.HasForceYes(ctx) { 61 | yes = true 62 | } else { 63 | yes = utils.AskYesNo("Are you sure to delete kv pairs with prefix: %s", string(k)) == 1 64 | } 65 | 66 | if yes { 67 | utils.Print("Your call") 68 | lastKey, cnt, err := client.GetTiKVClient().DeletePrefix(ctx, k, limit) 69 | if err != nil { 70 | return err 71 | } 72 | result := []client.KV{ 73 | {K: []byte("Last Key"), V: []byte(lastKey)}, 74 | {K: []byte("Affected Keys"), V: []byte(fmt.Sprintf("%d", cnt))}, 75 | } 76 | client.KVS(result).Print() 77 | } else { 78 | utils.Print("Nothing happened") 79 | } 80 | if err != nil { 81 | return err 82 | } 83 | return nil 84 | }) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcli 2 | The ultimate CLI tool for TiKV, for human being :). 3 | 4 | (Only support Transaction KV API now) 5 | 6 | ``` 7 | Commands: 8 | .stores list tikv stores in cluster 9 | backup dumps kv pairs to a csv file 10 | bench bench [type], type: ycsb 11 | clear clear the screen 12 | count count keys or keys with specific prefix 13 | del delete a single kv pair 14 | delall remove all key-value pairs, DANGEROUS 15 | delp delete kv pairs with specific prefix 16 | echo echo $ 17 | env print env variables 18 | exit exit the program 19 | get get [key] 20 | head scan keys from $head, equals to "scan $head limit=N", usage: head 21 | help display help 22 | hexdump hexdump 23 | loadcsv load csv file, use "loadcsv --help" for more details 24 | put put [key] [value] 25 | scan Scan keys from start key, use "scan --help" for more details 26 | scanp scan keys with prefix, equals to "scan [key prefix] strict-prefix=true" 27 | sysenv print system env variables 28 | sysvar set system variables, usage: 29 | sysvar =, variable name and value are both string 30 | example: scan $varname or get $varname 31 | var set variables, usage: 32 | var =, variable name and value are both string 33 | example: scan $varname or get $varname 34 | ``` 35 | 36 | Have a try: 37 | 38 | 1. Install `tiup` 39 | 40 | `curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh` 41 | 42 | 2. Deploy TiKV using `tiup` 43 | 44 | `tiup playground --mode tikv-slim` 45 | 46 | 3. Launch `tcli` 47 | 48 | ``` 49 | $ tcli -pd localhost:2379 50 | 2021/10/24 14:57:19 main.go:98: I | pd instance info: name:"pd-0" member_id:3474484975246189105 peer_urls:"http://127.0.0.1:2380" client_urls:"http://127.0.0.1:2379" 51 | 2021/10/24 14:57:19 main.go:105: I | tikv instance info: store_id:"1" version:"5.2.1" addr:"127.0.0.1:20160" state:"Up" status_addr:"127.0.0.1:20180" 52 | >>> 53 | ``` 54 | 4. Have a try: 55 | 56 | ``` 57 | >>> put hello world 58 | Input: put hello world 59 | Success, Elapse: 33 ms 60 | 61 | >>> get hello 62 | Input: get hello 63 | |-------|-------| 64 | | KEY | VALUE | 65 | |-------|-------| 66 | | hello | world | 67 | |-------|-------| 68 | 1 Record Found 69 | Success, Elapse: 8 ms 70 | 71 | >>> put hello_world world 72 | Input: put hello_world world 73 | Success, Elapse: 7 ms 74 | 75 | >>> scanp hello 76 | Input: scanp hello 77 | |-------------|-------| 78 | | KEY | VALUE | 79 | |-------------|-------| 80 | | hello | world | 81 | | hello_world | world | 82 | |-------------|-------| 83 | 2 Records Found 84 | Success, Elapse: 5 ms 85 | ``` -------------------------------------------------------------------------------- /kvcmds/cmd_backup.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/csv" 7 | "errors" 8 | "os" 9 | 10 | "github.com/c4pt0r/tcli" 11 | "github.com/c4pt0r/tcli/client" 12 | "github.com/c4pt0r/tcli/utils" 13 | "github.com/magiconair/properties" 14 | ) 15 | 16 | var _ tcli.Cmd = BackupCmd{} 17 | 18 | type BackupCmd struct{} 19 | 20 | func (c BackupCmd) Name() string { return "backup" } 21 | func (c BackupCmd) Alias() []string { return []string{"backup"} } 22 | func (c BackupCmd) Help() string { 23 | return "dumps kv pairs to a csv file" 24 | } 25 | 26 | func (c BackupCmd) LongHelp() string { 27 | var buf bytes.Buffer 28 | buf.WriteString(c.Help()) 29 | buf.WriteString(` 30 | Usage: 31 | backup 32 | Options: 33 | --batch-size=, default 1000 34 | Example: 35 | # backup all kvs with prefix "t_" to csv file 36 | backup "t_" backup.csv --batch-size=5000 37 | 38 | # backup all kvs to csv file 39 | backup * backup.csv 40 | backup $head backup.csv 41 | `) 42 | return buf.String() 43 | } 44 | 45 | func writeKvsToCsvFile(w *csv.Writer, kvs client.KVS) error { 46 | for _, kv := range kvs { 47 | line := []string{utils.Bytes2StrLit(kv.K), utils.Bytes2StrLit(kv.V)} 48 | err := w.Write(line) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | w.Flush() 54 | return nil 55 | } 56 | 57 | func (c BackupCmd) Handler() func(ctx context.Context) { 58 | return func(ctx context.Context) { 59 | utils.OutputWithElapse(func() error { 60 | ic := utils.ExtractIshellContext(ctx) 61 | if len(ic.Args) < 2 { 62 | utils.Print(c.LongHelp()) 63 | return nil 64 | } 65 | prefix, err := utils.GetStringLit(ic.Args[0]) 66 | if err != nil { 67 | return err 68 | } 69 | outputFile := ic.Args[1] 70 | _, err = os.Stat(outputFile) 71 | if !os.IsNotExist(err) { 72 | return errors.New("Backup file already exists") 73 | } 74 | fp, err := os.Create(outputFile) 75 | if err != nil { 76 | return err 77 | } 78 | csvWriter := csv.NewWriter(fp) 79 | defer csvWriter.Flush() 80 | // Write first line 81 | csvWriter.Write([]string{"Key", "Value"}) 82 | 83 | opt := properties.NewProperties() 84 | if len(ic.Args) > 2 { 85 | err := utils.SetOptByString(ic.Args[2:], opt) 86 | if err != nil { 87 | return err 88 | } 89 | } 90 | opt.Set(tcli.ScanOptLimit, opt.GetString(tcli.BackupOptBatchSize, "1000")) 91 | if bytes.Compare(prefix, []byte("\x00")) != 0 && string(prefix) != "*" { 92 | opt.Set(tcli.ScanOptStrictPrefix, "true") 93 | } 94 | kvs, cnt, err := client.GetTiKVClient().Scan(utils.ContextWithProp(context.TODO(), opt), prefix) 95 | if err != nil { 96 | return err 97 | } 98 | for cnt > 0 { 99 | // write file 100 | if err := writeKvsToCsvFile(csvWriter, kvs); err != nil { 101 | return err 102 | } 103 | lastKey := utils.NextKey(kvs[len(kvs)-1].K) 104 | utils.Print("Write a batch, batch size:", cnt, "Last key:", kvs[len(kvs)-1].K) 105 | // run next batch 106 | kvs, cnt, err = client.GetTiKVClient().Scan(utils.ContextWithProp(context.TODO(), opt), lastKey) 107 | if err != nil { 108 | return err 109 | } 110 | } 111 | return nil 112 | }) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /query/query_txn.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | 7 | "github.com/c4pt0r/kvql" 8 | "github.com/c4pt0r/tcli" 9 | "github.com/c4pt0r/tcli/client" 10 | "github.com/c4pt0r/tcli/utils" 11 | "github.com/magiconair/properties" 12 | ) 13 | 14 | var ( 15 | _ kvql.Storage = (*queryStorage)(nil) 16 | _ kvql.Cursor = (*queryCursor)(nil) 17 | ) 18 | 19 | type queryStorage struct { 20 | client client.Client 21 | } 22 | 23 | func NewQueryStorage(client client.Client) kvql.Storage { 24 | return &queryStorage{ 25 | client: client, 26 | } 27 | } 28 | 29 | func (s *queryStorage) Get(key []byte) ([]byte, error) { 30 | kv, err := s.client.Get(context.TODO(), client.Key(key)) 31 | if err != nil { 32 | if err.Error() == "not exist" { 33 | return nil, nil 34 | } 35 | return nil, err 36 | } 37 | return kv.V, nil 38 | } 39 | 40 | func (s *queryStorage) Put(key []byte, value []byte) error { 41 | return s.client.Put(context.TODO(), client.KV{K: key, V: value}) 42 | } 43 | 44 | func (s *queryStorage) BatchPut(kvs []kvql.KVPair) error { 45 | tkvs := make([]client.KV, len(kvs)) 46 | for i, kv := range kvs { 47 | tkvs[i] = client.KV{K: kv.Key, V: kv.Value} 48 | } 49 | return s.client.BatchPut(context.TODO(), tkvs) 50 | } 51 | 52 | func (s *queryStorage) Delete(key []byte) error { 53 | return s.client.Delete(context.TODO(), key) 54 | } 55 | 56 | func (s *queryStorage) BatchDelete(keys [][]byte) error { 57 | tkvs := make([]client.KV, len(keys)) 58 | for i, key := range keys { 59 | tkvs[i] = client.KV{K: key} 60 | } 61 | return s.client.BatchDelete(context.TODO(), tkvs) 62 | } 63 | 64 | func (s *queryStorage) Cursor() (kvql.Cursor, error) { 65 | return &queryCursor{ 66 | storage: s, 67 | batch: nil, 68 | prefix: []byte{}, 69 | iterPos: 0, 70 | }, nil 71 | } 72 | 73 | type queryCursor struct { 74 | storage *queryStorage 75 | batch client.KVS 76 | batchSize int 77 | prefix []byte 78 | iterPos int 79 | prevLastKey []byte 80 | } 81 | 82 | func (c *queryCursor) loadBatch() error { 83 | scanOpt := properties.NewProperties() 84 | scanOpt.Set(tcli.ScanOptLimit, "10") 85 | scanOpt.Set(tcli.ScanOptKeyOnly, "false") 86 | scanOpt.Set(tcli.ScanOptCountOnly, "false") 87 | scanOpt.Set(tcli.ScanOptStrictPrefix, "false") 88 | qctx := utils.ContextWithProp(context.TODO(), scanOpt) 89 | kvs, n, err := c.storage.client.Scan(qctx, c.prefix) 90 | if err != nil { 91 | return err 92 | } 93 | c.batch = kvs 94 | c.batchSize = n 95 | c.iterPos = 0 96 | if len(kvs) > 0 { 97 | // Skip first key 98 | if c.prevLastKey != nil && bytes.Compare(kvs[0].K, c.prevLastKey) == 0 { 99 | c.batch = c.batch[1:] 100 | c.batchSize-- 101 | } 102 | c.prevLastKey = kvs[len(kvs)-1].K 103 | c.prefix = c.prevLastKey 104 | } 105 | return nil 106 | } 107 | 108 | func (c *queryCursor) Next() (key []byte, val []byte, err error) { 109 | if c.batch == nil || c.iterPos >= c.batchSize { 110 | err = c.loadBatch() 111 | if err != nil { 112 | return nil, nil, err 113 | } 114 | } 115 | if c.batchSize == 0 { 116 | return nil, nil, nil 117 | } 118 | kv := c.batch[c.iterPos] 119 | c.iterPos++ 120 | key = []byte(kv.K) 121 | val = []byte(kv.V) 122 | return key, val, nil 123 | } 124 | 125 | func (c *queryCursor) Seek(key []byte) error { 126 | c.prefix = key 127 | c.batch = nil 128 | c.batchSize = 0 129 | c.iterPos = 0 130 | c.prevLastKey = nil 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /kvcmds/cmd_bench_ycsb.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | "time" 11 | 12 | // Register TiKV database 13 | "github.com/AlecAivazis/survey/v2" 14 | "github.com/magiconair/properties" 15 | "github.com/manifoldco/promptui" 16 | _ "github.com/pingcap/go-ycsb/db/tikv" 17 | "github.com/pingcap/go-ycsb/pkg/measurement" 18 | _ "github.com/pingcap/go-ycsb/pkg/workload" 19 | 20 | "github.com/pingcap/go-ycsb/pkg/client" 21 | "github.com/pingcap/go-ycsb/pkg/prop" 22 | "github.com/pingcap/go-ycsb/pkg/ycsb" 23 | ) 24 | 25 | type YcsbBench struct { 26 | Context context.Context 27 | Cancel context.CancelFunc 28 | pdAddr string 29 | DB ycsb.DB 30 | Workload ycsb.Workload 31 | } 32 | 33 | func (y *YcsbBench) init() { 34 | } 35 | 36 | func (y *YcsbBench) Start(load bool) { 37 | props := properties.NewProperties() 38 | var err error 39 | workloadCreator := ycsb.GetWorkloadCreator("core") 40 | if y.Workload, err = workloadCreator.Create(props); err != nil { 41 | log.Fatalf("create workload failed %v", err) 42 | } 43 | 44 | props.Set("tikv.type", "txn") 45 | props.Set("tikv.pd", y.pdAddr) 46 | props.Set(prop.ThreadCount, "10") 47 | props.Set(prop.OperationCount, "10000") 48 | props.Set(prop.RecordCount, "100000") 49 | // report every 2s 50 | props.Set(prop.LogInterval, "2") 51 | 52 | defaultProps := props.String() 53 | prompt := &survey.Editor{ 54 | Message: "Ycsb Config", 55 | Default: defaultProps, 56 | HideDefault: true, 57 | AppendDefault: true, 58 | } 59 | var content string 60 | survey.AskOne(prompt, &content) 61 | 62 | if err := props.Load([]byte(content), properties.UTF8); err != nil { 63 | fmt.Printf(err.Error()) 64 | return 65 | } 66 | 67 | measurement.InitMeasure(props) 68 | // Is load data 69 | if load { 70 | props.Set(prop.DoTransactions, "false") 71 | } else { 72 | props.Set(prop.DoTransactions, "true") 73 | } 74 | 75 | fmt.Println("***************** properties *****************") 76 | for key, value := range props.Map() { 77 | fmt.Printf("\"%s\"=\"%s\"\n", key, value) 78 | } 79 | fmt.Println("**********************************************") 80 | 81 | dbCreator := ycsb.GetDBCreator("tikv") 82 | if y.DB, err = dbCreator.Create(props); err != nil { 83 | log.Fatalf("create db %s failed %v", "ycsb", err) 84 | } 85 | y.DB = client.DbWrapper{DB: y.DB} 86 | 87 | y.Context, y.Cancel = context.WithCancel(context.Background()) 88 | c := client.NewClient(props, y.Workload, y.DB) 89 | start := time.Now() 90 | 91 | c.Run(y.Context) 92 | 93 | fmt.Printf("Run finished, takes %s\n", time.Now().Sub(start)) 94 | measurement.Output() 95 | } 96 | 97 | func NewYcsbBench(pdAddr string) BenchWorkload { 98 | ret := &YcsbBench{pdAddr: pdAddr} 99 | ret.init() 100 | return ret 101 | } 102 | 103 | func (y *YcsbBench) Name() string { return "ycsb" } 104 | func (y *YcsbBench) Run(ctx context.Context) error { 105 | c := make(chan os.Signal) 106 | // Ctrl-C to break 107 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 108 | go func() { 109 | <-c 110 | y.Stop(context.TODO()) 111 | }() 112 | 113 | prompt := promptui.Select{ 114 | Label: "Choose job:", 115 | Items: []string{"1. Load bench data", "2. Run workload"}, 116 | } 117 | i, _, err := prompt.Run() 118 | if err != nil { 119 | fmt.Println(err) 120 | return err 121 | } 122 | 123 | load := false 124 | if i == 0 { 125 | load = true 126 | } 127 | y.Start(load) 128 | return nil 129 | } 130 | func (y *YcsbBench) Stop(ctx context.Context) error { 131 | y.Cancel() 132 | return nil 133 | } 134 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/c4pt0r/tcli 2 | 3 | go 1.21.1 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.2.16 7 | github.com/abiosoft/ishell v2.0.0+incompatible 8 | github.com/c4pt0r/kvql v0.0.0-20240509061143-2e732b17190f 9 | github.com/c4pt0r/log v0.0.0-20211004143616-aa6380016a47 10 | github.com/fatih/color v1.12.0 11 | github.com/magiconair/properties v1.8.0 12 | github.com/manifoldco/promptui v0.8.0 13 | github.com/olekukonko/tablewriter v0.0.5 14 | github.com/pingcap/go-ycsb v0.0.0-20210727125954-0c816a248fc3 15 | github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 16 | github.com/pkg/errors v0.9.1 17 | github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210706041121-6ca00989ddb4 18 | github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d 19 | go.uber.org/atomic v1.7.0 20 | ) 21 | 22 | require ( 23 | github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect 24 | github.com/beorn7/perks v1.0.1 // indirect 25 | github.com/cespare/xxhash/v2 v2.1.1 // indirect 26 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect 27 | github.com/coreos/go-semver v0.3.0 // indirect 28 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect 29 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect 30 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect 31 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect 32 | github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect 33 | github.com/gogo/protobuf v1.3.2 // indirect 34 | github.com/golang/protobuf v1.3.4 // indirect 35 | github.com/google/btree v1.0.0 // indirect 36 | github.com/google/uuid v1.1.1 // indirect 37 | github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect 38 | github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect 39 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 40 | github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a // indirect 41 | github.com/mattn/go-colorable v0.1.8 // indirect 42 | github.com/mattn/go-isatty v0.0.12 // indirect 43 | github.com/mattn/go-runewidth v0.0.9 // indirect 44 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 45 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect 46 | github.com/opentracing/opentracing-go v1.1.0 // indirect 47 | github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 // indirect 48 | github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd // indirect 49 | github.com/pingcap/kvproto v0.0.0-20210531063847-f42e582bf0bb // indirect 50 | github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307 // indirect 51 | github.com/prometheus/client_golang v1.5.1 // indirect 52 | github.com/prometheus/client_model v0.2.0 // indirect 53 | github.com/prometheus/common v0.9.1 // indirect 54 | github.com/prometheus/procfs v0.0.8 // indirect 55 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect 56 | github.com/sirupsen/logrus v1.8.1 // indirect 57 | github.com/twmb/murmur3 v1.1.3 // indirect 58 | go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b // indirect 59 | go.uber.org/multierr v1.7.0 // indirect 60 | go.uber.org/zap v1.16.0 // indirect 61 | golang.org/x/net v0.14.0 // indirect 62 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 63 | golang.org/x/sys v0.12.0 // indirect 64 | golang.org/x/term v0.11.0 // indirect 65 | golang.org/x/text v0.12.0 // indirect 66 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 // indirect 67 | google.golang.org/grpc v1.27.1 // indirect 68 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 69 | ) 70 | -------------------------------------------------------------------------------- /kvcmds/cmd_query.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/abiosoft/ishell" 10 | "github.com/c4pt0r/kvql" 11 | "github.com/c4pt0r/tcli" 12 | "github.com/c4pt0r/tcli/client" 13 | "github.com/c4pt0r/tcli/query" 14 | "github.com/c4pt0r/tcli/utils" 15 | ) 16 | 17 | type QueryCmd struct{} 18 | 19 | var _ tcli.Cmd = QueryCmd{} 20 | 21 | func (c QueryCmd) Name() string { return "query" } 22 | func (c QueryCmd) Alias() []string { return []string{"query", "q"} } 23 | func (c QueryCmd) Help() string { 24 | return `query sql` 25 | } 26 | 27 | func (c QueryCmd) LongHelp() string { 28 | s := c.Help() 29 | s += ` 30 | Usage: 31 | query 32 | 33 | Example: 34 | query select * where key ^= 'k' limit 10 35 | ` 36 | return s 37 | } 38 | 39 | func getQueryString(ic *ishell.Context) string { 40 | ret := []string{} 41 | for _, arg := range ic.RawArgs[1:] { 42 | ret = append(ret, arg) 43 | } 44 | return strings.Join(ret, " ") 45 | } 46 | 47 | func convertColumnToString(c kvql.Column) string { 48 | switch v := c.(type) { 49 | case int, int8, int16, int32, int64, 50 | uint, uint8, uint16, uint32, uint64: 51 | return fmt.Sprintf("%d", v) 52 | case float32, float64: 53 | return fmt.Sprintf("%f", v) 54 | case []byte: 55 | return string(v) 56 | case string: 57 | return v 58 | case bool: 59 | if v { 60 | return "true" 61 | } 62 | return "false" 63 | case map[string]any, kvql.JSON, []any, []string, []int64, []float32, []float64: 64 | return fmt.Sprintf("%v", v) 65 | default: 66 | if v == nil { 67 | return "nil" 68 | } 69 | return "" 70 | } 71 | } 72 | 73 | func (c QueryCmd) Handler() func(ctx context.Context) { 74 | return func(ctx context.Context) { 75 | utils.OutputWithElapse(func() error { 76 | ic := utils.ExtractIshellContext(ctx) 77 | if len(ic.Args) < 1 { 78 | utils.Print(c.LongHelp()) 79 | return nil 80 | } 81 | sql := getQueryString(ic) 82 | qtxn := query.NewQueryStorage(client.GetTiKVClient()) 83 | opt := kvql.NewOptimizer(sql) 84 | plan, err := opt.BuildPlan(qtxn) 85 | if err != nil { 86 | return bindQueryToError(sql, err) 87 | } 88 | // ret, err := c.getRows(plan) 89 | ret, err := c.getRowsBatch(plan) 90 | if err != nil { 91 | return bindQueryToError(sql, err) 92 | } 93 | if len(ret) > 1 { 94 | utils.PrintTable(ret) 95 | fmt.Fprintf(os.Stderr, "%d Records Found\n", len(ret)-1) 96 | } else { 97 | fmt.Fprintf(os.Stderr, "%d Record Found\n", len(ret)-1) 98 | } 99 | return nil 100 | }) 101 | } 102 | } 103 | 104 | func bindQueryToError(sql string, err error) error { 105 | switch val := err.(type) { 106 | case kvql.QueryBinder: 107 | val.BindQuery(sql) 108 | return err 109 | default: 110 | return err 111 | } 112 | } 113 | 114 | func (c QueryCmd) getRows(plan kvql.FinalPlan) ([][]string, error) { 115 | ret := [][]string{ 116 | plan.FieldNameList(), 117 | } 118 | ectx := kvql.NewExecuteCtx() 119 | for { 120 | cols, err := plan.Next(ectx) 121 | if err != nil { 122 | return nil, err 123 | } 124 | ectx.Clear() 125 | if cols == nil { 126 | break 127 | } 128 | 129 | fields := make([]string, len(cols)) 130 | for i := 0; i < len(cols); i++ { 131 | fields[i] = convertColumnToString(cols[i]) 132 | } 133 | ret = append(ret, fields) 134 | } 135 | return ret, nil 136 | } 137 | 138 | func (c QueryCmd) getRowsBatch(plan kvql.FinalPlan) ([][]string, error) { 139 | ret := [][]string{ 140 | plan.FieldNameList(), 141 | } 142 | ectx := kvql.NewExecuteCtx() 143 | for { 144 | rows, err := plan.Batch(ectx) 145 | if err != nil { 146 | return nil, err 147 | } 148 | ectx.Clear() 149 | if len(rows) == 0 { 150 | break 151 | } 152 | fmt.Println("Exec Cache Hit", ectx.Hit) 153 | for _, cols := range rows { 154 | fields := make([]string, len(cols)) 155 | for i := 0; i < len(cols); i++ { 156 | fields[i] = convertColumnToString(cols[i]) 157 | } 158 | ret = append(ret, fields) 159 | } 160 | } 161 | return ret, nil 162 | } 163 | -------------------------------------------------------------------------------- /kvcmds/cmd_loadfile.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "encoding/csv" 6 | "fmt" 7 | "io" 8 | 9 | "github.com/c4pt0r/tcli/utils" 10 | 11 | "github.com/c4pt0r/tcli/client" 12 | 13 | "github.com/c4pt0r/tcli" 14 | 15 | "github.com/magiconair/properties" 16 | ) 17 | 18 | type LoadCsvCmd struct{} 19 | 20 | var _ tcli.Cmd = LoadCsvCmd{} 21 | 22 | func (c LoadCsvCmd) Name() string { return "loadcsv" } 23 | func (c LoadCsvCmd) Alias() []string { return []string{"lcsv"} } 24 | func (c LoadCsvCmd) Help() string { 25 | return `load csv file, use "loadcsv --help" for more details` 26 | 27 | } 28 | 29 | func (c LoadCsvCmd) LongHelp() string { 30 | s := c.Help() 31 | s += ` 32 | Usage: 33 | loadcsv [filename] 34 | Alias: 35 | lcsv 36 | Options: 37 | --batch-size=: int, how many records in one tikv transaction, default: 1000 38 | Examples: 39 | # load csv file to tikv 40 | loadcsv sample.csv 41 | 42 | # load csv file to tikv with key prefix: "prefix_" 43 | loadcsv sample.csv "prefix_" --batch-size=100 44 | 45 | # load csv file to tikv with key prefix and skip first row (header) 46 | loadcsv sample.csv "prefix_" --batch-size=100 --skip-rows=1 47 | ` 48 | return s 49 | } 50 | 51 | func (c LoadCsvCmd) processCSV(prop *properties.Properties, rc io.Reader, keyPrefix []byte) error { 52 | r := csv.NewReader(rc) 53 | var cnt int 54 | var batch []client.KV 55 | 56 | batchSize := prop.GetInt(tcli.LoadFileOptBatchSize, 1000) 57 | skips := prop.GetInt(tcli.LoadFileoptSkipRows, 0) 58 | for { 59 | rawRec, err := r.Read() 60 | if err != nil { 61 | if err == io.EOF { 62 | break 63 | } 64 | return err 65 | } 66 | if skips > 0 { 67 | skips-- 68 | continue 69 | } 70 | if len(rawRec) != 2 { 71 | return fmt.Errorf("invalid csv record: %v, format should be: ,", rawRec) 72 | } 73 | k, _ := utils.GetStringLit(rawRec[0]) 74 | v, _ := utils.GetStringLit(rawRec[1]) 75 | cnt++ 76 | var key []byte 77 | if len(keyPrefix) > 0 { 78 | key = append([]byte{}, keyPrefix...) 79 | key = append(key, k...) 80 | } else { 81 | key = k 82 | } 83 | // TODO multi-threaded 84 | batch = append(batch, client.KV{ 85 | K: key, 86 | V: v, 87 | }) 88 | if len(batch) == batchSize { 89 | // do insert 90 | err := client.GetTiKVClient().BatchPut(context.TODO(), batch) 91 | if err != nil { 92 | return err 93 | } 94 | // Show progress 95 | progress := rc.(*utils.ProgressReader).GetProgress() * 100 96 | utils.Print(fmt.Sprintf("Progress: %d%% Count: %d Last Key: %s", int(progress), cnt, k)) 97 | // clean buffer 98 | batch = nil 99 | } 100 | } 101 | // may have last batch 102 | if len(batch) > 0 { 103 | // do insert 104 | err := client.GetTiKVClient().BatchPut(context.TODO(), batch) 105 | if err != nil { 106 | return err 107 | } 108 | } 109 | utils.Print(fmt.Sprintf("Done, affected records: %d", cnt)) 110 | return nil 111 | } 112 | 113 | func (c LoadCsvCmd) Handler() func(ctx context.Context) { 114 | return func(ctx context.Context) { 115 | utils.OutputWithElapse(func() error { 116 | var err error 117 | ic := utils.ExtractIshellContext(ctx) 118 | if len(ic.Args) == 0 { 119 | utils.Print(c.LongHelp()) 120 | return nil 121 | } 122 | 123 | args, flags := utils.GetArgsAndOptionFlag(ic.RawArgs) 124 | 125 | // set filename 126 | var csvFile string 127 | if len(args) > 1 { // args[0] is the command name 128 | csvFile = args[1] 129 | } 130 | 131 | // set prefix 132 | var keyPrefix []byte 133 | if len(args) > 2 { 134 | var err error 135 | keyPrefix, err = utils.GetStringLit(args[2]) 136 | if err != nil { 137 | return err 138 | } 139 | } 140 | 141 | // set prop 142 | prop := properties.NewProperties() 143 | if len(ic.Args) > 2 { 144 | err = utils.SetOptByString(flags, prop) 145 | if err != nil { 146 | return nil 147 | } 148 | } 149 | // open file for read 150 | fp, rdr, err := utils.OpenFileToProgressReader(csvFile) 151 | if err != nil { 152 | return err 153 | } 154 | defer fp.Close() 155 | // TODO should validate first 156 | // TODO set batch size 157 | return c.processCSV(prop, rdr, keyPrefix) 158 | }) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /client/rawkv_client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/c4pt0r/log" 10 | "github.com/c4pt0r/tcli" 11 | "github.com/c4pt0r/tcli/utils" 12 | "github.com/tikv/client-go/v2/config" 13 | "github.com/tikv/client-go/v2/rawkv" 14 | pd "github.com/tikv/pd/client" 15 | ) 16 | 17 | var MaxRawKVScanLimit = 10240 18 | 19 | func newRawKVClient(pdAddr []string) *rawkvClient { 20 | client, err := rawkv.NewClient(context.TODO(), pdAddr, config.DefaultConfig().Security) 21 | if err != nil { 22 | log.F(err) 23 | } 24 | return &rawkvClient{ 25 | rawClient: client, 26 | pdAddr: pdAddr, 27 | } 28 | } 29 | 30 | type rawkvClient struct { 31 | rawClient *rawkv.Client 32 | pdAddr []string 33 | } 34 | 35 | func (c *rawkvClient) Close() { 36 | if c.rawClient != nil { 37 | c.rawClient.Close() 38 | } 39 | } 40 | 41 | func (c *rawkvClient) GetClientMode() TiKV_MODE { 42 | return RAW_CLIENT 43 | } 44 | 45 | func (c *rawkvClient) GetClusterID() string { 46 | return fmt.Sprintf("%d", c.rawClient.ClusterID()) 47 | } 48 | 49 | func (c *rawkvClient) GetStores() ([]StoreInfo, error) { 50 | panic("rawkvClient does not support GetStores()") 51 | } 52 | 53 | func (c *rawkvClient) GetPDs() ([]PDInfo, error) { 54 | panic("rawkvClient does not support GetPDs()") 55 | } 56 | 57 | func (c *rawkvClient) GetPDClient() pd.Client { 58 | panic("rawkvClient does not support GetPDClient()") 59 | } 60 | 61 | func (c *rawkvClient) Put(ctx context.Context, kv KV) error { 62 | return c.rawClient.Put(context.TODO(), kv.K, kv.V) 63 | } 64 | 65 | func (c *rawkvClient) BatchPut(ctx context.Context, kvs []KV) error { 66 | for _, kv := range kvs { 67 | err := c.rawClient.Put(context.TODO(), kv.K[:], kv.V[:]) 68 | if err != nil { 69 | return err 70 | } 71 | } 72 | return nil 73 | } 74 | 75 | func (c *rawkvClient) Get(ctx context.Context, k Key) (KV, error) { 76 | v, err := c.rawClient.Get(context.TODO(), k) 77 | if err != nil { 78 | return KV{}, err 79 | } 80 | if v == nil { 81 | return KV{}, errors.New("key not found") 82 | } 83 | return KV{k, v}, nil 84 | } 85 | 86 | func (c *rawkvClient) Scan(ctx context.Context, prefix []byte) (KVS, int, error) { 87 | scanOpts := utils.PropFromContext(ctx) 88 | 89 | strictPrefix := scanOpts.GetBool(tcli.ScanOptStrictPrefix, false) 90 | countOnly := scanOpts.GetBool(tcli.ScanOptCountOnly, false) 91 | keyOnly := scanOpts.GetBool(tcli.ScanOptKeyOnly, false) 92 | // count only mode will ignore this 93 | limit := scanOpts.GetInt(tcli.ScanOptLimit, 100) 94 | if countOnly { 95 | // BUG, TODO 96 | limit = MaxRawKVScanLimit 97 | } 98 | 99 | keys, values, err := c.rawClient.Scan(ctx, prefix, []byte{}, limit) 100 | if err != nil { 101 | return nil, 0, err 102 | } 103 | 104 | var ret []KV 105 | var lastKey KV 106 | count := 0 107 | for i := 0; i < len(keys); i++ { 108 | if strictPrefix && !bytes.HasPrefix(keys[i], prefix) { 109 | break 110 | } 111 | if !countOnly { 112 | if keyOnly { 113 | ret = append(ret, KV{K: keys[i], V: nil}) 114 | } else { 115 | ret = append(ret, KV{K: keys[i], V: values[i]}) 116 | } 117 | } 118 | count++ 119 | lastKey.K = keys[i] 120 | } 121 | if countOnly { 122 | ret = append(ret, KV{K: []byte("Count"), V: []byte(fmt.Sprintf("%d", count))}) 123 | ret = append(ret, KV{K: []byte("Last Key"), V: []byte(lastKey.K)}) 124 | } 125 | return ret, count, nil 126 | } 127 | 128 | func (c *rawkvClient) Delete(ctx context.Context, k Key) error { 129 | err := c.rawClient.Delete(context.TODO(), []byte(k)) 130 | return err 131 | } 132 | 133 | func (c *rawkvClient) BatchDelete(ctx context.Context, kvs []KV) error { 134 | keys := [][]byte{} 135 | for _, kv := range kvs { 136 | keys = append(keys, []byte(kv.K)) 137 | } 138 | return c.rawClient.BatchDelete(context.TODO(), keys) 139 | } 140 | 141 | func (c *rawkvClient) DeletePrefix(ctx context.Context, prefix Key, limit int) (Key, int, error) { 142 | endKey := []byte(prefix) 143 | endKey[len(endKey)] += 0x1 144 | keys, _, err := c.rawClient.Scan(ctx, prefix, endKey, MaxRawKVScanLimit) 145 | if err != nil { 146 | return nil, 0, err 147 | } 148 | lastKey := Key(keys[len(keys)-1]) 149 | return lastKey, len(keys), c.rawClient.BatchDelete(context.TODO(), keys) 150 | } 151 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | "strings" 9 | "sync/atomic" 10 | 11 | "github.com/c4pt0r/tcli/utils" 12 | 13 | "github.com/pkg/errors" 14 | pd "github.com/tikv/pd/client" 15 | ) 16 | 17 | type Key []byte 18 | type Value []byte 19 | 20 | type KV struct { 21 | K Key 22 | V Value 23 | } 24 | 25 | type KVS []KV 26 | 27 | func (kvs KVS) Print() { 28 | 29 | formatter := "table" 30 | if r, ok := utils.SysVarGet(utils.SysVarPrintFormatKey); ok { 31 | formatter = string(r) 32 | } 33 | 34 | switch formatter { 35 | case "json": 36 | { 37 | //Convert key value pairs to string or else JSON Marshaling breaks 38 | kvmaps := make([]map[string]interface{}, len(kvs)) 39 | for i := 0; i < len(kvs); i++ { 40 | kvmaps[i] = make(map[string]interface{}) 41 | kvmaps[i][string(kvs[i].K)] = string(kvs[i].V) 42 | } 43 | out, _ := json.MarshalIndent(kvmaps, "", " ") 44 | fmt.Println(string(out)) 45 | } 46 | case "raw": 47 | { 48 | for _, kv := range kvs { 49 | fmt.Println(kv.K, "\t=>\t", kv.V) 50 | } 51 | } 52 | default: // table 53 | { 54 | if len(kvs) == 0 { 55 | return 56 | } 57 | data := [][]string{ 58 | {"Key", "Value"}, 59 | } 60 | for _, kv := range kvs { 61 | row := []string{string(kv.K), string(kv.V)} 62 | data = append(data, row) 63 | } 64 | utils.PrintTable(data) 65 | if len(kvs) > 1 { 66 | fmt.Fprintf(os.Stderr, "%d Records Found\n", len(kvs)) 67 | } else { 68 | fmt.Fprintf(os.Stderr, "%d Record Found\n", len(kvs)) 69 | } 70 | } 71 | 72 | } 73 | } 74 | 75 | // Global client instance, safe to use concurrently 76 | var ( 77 | _globalKvClient atomic.Value 78 | ) 79 | 80 | func InitTiKVClient(pdAddrs []string, clientMode string) error { 81 | switch strings.ToLower(clientMode) { 82 | case "raw": 83 | kvClient := newRawKVClient(pdAddrs) 84 | _globalKvClient.Store(kvClient) 85 | return nil 86 | case "txn": 87 | kvClient := newTxnKVClient(pdAddrs) 88 | _globalKvClient.Store(kvClient) 89 | return nil 90 | default: 91 | return errors.Errorf("Unrecognized TiKV mode: %s", clientMode) 92 | } 93 | } 94 | 95 | func GetTiKVClient() Client { 96 | return _globalKvClient.Load().(Client) 97 | } 98 | 99 | // Make sure txnkvClient implements Client interface 100 | var _ Client = (*txnkvClient)(nil) 101 | var _ Client = (*rawkvClient)(nil) 102 | 103 | type Client interface { 104 | GetClientMode() TiKV_MODE 105 | GetClusterID() string 106 | GetStores() ([]StoreInfo, error) 107 | GetPDs() ([]PDInfo, error) 108 | GetPDClient() pd.Client 109 | 110 | Put(ctx context.Context, kv KV) error 111 | BatchPut(ctx context.Context, kv []KV) error 112 | 113 | Get(ctx context.Context, k Key) (KV, error) 114 | Scan(ctx context.Context, prefix []byte) (KVS, int, error) 115 | 116 | Delete(ctx context.Context, k Key) error 117 | BatchDelete(ctx context.Context, kvs []KV) error 118 | DeletePrefix(ctx context.Context, prefix Key, limit int) (Key, int, error) 119 | } 120 | 121 | type TiKV_MODE int 122 | 123 | const ( 124 | RAW_CLIENT TiKV_MODE = 0 125 | TXN_CLIENT TiKV_MODE = 1 126 | ) 127 | 128 | func (mode TiKV_MODE) String() string { 129 | switch mode { 130 | case RAW_CLIENT: 131 | return "Mode: Raw" 132 | case TXN_CLIENT: 133 | return "Mode: Txn" 134 | } 135 | return "unknown" 136 | } 137 | 138 | var ( 139 | propertiesKey = "property" 140 | ) 141 | 142 | type StoreInfo struct { 143 | ID string 144 | Version string 145 | Addr string 146 | State string 147 | StatusAddress string 148 | Labels string 149 | } 150 | 151 | type PDInfo struct { 152 | Name string 153 | ClientURLs []string 154 | } 155 | 156 | func (StoreInfo) TableTitle() []string { 157 | return []string{"Store ID", "Version", "Address", "State", "Status Address", "Labels"} 158 | } 159 | 160 | func (s StoreInfo) Flatten() []string { 161 | return []string{s.ID, s.Version, s.Addr, s.State, s.StatusAddress, s.Labels} 162 | } 163 | 164 | func (s StoreInfo) String() string { 165 | return fmt.Sprintf("store_id:\"%s\" version:\"%s\" addr:\"%s\" state:\"%s\" status_addr:\"%s\" labels:\"%s\"", 166 | s.ID, s.Version, s.Addr, s.State, s.StatusAddress, s.Labels) 167 | } 168 | 169 | func (p PDInfo) TableTitle() []string { 170 | return []string{"Name", "Client URLs"} 171 | } 172 | 173 | func (p PDInfo) Flatten() []string { 174 | return []string{p.Name, strings.Join(p.ClientURLs, ",")} 175 | } 176 | 177 | func (p PDInfo) String() string { 178 | return fmt.Sprintf("name:\"%s\" client_urls:\"%s\"", p.Name, strings.Join(p.ClientURLs, ",")) 179 | } 180 | -------------------------------------------------------------------------------- /kvcmds/cmd_scan.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/c4pt0r/tcli" 8 | "github.com/c4pt0r/tcli/client" 9 | "github.com/c4pt0r/tcli/utils" 10 | "github.com/magiconair/properties" 11 | ) 12 | 13 | type ScanCmd struct{} 14 | 15 | var _ tcli.Cmd = ScanCmd{} 16 | 17 | func (c ScanCmd) Name() string { return "scan" } 18 | func (c ScanCmd) Alias() []string { return []string{"scan"} } 19 | func (c ScanCmd) Help() string { 20 | return `Scan keys from start key, use "scan --help" for more details` 21 | } 22 | 23 | func (c ScanCmd) LongHelp() string { 24 | s := c.Help() 25 | s += ` 26 | Usage: 27 | scan 28 | Options: 29 | --limit=, default 100 30 | --key-only=, default false 31 | --strict-prefix=, default false 32 | --count-only=, default false 33 | Examples: 34 | # scan from "a", max 10 keys 35 | scan "a" --limit=10 36 | 37 | # scan from "a", max 10 keys, output key-only 38 | scan "a" --limit=10 --key-only=true 39 | 40 | # scan from "a", max 10 keys, output key-only, the result keys are strictly prefix 41 | scan "a" --limit=10 --strict-prefix 42 | 43 | # scan from "a", count the number of keys, max 10 keys 44 | scan "a" --limit=10 --count-only 45 | 46 | scan "a" --limit=10 --strict-prefix --key-only=true 47 | scan $head --limit=10 --key-only=true 48 | ` 49 | return s 50 | } 51 | 52 | func (c ScanCmd) Handler() func(ctx context.Context) { 53 | return func(ctx context.Context) { 54 | utils.OutputWithElapse(func() error { 55 | ic := utils.ExtractIshellContext(ctx) 56 | if len(ic.Args) < 1 { 57 | utils.Print(c.LongHelp()) 58 | return nil 59 | } 60 | s := ic.RawArgs[1] 61 | // it's a hex string literal 62 | startKey, err := utils.GetStringLit(s) 63 | if err != nil { 64 | return err 65 | } 66 | scanOpt := properties.NewProperties() 67 | if len(ic.Args) > 1 { 68 | err := utils.SetOptByString(ic.Args[1:], scanOpt) 69 | if err != nil { 70 | return err 71 | } 72 | } 73 | kvs, _, err := client.GetTiKVClient().Scan(utils.ContextWithProp(context.TODO(), scanOpt), startKey) 74 | if err != nil { 75 | return err 76 | } 77 | kvs.Print() 78 | return nil 79 | }) 80 | } 81 | } 82 | 83 | type ScanPrefixCmd struct{} 84 | 85 | var _ tcli.Cmd = ScanPrefixCmd{} 86 | 87 | func (c ScanPrefixCmd) Name() string { return "scanp" } 88 | func (c ScanPrefixCmd) Alias() []string { return []string{"scanp"} } 89 | func (c ScanPrefixCmd) Help() string { 90 | return `scan keys with prefix, equals to "scan [key prefix] strict-prefix=true"` 91 | } 92 | func (c ScanPrefixCmd) LongHelp() string { 93 | return c.Help() 94 | } 95 | 96 | func (c ScanPrefixCmd) Handler() func(ctx context.Context) { 97 | // TODO need refactor 98 | return func(ctx context.Context) { 99 | utils.OutputWithElapse(func() error { 100 | ic := utils.ExtractIshellContext(ctx) 101 | if len(ic.Args) < 1 { 102 | utils.Print(c.Help()) 103 | return nil 104 | } 105 | s := ic.RawArgs[1] 106 | // it's a hex string literal 107 | startKey, err := utils.GetStringLit(s) 108 | if err != nil { 109 | return err 110 | } 111 | scanOpt := properties.NewProperties() 112 | if len(ic.Args) > 1 { 113 | err := utils.SetOptByString(ic.Args[1:], scanOpt) 114 | if err != nil { 115 | return err 116 | } 117 | } 118 | scanOpt.Set(tcli.ScanOptStrictPrefix, "true") 119 | kvs, _, err := client.GetTiKVClient().Scan(utils.ContextWithProp(context.TODO(), scanOpt), startKey) 120 | if err != nil { 121 | return err 122 | } 123 | kvs.Print() 124 | return nil 125 | }) 126 | } 127 | } 128 | 129 | type HeadCmd struct{} 130 | 131 | var _ tcli.Cmd = HeadCmd{} 132 | 133 | func (c HeadCmd) Name() string { return "head" } 134 | func (c HeadCmd) Alias() []string { return []string{"head"} } 135 | func (c HeadCmd) Help() string { 136 | return `scan keys from $head, equals to "scan $head limit=N", usage: head ` 137 | } 138 | 139 | func (c HeadCmd) LongHelp() string { 140 | return c.Help() 141 | } 142 | 143 | func (c HeadCmd) Handler() func(ctx context.Context) { 144 | // TODO need refactor 145 | return func(ctx context.Context) { 146 | utils.OutputWithElapse(func() error { 147 | ic := utils.ExtractIshellContext(ctx) 148 | if len(ic.Args) < 1 { 149 | utils.Print(c.Help()) 150 | return nil 151 | } 152 | _, err := strconv.Atoi(ic.Args[0]) 153 | if err != nil { 154 | return err 155 | } 156 | scanOpt := properties.NewProperties() 157 | if len(ic.Args) > 1 { 158 | err := utils.SetOptByString(ic.Args[1:], scanOpt) 159 | if err != nil { 160 | return err 161 | } 162 | } 163 | // set limit 164 | scanOpt.Set(tcli.ScanOptLimit, ic.Args[0]) 165 | scanOpt.Set(tcli.ScanOptStrictPrefix, "false") 166 | kvs, _, err := client.GetTiKVClient().Scan(utils.ContextWithProp(context.TODO(), scanOpt), []byte("\x00")) 167 | if err != nil { 168 | return err 169 | } 170 | kvs.Print() 171 | return nil 172 | }) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "runtime/pprof" 9 | "strings" 10 | 11 | "github.com/c4pt0r/tcli" 12 | "github.com/c4pt0r/tcli/client" 13 | "github.com/c4pt0r/tcli/kvcmds" 14 | "github.com/c4pt0r/tcli/opcmds" 15 | "github.com/c4pt0r/tcli/utils" 16 | 17 | "github.com/abiosoft/ishell" 18 | "github.com/c4pt0r/log" 19 | "github.com/fatih/color" 20 | plog "github.com/pingcap/log" 21 | ) 22 | 23 | var ( 24 | pdAddr = flag.String("pd", "localhost:2379", "PD addr") 25 | clientLog = flag.String("log-file", "/dev/null", "TiKV client log file") 26 | clientLogLevel = flag.String("log-level", "info", "TiKV client log level") 27 | clientmode = flag.String("mode", "txn", "TiKV API mode, accepted values: [raw | txn]") 28 | resultFmt = flag.String("output-format", "table", "output format, accepted values: [table | json]") 29 | profileFile = flag.String("p", "", "profile file") 30 | ) 31 | var ( 32 | logo string = "" 33 | ) 34 | 35 | // RegisteredCmds global command registration 36 | // the Cmd objects inside this array can only be used 37 | var RegisteredCmds = []tcli.Cmd{ 38 | kvcmds.ScanCmd{}, 39 | kvcmds.ScanPrefixCmd{}, 40 | kvcmds.HeadCmd{}, 41 | kvcmds.PutCmd{}, 42 | kvcmds.BackupCmd{}, 43 | kvcmds.NewBenchCmd( 44 | kvcmds.NewYcsbBench(*pdAddr), 45 | ), 46 | kvcmds.GetCmd{}, 47 | kvcmds.LoadCsvCmd{}, 48 | kvcmds.DeleteCmd{}, 49 | kvcmds.DeletePrefixCmd{}, 50 | kvcmds.DeleteAllCmd{}, 51 | kvcmds.CountCmd{}, 52 | kvcmds.EchoCmd{}, 53 | kvcmds.HexCmd{}, 54 | kvcmds.VarCmd{}, 55 | kvcmds.PrintVarsCmd{}, 56 | kvcmds.PrintSysVarsCmd{}, 57 | kvcmds.SysVarCmd{}, 58 | kvcmds.ExplainCmd{}, 59 | kvcmds.QueryCmd{}, 60 | opcmds.ListStoresCmd{}, 61 | opcmds.ListPDCmd{}, 62 | //opcmds.ConnectCmd{}, 63 | //opcmds.ConfigEditorCmd{}, 64 | } 65 | 66 | func initLog() { 67 | // keep pingcap's log silent 68 | conf := &plog.Config{Level: *clientLogLevel, File: plog.FileLogConfig{Filename: *clientLog}} 69 | lg, r, _ := plog.InitLogger(conf) 70 | plog.ReplaceGlobals(lg, r) 71 | 72 | log.SetLevelByString(*clientLogLevel) 73 | } 74 | 75 | func showWelcomeMessage() { 76 | fmt.Fprintf( 77 | os.Stderr, 78 | "Welcome, TiKV Cluster ID: %s, TiKV Mode: %s\n", 79 | client.GetTiKVClient().GetClusterID(), 80 | client.GetTiKVClient().GetClientMode(), 81 | ) 82 | 83 | if client.GetTiKVClient().GetClientMode() == client.RAW_CLIENT { 84 | return 85 | } 86 | 87 | // show pd members 88 | pdClient := client.GetTiKVClient().GetPDClient() 89 | members, err := pdClient.GetAllMembers(context.TODO()) 90 | if err != nil { 91 | log.F(err) 92 | } 93 | for _, member := range members { 94 | log.D("pd instance info:", member) 95 | } 96 | stores, err := client.GetTiKVClient().GetStores() 97 | if err != nil { 98 | log.F(err) 99 | } 100 | for _, store := range stores { 101 | log.D("tikv instance info:", store) 102 | } 103 | } 104 | 105 | func main() { 106 | flag.Parse() 107 | initLog() 108 | fmt.Fprintf(os.Stderr, "Try connecting to PD: %s...", *pdAddr) 109 | if err := client.InitTiKVClient([]string{*pdAddr}, *clientmode); err != nil { 110 | log.Fatal(err) 111 | } 112 | fmt.Fprintf(os.Stderr, "done\n") 113 | utils.InitBuiltinVaribles() 114 | 115 | // Set output format 116 | utils.SysVarSet(utils.SysVarPrintFormatKey, *resultFmt) 117 | 118 | showWelcomeMessage() 119 | 120 | if *profileFile != "" { 121 | f, err := os.Create(*profileFile) 122 | if err != nil { 123 | log.Fatal("could not create CPU profile: ", err) 124 | } 125 | defer f.Close() // error handling omitted for example 126 | if err := pprof.StartCPUProfile(f); err != nil { 127 | log.Fatal("could not start CPU profile: ", err) 128 | } 129 | defer pprof.StopCPUProfile() 130 | } 131 | 132 | // set shell prompts 133 | shell := ishell.New() 134 | if client.GetTiKVClient().GetClientMode() == client.RAW_CLIENT { 135 | // TODO: add pd leader addr after we can get PD client from RawKV client. 136 | shell.SetPrompt(fmt.Sprintf("%s> ", client.GetTiKVClient().GetClientMode())) 137 | } else { 138 | pdLeaderAddr := client.GetTiKVClient().GetPDClient().GetLeaderAddr() 139 | shell.SetPrompt(fmt.Sprintf("%s @ %s> ", client.GetTiKVClient().GetClientMode(), pdLeaderAddr)) 140 | } 141 | shell.EOF(func(c *ishell.Context) { shell.Close() }) 142 | shell.AutoHelp(false) 143 | 144 | // register shell commands 145 | for _, cmd := range RegisteredCmds { 146 | handler := cmd.Handler() 147 | //completer := cmd.Completer() 148 | longhelp := cmd.LongHelp() 149 | shell.SetHomeHistoryPath(".tcli.history") 150 | shell.AddCmd(&ishell.Cmd{ 151 | Name: cmd.Name(), 152 | Help: cmd.Help(), 153 | LongHelp: cmd.LongHelp(), 154 | Aliases: cmd.Alias(), 155 | Func: func(c *ishell.Context) { 156 | ctx := context.WithValue(context.TODO(), "ishell", c) 157 | if strings.ToLower(*clientLogLevel) == "debug" { 158 | fmt.Fprintln(os.Stderr, color.YellowString("Input:"), c.RawArgs) 159 | for _, arg := range c.Args { 160 | fmt.Fprintln(os.Stderr, color.YellowString("Arg:"), arg) 161 | } 162 | fmt.Fprintf(os.Stderr, "\033[33mOutput:\033[0m\n") 163 | } 164 | if len(c.Args) > 0 && c.Args[0] == "--help" { 165 | c.Println(longhelp) 166 | return 167 | } 168 | handler(ctx) 169 | }, 170 | }) 171 | } 172 | shell.Run() 173 | shell.Close() 174 | } 175 | -------------------------------------------------------------------------------- /client/txnkv_client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/c4pt0r/tcli/utils" 10 | 11 | "github.com/c4pt0r/tcli" 12 | 13 | "github.com/c4pt0r/log" 14 | "github.com/tikv/client-go/v2/tikv" 15 | pd "github.com/tikv/pd/client" 16 | ) 17 | 18 | func newTxnKVClient(pdAddr []string) *txnkvClient { 19 | client, err := tikv.NewTxnClient(pdAddr) 20 | if err != nil { 21 | log.F(err) 22 | } 23 | return &txnkvClient{ 24 | txnClient: client, 25 | pdAddr: pdAddr, 26 | } 27 | } 28 | 29 | type txnkvClient struct { 30 | txnClient *tikv.KVStore 31 | pdAddr []string 32 | } 33 | 34 | func (c *txnkvClient) Close() { 35 | if c.txnClient != nil { 36 | c.txnClient.Close() 37 | } 38 | } 39 | 40 | func (c *txnkvClient) GetClientMode() TiKV_MODE { 41 | return TXN_CLIENT 42 | } 43 | 44 | func (c *txnkvClient) GetClusterID() string { 45 | return fmt.Sprintf("%d", c.txnClient.GetPDClient().GetClusterID(context.TODO())) 46 | } 47 | 48 | func (c *txnkvClient) GetStores() ([]StoreInfo, error) { 49 | var ret []StoreInfo 50 | stores, err := c.txnClient.GetPDClient().GetAllStores(context.TODO()) 51 | if err != nil { 52 | return nil, err 53 | } 54 | for _, store := range stores { 55 | labels := store.GetLabels() 56 | var strLabels []string 57 | for _, label := range labels { 58 | strLabels = append(strLabels, fmt.Sprintf("%s=%s", label.Key, label.Value)) 59 | } 60 | ret = append(ret, StoreInfo{ 61 | ID: fmt.Sprintf("%d", store.GetId()), 62 | Version: store.GetVersion(), 63 | Addr: store.GetAddress(), 64 | State: store.GetState().String(), 65 | StatusAddress: store.GetStatusAddress(), 66 | Labels: strings.Join(strLabels, ","), 67 | }) 68 | } 69 | return ret, nil 70 | } 71 | 72 | func (c *txnkvClient) GetPDClient() pd.Client { 73 | return c.txnClient.GetPDClient() 74 | } 75 | 76 | func (c *txnkvClient) Put(ctx context.Context, kv KV) error { 77 | tx, err := c.txnClient.Begin() 78 | if err != nil { 79 | return err 80 | } 81 | 82 | tx.Set(kv.K, kv.V) 83 | 84 | err = tx.Commit(context.TODO()) 85 | if err != nil { 86 | err = tx.Rollback() 87 | if err != nil { 88 | return err 89 | } 90 | } 91 | return nil 92 | } 93 | 94 | func (c *txnkvClient) Scan(ctx context.Context, startKey []byte) (KVS, int, error) { 95 | scanOpts := utils.PropFromContext(ctx) 96 | tx, err := c.txnClient.Begin() 97 | if err != nil { 98 | return nil, 0, err 99 | } 100 | 101 | strictPrefix := scanOpts.GetBool(tcli.ScanOptStrictPrefix, false) 102 | countOnly := scanOpts.GetBool(tcli.ScanOptCountOnly, false) 103 | keyOnly := scanOpts.GetBool(tcli.ScanOptKeyOnly, false) 104 | if keyOnly || countOnly { 105 | tx.GetSnapshot().SetKeyOnly(keyOnly) 106 | } 107 | // count only mode will ignore this 108 | limit := scanOpts.GetInt(tcli.ScanOptLimit, 100) 109 | it, err := tx.Iter(startKey, nil) 110 | if err != nil { 111 | return nil, 0, err 112 | } 113 | defer it.Close() 114 | 115 | var ret []KV 116 | var lastKey KV 117 | count := 0 118 | for it.Valid() { 119 | if !countOnly && limit == 0 { 120 | break 121 | } 122 | if strictPrefix && !bytes.HasPrefix(it.Key(), startKey) { 123 | break 124 | } 125 | // count only will not use limit 126 | if !countOnly { 127 | ret = append(ret, KV{K: it.Key()[:], V: it.Value()[:]}) 128 | limit-- 129 | } 130 | count++ 131 | lastKey.K = it.Key()[:] 132 | it.Next() 133 | } 134 | if countOnly { 135 | ret = append(ret, KV{K: []byte("Count"), V: []byte(fmt.Sprintf("%d", count))}) 136 | ret = append(ret, KV{K: []byte("Last Key"), V: []byte(lastKey.K)}) 137 | } 138 | return ret, count, nil 139 | } 140 | 141 | func (c *txnkvClient) BatchPut(ctx context.Context, kvs []KV) error { 142 | tx, err := c.txnClient.Begin() 143 | if err != nil { 144 | return err 145 | } 146 | for _, kv := range kvs { 147 | err := tx.Set(kv.K[:], kv.V[:]) 148 | if err != nil { 149 | return err 150 | } 151 | } 152 | return tx.Commit(context.Background()) 153 | } 154 | 155 | func (c *txnkvClient) Get(ctx context.Context, k Key) (KV, error) { 156 | tx, err := c.txnClient.Begin() 157 | if err != nil { 158 | return KV{}, err 159 | } 160 | v, err := tx.Get(context.TODO(), k) 161 | if err != nil { 162 | return KV{}, err 163 | } 164 | return KV{K: k, V: v}, nil 165 | } 166 | 167 | func (c *txnkvClient) Delete(ctx context.Context, k Key) error { 168 | tx, err := c.txnClient.Begin() 169 | if err != nil { 170 | return err 171 | } 172 | tx.Delete(k) 173 | return tx.Commit(context.Background()) 174 | } 175 | 176 | // return lastKey, delete count, error 177 | func (c *txnkvClient) DeletePrefix(ctx context.Context, prefix Key, limit int) (Key, int, error) { 178 | tx, err := c.txnClient.Begin() 179 | if err != nil { 180 | return nil, 0, err 181 | } 182 | 183 | tx.GetSnapshot().SetKeyOnly(true) 184 | 185 | it, err := tx.Iter(prefix, nil) 186 | if err != nil { 187 | return nil, 0, err 188 | } 189 | defer it.Close() 190 | 191 | var lastKey KV 192 | count := 0 193 | 194 | var batch []KV 195 | 196 | for it.Valid() && limit > 0 { 197 | if !bytes.HasPrefix(it.Key(), prefix) { 198 | break 199 | } 200 | lastKey.K = it.Key()[:] 201 | batch = append(batch, KV{K: it.Key()[:]}) 202 | // TODO batch size shoule not be fixed 203 | if len(batch) == 1000 { 204 | // do delete 205 | if err := c.BatchDelete(ctx, batch); err != nil { 206 | return lastKey.K, count, err 207 | } 208 | count += len(batch) 209 | // reset batch 210 | batch = nil 211 | } 212 | limit-- 213 | it.Next() 214 | } 215 | if len(batch) > 0 { 216 | if err := c.BatchDelete(ctx, batch); err != nil { 217 | return nil, count, err 218 | } 219 | count += len(batch) 220 | } 221 | return lastKey.K, count, nil 222 | } 223 | 224 | func (c *txnkvClient) BatchDelete(ctx context.Context, kvs []KV) error { 225 | tx, err := c.txnClient.Begin() 226 | if err != nil { 227 | return err 228 | } 229 | for _, kv := range kvs { 230 | err := tx.Delete(kv.K) 231 | if err != nil { 232 | return err 233 | } 234 | } 235 | return tx.Commit(context.Background()) 236 | } 237 | 238 | func (c *txnkvClient) GetPDs() ([]PDInfo, error) { 239 | pds, err := c.txnClient.GetPDClient().GetAllMembers(context.TODO()) 240 | if err != nil { 241 | return nil, err 242 | } 243 | var ret []PDInfo 244 | for _, pd := range pds { 245 | ret = append(ret, PDInfo{Name: pd.Name, ClientURLs: pd.GetClientUrls()}) 246 | } 247 | return ret, nil 248 | } 249 | -------------------------------------------------------------------------------- /kvcmds/cmd_var.go: -------------------------------------------------------------------------------- 1 | package kvcmds 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/c4pt0r/tcli" 10 | "github.com/c4pt0r/tcli/utils" 11 | 12 | "github.com/abiosoft/ishell" 13 | ) 14 | 15 | type VarCmd struct{} 16 | 17 | func NewVarCmd() VarCmd { 18 | return VarCmd{} 19 | } 20 | 21 | var _ tcli.Cmd = VarCmd{} 22 | 23 | func (c VarCmd) Name() string { return "var" } 24 | func (c VarCmd) Alias() []string { return []string{"var", "let"} } 25 | func (c VarCmd) Help() string { 26 | return `set variables, usage: 27 | var =, variable name and value are both string 28 | example: scan $varname or get $varname` 29 | } 30 | 31 | func (c VarCmd) LongHelp() string { 32 | return c.Help() 33 | } 34 | 35 | func (c VarCmd) Handler() func(ctx context.Context) { 36 | return func(ctx context.Context) { 37 | utils.OutputWithElapse(func() error { 38 | ic := utils.ExtractIshellContext(ctx) 39 | if len(ic.Args) < 1 { 40 | utils.Print(c.LongHelp()) 41 | return errors.New("wrong args") 42 | } 43 | stmt := strings.Join(ic.RawArgs[1:], " ") 44 | parts := strings.Split(stmt, "=") 45 | if len(parts) != 2 { 46 | utils.Print(c.LongHelp()) 47 | return errors.New("wrong format") 48 | } 49 | varName, varValue := parts[0], parts[1] 50 | varName = strings.TrimSpace(varName) 51 | 52 | if !utils.IsStringLit(varValue) { 53 | return errors.New("wrong format for value") 54 | } 55 | // it's a hex string literal 56 | value, err := utils.GetStringLit(varValue) 57 | if err != nil { 58 | return err 59 | } 60 | utils.VarSet(varName, value) 61 | return nil 62 | }) 63 | } 64 | } 65 | 66 | type EchoCmd struct{} 67 | 68 | var _ tcli.Cmd = EchoCmd{} 69 | 70 | func (c EchoCmd) Name() string { return "echo" } 71 | func (c EchoCmd) Alias() []string { return []string{"echo"} } 72 | func (c EchoCmd) Help() string { 73 | return `echo $` 74 | } 75 | 76 | func (c EchoCmd) LongHelp() string { 77 | return c.Help() 78 | } 79 | 80 | func (c EchoCmd) Handler() func(ctx context.Context) { 81 | return func(ctx context.Context) { 82 | utils.OutputWithElapse(func() error { 83 | ic := ctx.Value("ishell").(*ishell.Context) 84 | if len(ic.Args) < 1 { 85 | utils.Print(c.Help()) 86 | return errors.New("wrong args number") 87 | } 88 | 89 | varName := ic.Args[0] 90 | if !strings.HasPrefix(varName, "$") { 91 | return errors.New("varname should have $ as prefix") 92 | } 93 | varName = varName[1:] 94 | if val, ok := utils.VarGet(varName); ok { 95 | utils.Print(fmt.Sprintf("string:\"%s\" bytes: %v", val, val)) 96 | } else { 97 | return errors.New("no such variable") 98 | } 99 | return nil 100 | }) 101 | } 102 | } 103 | 104 | type PrintVarsCmd struct{} 105 | 106 | var _ tcli.Cmd = PrintVarsCmd{} 107 | 108 | func (c PrintVarsCmd) Name() string { return "env" } 109 | func (c PrintVarsCmd) Alias() []string { return []string{"env"} } 110 | func (c PrintVarsCmd) Help() string { 111 | return `print env variables` 112 | } 113 | 114 | func (c PrintVarsCmd) LongHelp() string { 115 | return c.Help() 116 | } 117 | 118 | func (c PrintVarsCmd) Handler() func(ctx context.Context) { 119 | return func(ctx context.Context) { 120 | utils.OutputWithElapse(func() error { 121 | utils.PrintGlobalVaribles() 122 | return nil 123 | }) 124 | } 125 | } 126 | 127 | type PrintSysVarsCmd struct{} 128 | 129 | var _ tcli.Cmd = PrintSysVarsCmd{} 130 | 131 | func (c PrintSysVarsCmd) Name() string { return "sysenv" } 132 | func (c PrintSysVarsCmd) Alias() []string { return []string{"sysenv"} } 133 | func (c PrintSysVarsCmd) Help() string { 134 | return `print system env variables` 135 | } 136 | 137 | func (c PrintSysVarsCmd) LongHelp() string { 138 | return c.Help() 139 | } 140 | 141 | func (c PrintSysVarsCmd) Handler() func(ctx context.Context) { 142 | return func(ctx context.Context) { 143 | utils.OutputWithElapse(func() error { 144 | utils.PrintSysVaribles() 145 | return nil 146 | }) 147 | } 148 | } 149 | 150 | type HexCmd struct{} 151 | 152 | var _ tcli.Cmd = HexCmd{} 153 | 154 | func (c HexCmd) Name() string { return "hexdump" } 155 | func (c HexCmd) Alias() []string { return []string{"hex"} } 156 | func (c HexCmd) Help() string { 157 | return `hexdump ` 158 | } 159 | 160 | func (c HexCmd) LongHelp() string { 161 | return c.Help() 162 | } 163 | 164 | func (c HexCmd) Handler() func(ctx context.Context) { 165 | return func(ctx context.Context) { 166 | utils.OutputWithElapse(func() error { 167 | ic := ctx.Value("ishell").(*ishell.Context) 168 | if len(ic.Args) < 1 { 169 | utils.Print(c.Help()) 170 | return errors.New("wrong args number") 171 | } 172 | 173 | s := strings.Join(ic.RawArgs[1:], " ") 174 | utils.Print(fmt.Sprintf("string: %s\nbytes: %v\nhexLit: h'%s'", s, 175 | utils.Bytes2hex([]byte(s)), 176 | utils.Bytes2hex([]byte(s)))) 177 | return nil 178 | }) 179 | } 180 | } 181 | 182 | type SysVarCmd struct{} 183 | 184 | var _ tcli.Cmd = SysVarCmd{} 185 | 186 | func NewSysVarCmd() SysVarCmd { 187 | return SysVarCmd{} 188 | } 189 | 190 | func (c SysVarCmd) Name() string { return "sysvar" } 191 | func (c SysVarCmd) Alias() []string { return []string{"sysvar", "let"} } 192 | func (c SysVarCmd) Help() string { 193 | return `set system variables, usage: 194 | sysvar =, variable name and value are both string 195 | example: scan $varname or get $varname` 196 | } 197 | 198 | func (c SysVarCmd) LongHelp() string { 199 | return c.Help() 200 | } 201 | 202 | func (c SysVarCmd) Handler() func(ctx context.Context) { 203 | return func(ctx context.Context) { 204 | utils.OutputWithElapse(func() error { 205 | ic := utils.ExtractIshellContext(ctx) 206 | if len(ic.Args) < 1 { 207 | utils.Print(c.Help()) 208 | return errors.New("wrong args") 209 | } 210 | stmt := strings.Join(ic.RawArgs[1:], " ") 211 | parts := strings.Split(stmt, "=") 212 | if len(parts) != 2 { 213 | utils.Print(c.Help()) 214 | return errors.New("wrong format") 215 | } 216 | varName, varValue := parts[0], parts[1] 217 | varName = strings.TrimSpace(varName) 218 | 219 | if !utils.IsStringLit(varValue) { 220 | return errors.New("wrong format for value") 221 | } 222 | // it's a hex string literal 223 | value, err := utils.GetStringLit(varValue) 224 | if err != nil { 225 | return err 226 | } 227 | utils.SysVarSet(varName, string(value)) 228 | return nil 229 | }) 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /sample.csv: -------------------------------------------------------------------------------- 1 | Key,Value 2 | h'68656c6c6f776f726c64', nice 3 | h'68656c6c6f776f726c63', helllllll 4 | Hello,World 5 | 1,79 6 | 2,54 7 | 3,74 8 | 4,62 9 | 5,85 10 | 6,55 11 | 7,88 12 | 8,85 13 | 9,51 14 | 10,85 15 | 11,54 16 | 12,84 17 | 13,78 18 | 14,47 19 | 15,83 20 | 16,52 21 | 17,62 22 | 18,84 23 | 19,52 24 | 20,79 25 | 21,51 26 | 22,47 27 | 23,78 28 | 24,69 29 | 25,74 30 | 26,83 31 | 27,55 32 | 28,76 33 | 29,78 34 | 30,79 35 | 31,73 36 | 32,77 37 | 33,66 38 | 34,80 39 | 35,74 40 | 36,52 41 | 37,48 42 | 38,80 43 | 39,59 44 | 40,90 45 | 41,80 46 | 42,58 47 | 43,84 48 | 44,58 49 | 45,73 50 | 46,83 51 | 47,64 52 | 48,53 53 | 49,82 54 | 50,59 55 | 51,75 56 | 52,90 57 | 53,54 58 | 54,80 59 | 55,54 60 | 56,83 61 | 57,71 62 | 58,64 63 | 59,77 64 | 60,81 65 | 61,59 66 | 62,84 67 | 63,48 68 | 64,82 69 | 65,60 70 | 66,92 71 | 67,78 72 | 68,78 73 | 69,65 74 | 70,73 75 | 71,82 76 | 72,56 77 | 73,79 78 | 74,71 79 | 75,62 80 | 76,76 81 | 77,60 82 | 78,78 83 | 79,76 84 | 80,83 85 | 81,75 86 | 82,82 87 | 83,70 88 | 84,65 89 | 85,73 90 | 86,88 91 | 87,76 92 | 88,80 93 | 89,48 94 | 90,86 95 | 91,60 96 | 92,90 97 | 93,50 98 | 94,78 99 | 95,63 100 | 96,72 101 | 97,84 102 | 98,75 103 | 99,51 104 | 100,82 105 | 101,62 106 | 102,88 107 | 103,49 108 | 104,83 109 | 105,81 110 | 106,47 111 | 107,84 112 | 108,52 113 | 109,86 114 | 110,81 115 | 111,75 116 | 112,59 117 | 113,89 118 | 114,79 119 | 115,59 120 | 116,81 121 | 117,50 122 | 118,85 123 | 119,59 124 | 120,87 125 | 121,53 126 | 122,69 127 | 123,77 128 | 124,56 129 | 125,88 130 | 126,81 131 | 127,45 132 | 128,82 133 | 129,55 134 | 130,90 135 | 131,45 136 | 132,83 137 | 133,56 138 | 134,89 139 | 135,46 140 | 136,82 141 | 137,51 142 | 138,86 143 | 139,53 144 | 140,79 145 | 141,81 146 | 142,60 147 | 143,82 148 | 144,77 149 | 145,76 150 | 146,59 151 | 147,80 152 | 148,49 153 | 149,96 154 | 150,53 155 | 151,77 156 | 152,77 157 | 153,65 158 | 154,81 159 | 155,71 160 | 156,70 161 | 157,81 162 | 158,93 163 | 159,53 164 | 160,89 165 | 161,45 166 | 162,86 167 | 163,58 168 | 164,78 169 | 165,66 170 | 166,76 171 | 167,63 172 | 168,88 173 | 169,52 174 | 170,93 175 | 171,49 176 | 172,57 177 | 173,77 178 | 174,68 179 | 175,81 180 | 176,81 181 | 177,73 182 | 178,50 183 | 179,85 184 | 180,74 185 | 181,55 186 | 182,77 187 | 183,83 188 | 184,83 189 | 185,51 190 | 186,78 191 | 187,84 192 | 188,46 193 | 189,83 194 | 190,55 195 | 191,81 196 | 192,57 197 | 193,76 198 | 194,84 199 | 195,77 200 | 196,81 201 | 197,87 202 | 198,77 203 | 199,51 204 | 200,78 205 | 201,60 206 | 202,82 207 | 203,91 208 | 204,53 209 | 205,78 210 | 206,46 211 | 207,77 212 | 208,84 213 | 209,49 214 | 210,83 215 | 211,71 216 | 212,80 217 | 213,49 218 | 214,75 219 | 215,64 220 | 216,76 221 | 217,53 222 | 218,94 223 | 219,55 224 | 220,76 225 | 221,50 226 | 222,82 227 | 223,54 228 | 224,75 229 | 225,78 230 | 226,79 231 | 227,78 232 | 228,78 233 | 229,70 234 | 230,79 235 | 231,70 236 | 232,54 237 | 233,86 238 | 234,50 239 | 235,90 240 | 236,54 241 | 237,54 242 | 238,77 243 | 239,79 244 | 240,64 245 | 241,75 246 | 242,47 247 | 243,86 248 | 244,63 249 | 245,85 250 | 246,82 251 | 247,57 252 | 248,82 253 | 249,67 254 | 250,74 255 | 251,54 256 | 252,83 257 | 253,73 258 | 254,73 259 | 255,88 260 | 256,80 261 | 257,71 262 | 258,83 263 | 259,56 264 | 260,79 265 | 261,78 266 | 262,84 267 | 263,58 268 | 264,83 269 | 265,43 270 | 266,60 271 | 267,75 272 | 268,81 273 | 269,46 274 | 270,90 275 | 271,46 276 | 272,74 277 | 1,79 278 | 2,54 279 | 3,74 280 | 4,62 281 | 5,85 282 | 6,55 283 | 7,88 284 | 8,85 285 | 9,51 286 | 10,85 287 | 11,54 288 | 12,84 289 | 13,78 290 | 14,47 291 | 15,83 292 | 16,52 293 | 17,62 294 | 18,84 295 | 19,52 296 | 20,79 297 | 21,51 298 | 22,47 299 | 23,78 300 | 24,69 301 | 25,74 302 | 26,83 303 | 27,55 304 | 28,76 305 | 29,78 306 | 30,79 307 | 31,73 308 | 32,77 309 | 33,66 310 | 34,80 311 | 35,74 312 | 36,52 313 | 37,48 314 | 38,80 315 | 39,59 316 | 40,90 317 | 41,80 318 | 42,58 319 | 43,84 320 | 44,58 321 | 45,73 322 | 46,83 323 | 47,64 324 | 48,53 325 | 49,82 326 | 50,59 327 | 51,75 328 | 52,90 329 | 53,54 330 | 54,80 331 | 55,54 332 | 56,83 333 | 57,71 334 | 58,64 335 | 59,77 336 | 60,81 337 | 61,59 338 | 62,84 339 | 63,48 340 | 64,82 341 | 65,60 342 | 66,92 343 | 67,78 344 | 68,78 345 | 69,65 346 | 70,73 347 | 71,82 348 | 72,56 349 | 73,79 350 | 74,71 351 | 75,62 352 | 76,76 353 | 77,60 354 | 78,78 355 | 79,76 356 | 80,83 357 | 81,75 358 | 82,82 359 | 83,70 360 | 84,65 361 | 85,73 362 | 86,88 363 | 87,76 364 | 88,80 365 | 89,48 366 | 90,86 367 | 91,60 368 | 92,90 369 | 93,50 370 | 94,78 371 | 95,63 372 | 96,72 373 | 97,84 374 | 98,75 375 | 99,51 376 | 100,82 377 | 101,62 378 | 102,88 379 | 103,49 380 | 104,83 381 | 105,81 382 | 106,47 383 | 107,84 384 | 108,52 385 | 109,86 386 | 110,81 387 | 111,75 388 | 112,59 389 | 113,89 390 | 114,79 391 | 115,59 392 | 116,81 393 | 117,50 394 | 118,85 395 | 119,59 396 | 120,87 397 | 121,53 398 | 122,69 399 | 123,77 400 | 124,56 401 | 125,88 402 | 126,81 403 | 127,45 404 | 128,82 405 | 129,55 406 | 130,90 407 | 131,45 408 | 132,83 409 | 133,56 410 | 134,89 411 | 135,46 412 | 136,82 413 | 137,51 414 | 138,86 415 | 139,53 416 | 140,79 417 | 141,81 418 | 142,60 419 | 143,82 420 | 144,77 421 | 145,76 422 | 146,59 423 | 147,80 424 | 148,49 425 | 149,96 426 | 150,53 427 | 151,77 428 | 152,77 429 | 153,65 430 | 154,81 431 | 155,71 432 | 156,70 433 | 157,81 434 | 158,93 435 | 159,53 436 | 160,89 437 | 161,45 438 | 162,86 439 | 163,58 440 | 164,78 441 | 165,66 442 | 166,76 443 | 167,63 444 | 168,88 445 | 169,52 446 | 170,93 447 | 171,49 448 | 172,57 449 | 173,77 450 | 174,68 451 | 175,81 452 | 176,81 453 | 177,73 454 | 178,50 455 | 179,85 456 | 180,74 457 | 181,55 458 | 182,77 459 | 183,83 460 | 184,83 461 | 185,51 462 | 186,78 463 | 187,84 464 | 188,46 465 | 189,83 466 | 190,55 467 | 191,81 468 | 192,57 469 | 193,76 470 | 194,84 471 | 195,77 472 | 196,81 473 | 197,87 474 | 198,77 475 | 199,51 476 | 200,78 477 | 201,60 478 | 202,82 479 | 203,91 480 | 204,53 481 | 205,78 482 | 206,46 483 | 207,77 484 | 208,84 485 | 209,49 486 | 210,83 487 | 211,71 488 | 212,80 489 | 213,49 490 | 214,75 491 | 215,64 492 | 216,76 493 | 217,53 494 | 218,94 495 | 219,55 496 | 220,76 497 | 221,50 498 | 222,82 499 | 223,54 500 | 224,75 501 | 225,78 502 | 226,79 503 | 227,78 504 | 228,78 505 | 229,70 506 | 230,79 507 | 231,70 508 | 232,54 509 | 233,86 510 | 234,50 511 | 235,90 512 | 236,54 513 | 237,54 514 | 238,77 515 | 239,79 516 | 240,64 517 | 241,75 518 | 242,47 519 | 243,86 520 | 244,63 521 | 245,85 522 | 246,82 523 | 247,57 524 | 248,82 525 | 249,67 526 | 250,74 527 | 251,54 528 | 252,83 529 | 253,73 530 | 254,73 531 | 255,88 532 | 256,80 533 | 257,71 534 | 258,83 535 | 259,56 536 | 260,79 537 | 261,78 538 | 262,84 539 | 263,58 540 | 264,83 541 | 265,43 542 | 266,60 543 | 267,75 544 | 268,81 545 | 269,46 546 | 270,90 547 | 271,46 548 | 272,74 549 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "os" 10 | "regexp" 11 | "strings" 12 | "time" 13 | 14 | "github.com/abiosoft/ishell" 15 | "github.com/magiconair/properties" 16 | "github.com/manifoldco/promptui" 17 | "github.com/olekukonko/tablewriter" 18 | "go.uber.org/atomic" 19 | ) 20 | 21 | var ( 22 | propertiesKey = "property" 23 | ) 24 | 25 | func PrintTable(data [][]string) { 26 | table := tablewriter.NewWriter(os.Stdout) 27 | table.SetHeader(data[0]) 28 | table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) 29 | table.SetCenterSeparator("|") 30 | table.AppendBulk(data[1:]) 31 | table.Render() 32 | } 33 | 34 | func PrintTableNoWrap(data [][]string) { 35 | table := tablewriter.NewWriter(os.Stdout) 36 | table.SetAutoWrapText(false) 37 | table.SetAutoFormatHeaders(false) 38 | table.SetHeader(data[0]) 39 | table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) 40 | table.SetCenterSeparator("|") 41 | table.AppendBulk(data[1:]) 42 | table.Render() 43 | } 44 | 45 | func OutputWithElapse(f func() error) error { 46 | tt := time.Now() 47 | err := f() 48 | if err != nil { 49 | fmt.Fprintf(os.Stderr, "\033[31mError: %s\033[0m\nElapse: %d ms\n", err, time.Since(tt)/time.Millisecond) 50 | } else { 51 | fmt.Fprintf(os.Stderr, "\033[32mSuccess\033[0m\nElapse: %d ms\n", time.Since(tt)/time.Millisecond) 52 | } 53 | return err 54 | } 55 | 56 | func Hexstr2bytes(hexStr string) ([]byte, error) { 57 | return hex.DecodeString(hexStr) 58 | } 59 | 60 | func Bytes2hex(s []byte) string { 61 | return hex.EncodeToString(s) 62 | } 63 | 64 | // String Literal Parsing 65 | // h'12332321' <---- Hex string 66 | type StrLitType int 67 | 68 | const ( 69 | StrLitHex StrLitType = iota 70 | StrLitNormal 71 | ) 72 | 73 | func Bytes2StrLit(b []byte) string { 74 | return fmt.Sprintf("h'%s'", Bytes2hex(b)) 75 | } 76 | 77 | var ( 78 | _reHexStr, _reNormalStr *regexp.Regexp 79 | ) 80 | 81 | func init() { 82 | _reHexStr, _ = regexp.Compile(`h"([^"\\]|\\[\s\S])*"|h'([^'\\]|\\[\s\S])*'`) 83 | _reNormalStr, _ = regexp.Compile(`"([^"\\]|\\[\s\S])*"|'([^'\\]|\\[\s\S])*'`) 84 | } 85 | 86 | func IsStringLit(raw string) bool { 87 | return _reHexStr.MatchString(raw) || _reNormalStr.MatchString(raw) 88 | } 89 | 90 | func GetStringLit(raw string) ([]byte, error) { 91 | if strings.HasPrefix(raw, "--") { 92 | return nil, fmt.Errorf("wrong format: [%s], it seems a option flag?", raw) 93 | } 94 | if raw[0] == '$' { 95 | varVal, ok := VarGet(raw[1:]) 96 | if !ok { 97 | return nil, errors.New("no such variable") 98 | } 99 | return varVal, nil 100 | } 101 | // h"" | h'' 102 | if _reHexStr.MatchString(raw) { 103 | out := _reHexStr.FindString(raw) 104 | val := string(out[2 : len(out)-1]) 105 | b, err := Hexstr2bytes(val) 106 | if err != nil { 107 | return nil, err 108 | } 109 | return b, nil 110 | } 111 | // "" | '' 112 | if _reNormalStr.MatchString(raw) { 113 | out := _reNormalStr.FindString(raw) 114 | val := out[1 : len(out)-1] 115 | return []byte(val), nil 116 | } 117 | return []byte(raw), nil 118 | } 119 | 120 | func SetOptByString(ss []string, props *properties.Properties) error { 121 | for _, flag := range ss { 122 | if strings.HasPrefix(flag, "--") { 123 | flag = flag[2:] 124 | parts := strings.Split(flag, "=") 125 | 126 | switch len(parts) { 127 | case 1: 128 | { 129 | // parse option flag like --option-bool 130 | k := strings.TrimSpace(parts[0]) 131 | props.Set(k, "true") 132 | } 133 | case 2: 134 | { 135 | // parse option flag like --option-a=value 136 | k, v := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) 137 | props.Set(k, v) 138 | } 139 | } 140 | } else { 141 | return fmt.Errorf("wrong flag format: [%s] ", flag) 142 | } 143 | } 144 | return nil 145 | } 146 | 147 | // GetArgsAndOptionFlag returns args and option flag 148 | // Example: 149 | // ['arg1', 'arg2', '--option-a=value', '--option-b'] => {args: ['arg1', 'arg2'], optionFlag: ['--option-a=value', '--option-b']} 150 | // ['arg1', '--option1', 'arg2', '--option2'] => {args: ['arg1', 'arg2'], optionFlag: ['--option1', '--option2']} 151 | func GetArgsAndOptionFlag(rawArgs []string) ([]string, []string) { 152 | args := []string{} 153 | optionFlag := []string{} 154 | for _, arg := range rawArgs { 155 | if strings.HasPrefix(arg, "--") { 156 | optionFlag = append(optionFlag, arg) 157 | } else { 158 | args = append(args, arg) 159 | } 160 | } 161 | return args, optionFlag 162 | } 163 | 164 | /* 165 | func SetOptByString(ss []string, props *properties.Properties) error { 166 | // hack 167 | var items []string 168 | var kvItems []string 169 | var boolItems []string 170 | for _, item := range ss { 171 | kvs := strings.Split(item, ",") 172 | items = append(items, kvs...) 173 | } 174 | // 1. ss: opt1=val1 opt2=val2, opt3=val3 => opt1=val1\nopt2=val2\n 175 | for _, item := range items { 176 | kv := strings.ReplaceAll(item, " ", "\n") 177 | // item like: key-only,count-only 178 | if strings.Contains(kv, "=") { 179 | kvItems = append(kvItems, kv) 180 | } else { 181 | boolItems = append(boolItems, kv) 182 | } 183 | } 184 | confBuf := strings.Join(kvItems, "\n") 185 | props.Load([]byte(confBuf), properties.UTF8) 186 | 187 | for _, item := range boolItems { 188 | props.Set(item, "true") 189 | } 190 | return nil 191 | } 192 | */ 193 | 194 | func ContextWithProp(ctx context.Context, p *properties.Properties) context.Context { 195 | return context.WithValue(ctx, propertiesKey, p) 196 | } 197 | 198 | func PropFromContext(ctx context.Context) *properties.Properties { 199 | prop := ctx.Value(propertiesKey).(*properties.Properties) 200 | if prop == nil { 201 | return properties.NewProperties() 202 | } 203 | return prop 204 | } 205 | 206 | type ProgressReader struct { 207 | totalSz int64 208 | readSz *atomic.Int32 209 | rdr io.Reader 210 | err atomic.Value 211 | } 212 | 213 | func NewProgressReader(r io.Reader, total int64) *ProgressReader { 214 | return &ProgressReader{ 215 | totalSz: total, 216 | readSz: atomic.NewInt32(0), 217 | err: atomic.Value{}, 218 | rdr: r, 219 | } 220 | } 221 | 222 | func (pr *ProgressReader) Read(b []byte) (int, error) { 223 | n, err := pr.rdr.Read(b) 224 | if err != nil { 225 | pr.err.Store(err) 226 | return n, err 227 | } 228 | pr.readSz.Add(int32(n)) 229 | return n, err 230 | } 231 | 232 | func (pr *ProgressReader) GetProgress() float64 { 233 | return float64(pr.readSz.Load()) / float64(pr.totalSz) 234 | } 235 | 236 | func (pr *ProgressReader) Error() error { 237 | v := pr.err.Load() 238 | if v != nil { 239 | return v.(error) 240 | } 241 | return nil 242 | } 243 | 244 | func OpenFileToProgressReader(fname string) (*os.File, *ProgressReader, error) { 245 | fp, err := os.Open(fname) 246 | if err != nil { 247 | return nil, nil, err 248 | } 249 | fi, err := fp.Stat() 250 | if err != nil { 251 | return nil, nil, err 252 | } 253 | pr := NewProgressReader(fp, fi.Size()) 254 | return fp, pr, nil 255 | } 256 | 257 | // 1 yes, 0 no, -1 return 258 | func AskYesNo(msg string, def string) int { 259 | prompt := promptui.Select{ 260 | Label: msg, 261 | Items: []string{"yes", "no"}, 262 | } 263 | 264 | // TODO 265 | switch def { 266 | case "yes": 267 | case "no": 268 | } 269 | 270 | _, res, err := prompt.Run() 271 | if err != nil { 272 | return -1 273 | } 274 | 275 | switch res { 276 | case "yes": 277 | return 1 278 | case "no": 279 | return 0 280 | } 281 | return -1 282 | } 283 | 284 | func HasForceYes(ctx context.Context) bool { 285 | ic := ExtractIshellContext(ctx) 286 | _, flags := GetArgsAndOptionFlag(ic.Args) 287 | for _, flag := range flags { 288 | if flag == "--yes" { 289 | return true 290 | } 291 | } 292 | return false 293 | } 294 | 295 | func Print(a ...interface{}) { 296 | fmt.Println(a...) 297 | } 298 | 299 | func ExtractIshellContext(ctx context.Context) *ishell.Context { 300 | ic := ctx.Value("ishell").(*ishell.Context) 301 | return ic 302 | } 303 | 304 | // NextKey returns the next key in byte-order. 305 | func NextKey(k []byte) []byte { 306 | // add 0x0 to the end of key 307 | buf := make([]byte, len(k)+1) 308 | copy(buf, k) 309 | return buf 310 | } 311 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY= 9 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 10 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/spanner v1.1.0/go.mod h1:TzTaF9l2ZY2CIetNvVpUu6ZQy8YEOtzB6ICa5EwYjL0= 13 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 14 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 15 | github.com/AlecAivazis/survey/v2 v2.2.16 h1:KJ4fLFqY/NfR5OaFLcf4pThxrlV2YCHGCnCHAKLsJ+U= 16 | github.com/AlecAivazis/survey/v2 v2.2.16/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg= 17 | github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 18 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 19 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 20 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 21 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 22 | github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= 23 | github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= 24 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 25 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 26 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 27 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 28 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= 29 | github.com/VividCortex/mysqlerr v0.0.0-20200629151747-c28746d985dd/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0= 30 | github.com/Xeoncross/go-aesctr-with-hmac v0.0.0-20200623134604-12b17a7ff502/go.mod h1:pmnBM9bxWSiHvC/gSWunUIyDvGn33EkP2CUjxFKtTTM= 31 | github.com/XiaoMi/pegasus-go-client v0.0.0-20181029071519-9400942c5d1c/go.mod h1:KcL6D/4RZ8RAYzQ5gKI0odcdWUmCVlbQTOlWrhP71CY= 32 | github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw= 33 | github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= 34 | github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8= 35 | github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= 36 | github.com/aerospike/aerospike-client-go v1.35.2/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc= 37 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 38 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 39 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 40 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 41 | github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= 42 | github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 43 | github.com/apple/foundationdb/bindings/go v0.0.0-20200112054404-407dc0907f4f/go.mod h1:OMVSB21p9+xQUIqlGizHPZfjK+SHws1ht+ZytVDoz9U= 44 | github.com/appleboy/gin-jwt/v2 v2.6.3/go.mod h1:MfPYA4ogzvOcVkRwAxT7quHOtQmVKDpTwxyUrC2DNw0= 45 | github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= 46 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 47 | github.com/aws/aws-sdk-go v1.35.3 h1:r0puXncSaAfRt7Btml2swUo74Kao+vKhO3VLjwDjK54= 48 | github.com/aws/aws-sdk-go v1.35.3/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= 49 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 50 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 51 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 52 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 53 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 54 | github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= 55 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 56 | github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= 57 | github.com/c4pt0r/kvql v0.0.0-20240509061143-2e732b17190f h1:v83DkTOSXdNJBi3iZIIfrlh5SXxA/MXGM+lBP5EWOX8= 58 | github.com/c4pt0r/kvql v0.0.0-20240509061143-2e732b17190f/go.mod h1:ksAcL0FG13v+9UVAznpGjkIh2yB3qUSkgoNR4aPrl9w= 59 | github.com/c4pt0r/log v0.0.0-20211004143616-aa6380016a47 h1:I7bb8MbleLvoW6scHXngCQaroNa9slYTaYOaQEsv2TQ= 60 | github.com/c4pt0r/log v0.0.0-20211004143616-aa6380016a47/go.mod h1:N78ACK7UQq5KjTLWQPw2A7UuzX712vN9akunb8ydlck= 61 | github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g= 62 | github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets= 63 | github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= 64 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 65 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 66 | github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= 67 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 68 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 69 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 70 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 71 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 72 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 73 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 74 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 75 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 76 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= 77 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 78 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 79 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 80 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 81 | github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 82 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 83 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 84 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= 85 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 86 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 87 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= 88 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 89 | github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= 90 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 91 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 92 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 93 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 94 | github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= 95 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= 96 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= 97 | github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM= 98 | github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= 99 | github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= 100 | github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs= 101 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 102 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 103 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 104 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 105 | github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= 106 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 107 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 108 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= 109 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 110 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 111 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 112 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 113 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 114 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 115 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 116 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 117 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 118 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 119 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 120 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 121 | github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= 122 | github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= 123 | github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= 124 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 125 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 126 | github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= 127 | github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 128 | github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= 129 | github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI= 130 | github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= 131 | github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 132 | github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 133 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 134 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 135 | github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= 136 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 137 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 138 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 139 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 140 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= 141 | github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= 142 | github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= 143 | github.com/go-echarts/go-echarts v1.0.0/go.mod h1:qbmyAb/Rl1f2w7wKba1D4LoNq4U164yO4/wedFbcWyo= 144 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 145 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 146 | github.com/go-ini/ini v1.49.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 147 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 148 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 149 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 150 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 151 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 152 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= 153 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= 154 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 155 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 156 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 157 | github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= 158 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 159 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 160 | github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= 161 | github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= 162 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= 163 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 164 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 165 | github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= 166 | github.com/go-playground/overalls v0.0.0-20180201144345-22ec1a223b7c/go.mod h1:UqxAgEOt89sCiXlrc/ycnx00LVvUO/eS8tMUkWX4R7w= 167 | github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= 168 | github.com/go-redis/redis v6.15.1+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 169 | github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 170 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 171 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 172 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 173 | github.com/goccy/go-graphviz v0.0.5/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= 174 | github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= 175 | github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 176 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 177 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 178 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 179 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 180 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 181 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 182 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 183 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 184 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 185 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 186 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 187 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 188 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= 189 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 190 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 191 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 192 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 193 | github.com/golang/protobuf v0.0.0-20180814211427-aa810b61a9c7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 194 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 195 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 196 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 197 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 198 | github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= 199 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 200 | github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 201 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 202 | github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf h1:gFVkHXmVAhEbxZVDln5V9GKrLaluNoFHDbrZwAWZgws= 203 | github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 204 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 205 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 206 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 207 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 208 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 209 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 210 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 211 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 212 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 213 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 214 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 215 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 216 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 217 | github.com/google/pprof v0.0.0-20200407044318-7d83b28da2e9/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 218 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 219 | github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= 220 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 221 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 222 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 223 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 224 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 225 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 226 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 227 | github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 228 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 229 | github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= 230 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 231 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 232 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 233 | github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= 234 | github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= 235 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= 236 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 237 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 238 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 239 | github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4= 240 | github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= 241 | github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo= 242 | github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= 243 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 244 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 245 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 246 | github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= 247 | github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= 248 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 249 | github.com/hypnoglow/gormzap v0.3.0/go.mod h1:5Wom8B7Jl2oK0Im9hs6KQ+Kl92w4Y7gKCrj66rhyvw0= 250 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 251 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 252 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 253 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 254 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 255 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 256 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 257 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 258 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 259 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 260 | github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= 261 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 262 | github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ= 263 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 264 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 265 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 266 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 267 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 268 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 269 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 270 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 271 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 272 | github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= 273 | github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= 274 | github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= 275 | github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= 276 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 277 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 278 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 279 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 280 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 281 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 282 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 283 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 284 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 285 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 286 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 287 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 288 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 289 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 290 | github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 291 | github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= 292 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 293 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 294 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 295 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 296 | github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= 297 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 298 | github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= 299 | github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 300 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= 301 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 302 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 303 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 304 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 305 | github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= 306 | github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= 307 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 308 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 309 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 310 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 311 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 312 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 313 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 314 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 315 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 316 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 317 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 318 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 319 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 320 | github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 321 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 322 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 323 | github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= 324 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 325 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 326 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 327 | github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= 328 | github.com/mgechev/revive v1.0.2/go.mod h1:rb0dQy1LVAxW9SWy5R3LPUjevzUbUS316U5MFySA2lo= 329 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= 330 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 331 | github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8= 332 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 333 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 334 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 335 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 336 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 337 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 338 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 339 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 340 | github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk= 341 | github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 342 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 343 | github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 344 | github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= 345 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 346 | github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= 347 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 348 | github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= 349 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 350 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 351 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 352 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 353 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 354 | github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= 355 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 356 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 357 | github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= 358 | github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= 359 | github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d h1:U+PMnTlV2tu7RuMK5etusZG3Cf+rpow5hqQByeCzJ2g= 360 | github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d/go.mod h1:lXfE4PvvTW5xOjO6Mba8zDPyw8M93B6AQ7frTGnMlA8= 361 | github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= 362 | github.com/pingcap/check v0.0.0-20191107115940-caf2b9e6ccf4/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= 363 | github.com/pingcap/check v0.0.0-20191216031241-8a5a85928f12/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= 364 | github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712 h1:R8gStypOBmpnHEx1qi//SaqxJVI4inOqljg/Aj5/390= 365 | github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= 366 | github.com/pingcap/errcode v0.3.0/go.mod h1:4b2X8xSqxIroj/IZ9MX/VGZhAwc11wB9wRIzHvz6SeM= 367 | github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 368 | github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 369 | github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 370 | github.com/pingcap/errors v0.11.5-0.20200917111840-a15ef68f753d/go.mod h1:g4vx//d6VakjJ0mk7iLBlKA8LFavV/sAVINT/1PFxeQ= 371 | github.com/pingcap/errors v0.11.5-0.20201029093017-5a7df2af2ac7/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= 372 | github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 h1:LllgC9eGfqzkfubMgjKIDyZYaa609nNWAyNZtpy2B3M= 373 | github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= 374 | github.com/pingcap/failpoint v0.0.0-20191029060244-12f4ac2fd11d/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= 375 | github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce/go.mod h1:w4PEZ5y16LeofeeGwdgZB4ddv9bLyDuIX+ljstgKZyk= 376 | github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd h1:I8IeI8MNiZVKnwuXhcIIzz6pREcOSbq18Q31KYIzFVM= 377 | github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd/go.mod h1:IVF+ijPSMZVtx2oIqxAg7ur6EyixtTYfOHwpfmlhqI4= 378 | github.com/pingcap/go-ycsb v0.0.0-20210727125954-0c816a248fc3 h1:Ckm5X50/eB62oa9QIMISoKtekcF5EGBeucGeh+jza1U= 379 | github.com/pingcap/go-ycsb v0.0.0-20210727125954-0c816a248fc3/go.mod h1:joHzTTQXFaQJBWz0QudbE4g7hVWGxu1F7QG2wveE0bw= 380 | github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 h1:surzm05a8C9dN8dIUmo4Be2+pMRb6f55i+UIYrluu2E= 381 | github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= 382 | github.com/pingcap/kvproto v0.0.0-20191211054548-3c6b38ea5107/go.mod h1:WWLmULLO7l8IOcQG+t+ItJ3fEcrL5FxF0Wu+HrMy26w= 383 | github.com/pingcap/kvproto v0.0.0-20200411081810-b85805c9476c/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= 384 | github.com/pingcap/kvproto v0.0.0-20210219064844-c1844a4775d6/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= 385 | github.com/pingcap/kvproto v0.0.0-20210531063847-f42e582bf0bb h1:6isHwZRl1fc9i1Mggiza2iQcfvVvYAAerFIs5t9msXg= 386 | github.com/pingcap/kvproto v0.0.0-20210531063847-f42e582bf0bb/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= 387 | github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= 388 | github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= 389 | github.com/pingcap/log v0.0.0-20201112100606-8f1e84a3abc8/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= 390 | github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 h1:ERrF0fTuIOnwfGbt71Ji3DKbOEaP189tjym50u8gpC8= 391 | github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= 392 | github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307 h1:v7SipssMu4X1tVQOe3PIVE73keJNHCFXe4Cza5uNDZ8= 393 | github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw= 394 | github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= 395 | github.com/pingcap/sysutil v0.0.0-20210315073920-cc0985d983a3/go.mod h1:tckvA041UWP+NqYzrJ3fMgC/Hw9wnmQ/tUkp/JaHly8= 396 | github.com/pingcap/tidb-dashboard v0.0.0-20210312062513-eef5d6404638/go.mod h1:OzFN8H0EDMMqeulPhPMw2i2JaiZWOKFQ7zdRPhENNgo= 397 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 398 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 399 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 400 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 401 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 402 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 403 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 404 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 405 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 406 | github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= 407 | github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= 408 | github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 409 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 410 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 411 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 412 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 413 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 414 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 415 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 416 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 417 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 418 | github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= 419 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 420 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 421 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 422 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 423 | github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 424 | github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= 425 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 426 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 427 | github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 428 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= 429 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 430 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 431 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 432 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 433 | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 434 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 435 | github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= 436 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 437 | github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 438 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 439 | github.com/shirou/gopsutil v2.19.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 440 | github.com/shirou/gopsutil v3.21.2+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 441 | github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= 442 | github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 443 | github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= 444 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 445 | github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 446 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 447 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 448 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 449 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 450 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 451 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 452 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 453 | github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= 454 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 455 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 456 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 457 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 458 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 459 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 460 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 461 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 462 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 463 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 464 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 465 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 466 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 467 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 468 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 469 | github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 470 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 471 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 472 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 473 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 474 | github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 475 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 476 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 477 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 478 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= 479 | github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= 480 | github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= 481 | github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= 482 | github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio= 483 | github.com/swaggo/swag v1.6.6-0.20200529100950-7c765ddd0476/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= 484 | github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= 485 | github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= 486 | github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= 487 | github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= 488 | github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= 489 | github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= 490 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 491 | github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210706041121-6ca00989ddb4 h1:kokFPtkpQ2ZH910iuxfmKMk8M9zzCLskN8h1LELWN20= 492 | github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210706041121-6ca00989ddb4/go.mod h1:crzTwbliZf57xC5ZSzmQx4iMZCLCGhA364to+E2JAPU= 493 | github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d h1:K0XnvsnT6ofLDuM8Rt3PuFQO4p8bNraeHYstspD316g= 494 | github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d/go.mod h1:Jw9KG11C/23Rr7DW4XWQ7H5xOgGZo6DFL1OKAF4+Igw= 495 | github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= 496 | github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8= 497 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 498 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= 499 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 500 | github.com/twmb/murmur3 v1.1.3 h1:D83U0XYKcHRYwYIpBKf3Pks91Z0Byda/9SJ8B6EMRcA= 501 | github.com/twmb/murmur3 v1.1.3/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= 502 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 503 | github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= 504 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 505 | github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 506 | github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= 507 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 508 | github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= 509 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 510 | github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= 511 | github.com/urfave/negroni v0.3.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= 512 | github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= 513 | github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1/go.mod h1:xlngVLeyQ/Qi05oQxhQ+oTuqa03RjMwMfk/7/TCs+QI= 514 | github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 515 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 516 | github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 517 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= 518 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 519 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 520 | github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= 521 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 522 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 523 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 524 | github.com/yuin/gopher-lua v0.0.0-20181031023651-12c4817b42c5/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= 525 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 526 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 527 | go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= 528 | go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 529 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 530 | go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 531 | go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b h1:3kC4J3eQF6p1UEfQTkC67eEeb3rTk+shQqdX6tFyq9Q= 532 | go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= 533 | go.mongodb.org/mongo-driver v1.0.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= 534 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 535 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 536 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 537 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 538 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 539 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 540 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 541 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 542 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 543 | go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= 544 | go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= 545 | go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= 546 | go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= 547 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 548 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 549 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 550 | go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 551 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 552 | go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= 553 | go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 554 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 555 | go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 556 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 557 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 558 | go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 559 | go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 560 | go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= 561 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 562 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 563 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 564 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 565 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 566 | golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 567 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 568 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 569 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 570 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 571 | golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 572 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 573 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 574 | golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= 575 | golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 576 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 577 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 578 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 579 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 580 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 581 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 582 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 583 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 584 | golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 585 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 586 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 587 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 588 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 589 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 590 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 591 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 592 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 593 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= 594 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 595 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 596 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 597 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 598 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 599 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 600 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 601 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 602 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 603 | golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= 604 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 605 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 606 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 607 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 608 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 609 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 610 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 611 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 612 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 613 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 614 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 615 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 616 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 617 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 618 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 619 | golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 620 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 621 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 622 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 623 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 624 | golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 625 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 626 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 627 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 628 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 629 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 630 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 631 | golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= 632 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 633 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 634 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 635 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 636 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 637 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 638 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 639 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 640 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 641 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 642 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 643 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 644 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 645 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 646 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 647 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 648 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 649 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 650 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 651 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 652 | golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 653 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 654 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 655 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 656 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 657 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 658 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 659 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 660 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 661 | golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 662 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 663 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 664 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 665 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 666 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 667 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 668 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 669 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 670 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 671 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 672 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 673 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 674 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 675 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 676 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 677 | golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 678 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 679 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 680 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 681 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 682 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 683 | golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= 684 | golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= 685 | golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 686 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 687 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 688 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 689 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 690 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 691 | golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= 692 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 693 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 694 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 695 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 696 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 697 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 698 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 699 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 700 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 701 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 702 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 703 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 704 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 705 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 706 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 707 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 708 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 709 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 710 | golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 711 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 712 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 713 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 714 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 715 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 716 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 717 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 718 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 719 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 720 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 721 | golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 722 | golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 723 | golang.org/x/tools v0.0.0-20191107010934-f79515f33823/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 724 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 725 | golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 726 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 727 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 728 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 729 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 730 | golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 731 | golang.org/x/tools v0.0.0-20200527183253-8e7acdbce89d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 732 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 733 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 734 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 735 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 736 | golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= 737 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 738 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 739 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 740 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 741 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 742 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 743 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 744 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 745 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 746 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 747 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 748 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 749 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 750 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 751 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 752 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 753 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 754 | google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 755 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 756 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 757 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 758 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 759 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 760 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 761 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 762 | google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 763 | google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 764 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 765 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8= 766 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 767 | google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 768 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 769 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 770 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 771 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 772 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 773 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 774 | google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= 775 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 776 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 777 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 778 | google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= 779 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 780 | gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM7FIKyzVzGhjB/qfYo= 781 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 782 | gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= 783 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 784 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 785 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 786 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 787 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 788 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 789 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 790 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 791 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 792 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 793 | gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 794 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 795 | gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 796 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 797 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 798 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 799 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 800 | gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk= 801 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 802 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 803 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 804 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 805 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 806 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 807 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 808 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 809 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 810 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 811 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 812 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 813 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 814 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 815 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 816 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 817 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 818 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 819 | honnef.co/go/tools v0.2.0 h1:ws8AfbgTX3oIczLPNPCu5166oBg9ST2vNs0rcht+mDE= 820 | honnef.co/go/tools v0.2.0/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= 821 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 822 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 823 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 824 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 825 | --------------------------------------------------------------------------------