├── .github └── workflows │ ├── codeql-analysis.yml │ └── golangci-lint.yml ├── .travis.yml ├── LICENSE ├── README.md ├── _examples ├── config.yml ├── demo.svg └── example.gif ├── ci ├── build.sh ├── homebrew.rb └── install_upx.sh ├── config ├── config.go ├── exec_config.go ├── exec_connect.go └── ui_config.go ├── docker ├── exec.go └── main.go ├── go.mod ├── go.sum ├── main.go ├── ui ├── commands │ ├── commands.go │ ├── input.go │ ├── menu.go │ ├── search.go │ └── terminal.go ├── helpers │ └── tty_colors.go ├── render_lock │ └── render_lock.go ├── ui.go └── widgets │ ├── tabs_styled.go │ ├── terminal_list.go │ └── textbox.go └── version └── version.go /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '28 8 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | branches: 7 | - '*' 8 | pull_request: 9 | jobs: 10 | golangci: 11 | name: lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: golangci-lint 16 | uses: golangci/golangci-lint-action@v2 17 | with: 18 | version: v1.29 19 | 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.16.x 5 | 6 | git: 7 | depth: 1 8 | 9 | 10 | env: 11 | global: 12 | - NAME=docker-commander 13 | - GO111MODULE=on 14 | 15 | 16 | matrix: 17 | include: 18 | - env: _GOOS=linux _GOARCH=amd64 19 | os: linux 20 | - env: _GOOS=linux _GOARCH=386 21 | os: linux 22 | - env: _GOOS=linux _GOARCH=arm 23 | os: linux 24 | - env: _GOOS=linux _GOARCH=arm64 25 | os: linux 26 | - env: _GOOS=darwin _GOARCH=386 27 | os: osx 28 | - env: _GOOS=darwin _GOARCH=amd64 29 | os: osx 30 | - env: _GOOS=windows _GOARCH=386 31 | os: windows 32 | - env: _GOOS=windows _GOARCH=amd64 33 | os: windows 34 | 35 | 36 | 37 | install: true 38 | 39 | script: 40 | - ./ci/install_upx.sh 41 | - ./ci/build.sh 42 | 43 | 44 | deploy: 45 | provider: releases 46 | api_key: $GITHUB_TOKEN 47 | file_glob: true 48 | file: "./dist/*" 49 | skip_cleanup: true 50 | draft: true 51 | on: 52 | tags: true 53 | 54 | if: tag IS present 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vasyl Plotnikov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Current Release](https://img.shields.io/github/release/daylioti/docker-commander.svg)](https://github.com/daylioti/docker-commander/releases/latest) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/daylioti/docker-commander)](https://goreportcard.com/report/github.com/daylioti/docker-commander) 3 | [![Build Status](https://api.travis-ci.org/daylioti/docker-commander.svg?branch=master)](https://travis-ci.org/daylioti/docker-commander) 4 | 5 | docker-commander is a cross-platform, customizable, execution commands in docker containers dashboard based on termui 6 | 7 | 8 | 9 | ## Installation 10 | 11 | ### Linux 12 | ```bash 13 | sudo wget -qO- https://github.com/daylioti/docker-commander/releases/download/1.2.0/docker-commander_1.2.0_linux_amd64.tgz | sudo tar xvz --overwrite -C /usr/local/bin 14 | sudo chmod +x /usr/local/bin/docker-commander 15 | ``` 16 | ### MacOS 17 | ```bash 18 | brew install wget 19 | sudo wget -qO- https://github.com/daylioti/docker-commander/releases/download/1.2.0/docker-commander_1.2.0_darwin_amd64.tgz | sudo tar xvz -C /usr/local/bin 20 | sudo chmod +x /usr/local/bin/docker-commander 21 | ``` 22 | 23 | `docker-commander` is also avaliable for Arch in the AUR 24 | 25 | ### Options 26 | 27 | Option | Description 28 | --- | --- 29 | -api-host| docker api host. Example: tcp://127.0.0.1:2376 30 | --tty | enable docker exec tty option. 31 | --color | display ANSI colors in command output. 32 | -h | display help dialog 33 | -c | path to yml config file or url to download yml, default - ./config.yml. Also yml config can be used from pipe 34 | -v | output version information and exit 35 | 36 | ### Keybindings 37 | 38 | Key | Action 39 | --- | --- 40 | \ | Execute command 41 | \, \, \, \, H, J, K, L | Menu list controls 42 | \ | Switch between terminal and menu 43 | \ | Remove selected process in tab. 44 | \ | Paste from clipboard to input field. 45 | \, \, \, \, \, \, \, \ | Scroll command output 46 | q, Q | Quit from docker-commander, except opened input popup 47 | \ | Quit from docker-commander from anywhere 48 | 49 | ## Usage 50 | 51 | `docker-commander` requires config file to build menu, default config path - ./config.yml. 52 | You can also use `-c` param to specify path to yml file or url to download. 53 | ### Docker host 54 | By default `docker-commander` tries to find local docker api client or you can specify it with 55 | `-api-host` param 56 | 57 | ### Input field 58 | Input field works like placeholder but with possibility for user to set variable manually. 59 | ```yaml 60 | - name: input field 61 | exec: 62 | connect: 63 | container_name: ubuntu 64 | cmd: echo @input1 65 | input: 66 | input1: Type something. 67 | ``` 68 | On execute this command docker-commander will display popup into which you can enter your values. 69 | 70 | Paste from clipboard - you should install one of those packages xsel, xclip, wl-clipboard 71 | 72 | ### Config file 73 | ```yaml 74 | config: 75 | - name: "menu item name" 76 | config: 77 | - name: "another child menu item" 78 | config: 79 | - name: "item with command" 80 | exec: # it just example. 81 | connect: 82 | container_image: "flatland-site-reactjs-public" 83 | workdir: "/usr/src/site" 84 | cmd: "npm run-script start" 85 | - name: "item" 86 | config: 87 | - name: "item2" 88 | exec: 89 | connect: 90 | container_image: "ubuntu" 91 | workdir: "/var" 92 | cmd: "ls -lah" 93 | - name: "menu item 2" 94 | config: 95 | - name: "another child menu item 2" 96 | exec: 97 | connect: 98 | container_image: "ubuntu" 99 | cmd: "ls -lah /var" 100 | ``` 101 | Configuration of menu can be with any depth, yml support anchors for some optimizations 102 | -------------------------------------------------------------------------------- /_examples/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # docker run -it -d ubuntu 4 | 5 | ubuntu: &ubuntu 6 | connect: 7 | container_image: ubuntu 8 | 9 | config: 10 | - name: group 1 11 | config: 12 | 13 | - name: command 1 14 | exec: 15 | <<: *ubuntu 16 | cmd: ls -lah 17 | 18 | - name: group 2 19 | config: 20 | - name: command 2 21 | exec: 22 | <<: *ubuntu 23 | cmd: ls -lah /var 24 | 25 | - name: group variables 26 | placeholders: 27 | some_variable: some value 28 | config: 29 | - name: variables 1 30 | exec: 31 | <<: *ubuntu 32 | cmd: echo @some_variable 33 | 34 | - name: variables input 35 | exec: 36 | <<: *ubuntu 37 | cmd: echo @input_field @some_variable 38 | input: 39 | input_field: Type something... 40 | 41 | - name: wait 5 sec 42 | exec: 43 | <<: *ubuntu 44 | cmd: sleep 5 45 | 46 | -------------------------------------------------------------------------------- /_examples/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daylioti/docker-commander/4464aa7765890a2f625c32a4584a0da1af8c868c/_examples/example.gif -------------------------------------------------------------------------------- /ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | GOARCH=${_GOARCH} 5 | GOOS=${_GOOS} 6 | env GOOS=${GOOS} GOARCH=${GOARCH} go build -mod=mod -ldflags "-s -w -X github.com/daylioti/docker-commander/version.Version=${TRAVIS_BRANCH}" -o ${NAME} || ERROR=true 7 | if [ "${_GOOS}" == "linux" ]; then 8 | /bin/bash ./ci/install_upx.sh 9 | ./upx --brute docker-commander 10 | fi 11 | mkdir -p dist 12 | 13 | 14 | FILE=${NAME}_${TRAVIS_BRANCH}_${GOOS}_${GOARCH} 15 | 16 | tar -czf dist/${FILE}.tgz ${NAME} || ERROR=true 17 | 18 | if [ "$ERROR" == "true" ]; then 19 | exit 1 20 | fi 21 | 22 | -------------------------------------------------------------------------------- /ci/homebrew.rb: -------------------------------------------------------------------------------- 1 | class DockerCommander < Formula 2 | desc "Execute commands in docker containers." 3 | homepage "https://github.com/daylioti/docker-commander" 4 | url "https://github.com/daylioti/docker-commander.git", 5 | :tag => "1.1.4", 6 | :revision => "76a727fc77a4236a1c980f6b637ffa7288816c8b" 7 | 8 | version "1.1.4" 9 | 10 | depends_on "go" => :build 11 | 12 | def install 13 | ENV["GOPATH"] = buildpath 14 | ENV["GO111MODULE"] = "on" 15 | src = buildpath/"src/github.com/daylioti/docker-commander" 16 | src.install buildpath.children 17 | src.cd do 18 | system "make", "build" 19 | bin.install "docker-commander" 20 | prefix.install_metafiles 21 | end 22 | end 23 | 24 | test do 25 | system "#{bin}/docker-commander", "-v" 26 | end 27 | 28 | end -------------------------------------------------------------------------------- /ci/install_upx.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | cmd="curl -sL -o" 4 | 5 | 6 | if [ "${_GOOS}" == "linux" ] || [ "${_GOOS}" == "darwin" ]; then 7 | cmd="${cmd} upx.tar.xz https://github.com/upx/upx/releases/download/v3.96/upx-3.96-" 8 | if [ "${_GOARCH}" == "386" ]; then 9 | cmd="${cmd}i${_GOARCH}_linux.tar.xz" 10 | else 11 | cmd="${cmd}${_GOARCH}_linux.tar.xz" 12 | fi 13 | $cmd 14 | unxz upx.tar.xz 15 | tar -xvf upx.tar 16 | cp upx-3.96*/upx . 17 | fi 18 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "gopkg.in/yaml.v2" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // Config main config structure for commands lists. 18 | type Config struct { 19 | Name string `yaml:"name"` // Display name 20 | Selected bool // Selected config or not 21 | Status bool // Display or not 22 | Config []Config `yaml:"config"` // Sub-configs (recursive) 23 | Exec ExecConfig `yaml:"exec"` // Docker exec config. 24 | Placeholders map[string]string `yaml:"placeholders"` 25 | } 26 | 27 | // CnfInit unmarshal yml by structures. 28 | func CnfInit(path string, configs ...interface{}) { 29 | var err error 30 | var data []byte 31 | var fi os.FileInfo 32 | fi, err = os.Stdin.Stat() 33 | if err != nil || fi.Mode()&os.ModeNamedPipe == 0 { 34 | if data, err = ioutil.ReadFile(path); err != nil { 35 | _, parseErr := url.Parse(path) 36 | if parseErr == nil { 37 | // Get from url 38 | client := &http.Client{Timeout: time.Second} 39 | if r, responseErr := client.Get(path); responseErr == nil { 40 | data, err = ioutil.ReadAll(r.Body) 41 | if err != nil { 42 | fmt.Printf("Can't open config from %v", path) 43 | os.Exit(0) 44 | } 45 | } else { 46 | fmt.Printf("Can't open config from %v", path) 47 | os.Exit(0) 48 | } 49 | } else { 50 | fmt.Printf("Can't open config from %v", path) 51 | os.Exit(0) 52 | } 53 | } 54 | 55 | } else { 56 | // Here we have data from pipe. 57 | var n int 58 | reader := bufio.NewReader(os.Stdin) 59 | buf := make([]byte, 0, 256) 60 | for { 61 | n, err = reader.Read(buf[:cap(buf)]) 62 | data = append(data, buf[:n]...) 63 | if n == 0 { 64 | if err == nil { 65 | continue 66 | } 67 | if err == io.EOF { 68 | break 69 | } 70 | log.Fatal(err) 71 | } 72 | if err != nil && err != io.EOF { 73 | log.Fatal(err) 74 | } 75 | } 76 | 77 | } 78 | for _, cfg := range configs { 79 | data = []byte(os.ExpandEnv(string(data))) 80 | if err = yaml.Unmarshal(data, cfg); err != nil { 81 | panic(err) 82 | } 83 | } 84 | } 85 | 86 | // Init set default selected items, replace placeholders. 87 | func (cfg *Config) Init() { 88 | // Set default config data. 89 | cfg.Status = true 90 | cfg.Config[0].Selected = true 91 | for i := 0; i < len(cfg.Config); i++ { 92 | cfg.Config[i].Status = true 93 | } 94 | cfg.Config[0].Status = true 95 | if len(cfg.Config[0].Config) > 0 { 96 | for i := 0; i < len(cfg.Config[0].Config); i++ { 97 | cfg.Config[0].Config[i].Status = true 98 | } 99 | } 100 | } 101 | 102 | // ReplacePlaceholders replace placeholders for config. 103 | func (cfg *Config) ReplacePlaceholders(placeholders map[string]string, c *Config) { 104 | for k, v := range placeholders { 105 | cfg.ReplacePlaceholder(k, v, c) 106 | } 107 | } 108 | 109 | // GetPlaceholders all placeholders for selected config. 110 | func (cfg *Config) GetPlaceholders(path []int, placeholders map[string]string, c *Config) map[string]string { 111 | if len(path) < 1 { 112 | return cfg.mergePlaceholders(c, placeholders) 113 | } 114 | return cfg.GetPlaceholders(path[1:], cfg.mergePlaceholders(c, placeholders), &c.Config[path[0]]) 115 | } 116 | 117 | // mergePlaceholders 118 | func (cfg *Config) mergePlaceholders(c *Config, placeholders map[string]string) map[string]string { 119 | for key, value := range c.Placeholders { 120 | for k, v := range placeholders { 121 | cfg.Replace(&value, k, v) 122 | } 123 | placeholders[key] = value 124 | } 125 | return placeholders 126 | } 127 | 128 | // Replace replace placeholder strings. 129 | func (cfg *Config) Replace(str *string, placeholder string, value string) { 130 | *str = strings.Replace(*str, "@"+placeholder, value, -1) 131 | *str = strings.Replace(*str, "["+placeholder+"]", value, -1) 132 | } 133 | 134 | // ReplacePlaceholder replace placeholders in all available fields. 135 | func (cfg *Config) ReplacePlaceholder(placeholder string, value string, c *Config) { 136 | cfg.Replace(&c.Exec.WorkingDir, placeholder, value) 137 | cfg.Replace(&c.Exec.Connect.FromImage, placeholder, value) 138 | cfg.Replace(&c.Exec.Connect.ContainerID, placeholder, value) 139 | cfg.Replace(&c.Exec.Cmd, placeholder, value) 140 | for i := 0; i < len(c.Exec.Env); i++ { 141 | cfg.Replace(&c.Exec.Env[i], placeholder, value) 142 | } 143 | replacedPlaceholders := make(map[string]string) 144 | for k, v := range c.Placeholders { 145 | cfg.Replace(&k, placeholder, value) 146 | cfg.Replace(&v, placeholder, value) 147 | replacedPlaceholders[k] = v 148 | } 149 | c.Placeholders = replacedPlaceholders 150 | for k, v := range c.Exec.Input { 151 | c.Exec.Input[k].Key = strings.Replace(fmt.Sprintf("%v", v.Key), "@"+placeholder, value, -1) 152 | c.Exec.Input[k].Key = strings.Replace(fmt.Sprintf("%v", v.Key), "["+placeholder+"]", value, -1) 153 | c.Exec.Input[k].Value = strings.Replace(fmt.Sprintf("%v", v.Value), "@"+placeholder, value, -1) 154 | c.Exec.Input[k].Value = strings.Replace(fmt.Sprintf("%v", v.Value), "["+placeholder+"]", value, -1) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /config/exec_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "gopkg.in/yaml.v2" 4 | 5 | // ExecConfig docker exec configs (what to execute). 6 | type ExecConfig struct { 7 | Connect ExecConnect `yaml:"connect"` 8 | Env []string `yaml:"env"` // Environment variables. 9 | WorkingDir string `yaml:"workdir"` // Working directory. 10 | Cmd string `yaml:"cmd"` // Execution commands and args 11 | Input yaml.MapSlice `yaml:"input"` 12 | } 13 | -------------------------------------------------------------------------------- /config/exec_connect.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // ExecConnect docker execute configs (where execute the command). 4 | type ExecConnect struct { 5 | FromImage string `yaml:"container_image"` // The name of the image from which the container is made. 6 | ContainerName string `yaml:"container_name"` // Container Name 7 | ContainerID string `yaml:"container_id"` // Container id 8 | } 9 | -------------------------------------------------------------------------------- /config/ui_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // UIConfig config struct for UI. 8 | type UIConfig struct { 9 | UI struct { 10 | Commands map[string]string `yaml:"commands"` 11 | } 12 | } 13 | 14 | // GetCommandsHeight commands lists height. 15 | func (uc *UIConfig) GetCommandsHeight() int { 16 | var height int 17 | if h, exist := uc.UI.Commands["height"]; !exist { 18 | height = 5 19 | } else { 20 | height, _ = strconv.Atoi(h) 21 | } 22 | return height + 2 23 | } 24 | -------------------------------------------------------------------------------- /docker/exec.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "github.com/daylioti/docker-commander/config" 5 | "github.com/daylioti/docker-commander/ui/helpers" 6 | commanderWidgets "github.com/daylioti/docker-commander/ui/widgets" 7 | "github.com/docker/docker/api/types" 8 | "github.com/gizak/termui/v3" 9 | "net" 10 | "strings" 11 | ) 12 | 13 | const bufferReadSize = 1024 14 | 15 | // Exec - main struct for execute commands inside docker containers. 16 | type Exec struct { 17 | Tty bool 18 | Color bool 19 | dockerClient *Docker 20 | Terminals []*TerminalRun 21 | updateTerminal fn 22 | } 23 | 24 | // TerminalRun - struct for running commands. 25 | type TerminalRun struct { 26 | TabItem *commanderWidgets.TabItem // tab item text and styles 27 | List *commanderWidgets.TerminalList // list widget with command output 28 | Active bool // opened tab or not 29 | Command string 30 | Running bool 31 | ContainerID string 32 | ID string // based on selected item path in menu 33 | ExecID string // Docker Exec instance ID 34 | Name string 35 | WorkDir string 36 | } 37 | 38 | // Function for re-render after receiving some updates from execution command. 39 | type fn func(*TerminalRun, bool) 40 | 41 | // Init initialize exec object. 42 | func (e *Exec) Init(dockerClient *Docker) { 43 | e.dockerClient = dockerClient 44 | } 45 | 46 | // SetTerminalUpdateFn set function for updating terminal list from command output. 47 | func (e *Exec) SetTerminalUpdateFn(ui fn) { 48 | e.updateTerminal = ui 49 | } 50 | 51 | // CommandRun - execute process in docker container. 52 | func (e *Exec) CommandRun(term *TerminalRun) { 53 | var ExecConfig types.ExecConfig 54 | var Response types.IDResponse 55 | var err error 56 | var HResponse types.HijackedResponse 57 | var c net.Conn 58 | if term.ContainerID == "" { 59 | e.execReadFinish(term, "Can't find running container") 60 | } 61 | ExecConfig.Cmd = strings.Split(term.Command, " ") 62 | ExecConfig.AttachStdout = true 63 | if term.WorkDir != "" { 64 | ExecConfig.WorkingDir = term.WorkDir 65 | } 66 | ExecConfig.Tty = e.Tty 67 | Response, err = e.dockerClient.client.ContainerExecCreate(e.dockerClient.context, term.ContainerID, ExecConfig) 68 | width, height := termui.TerminalDimensions() 69 | resize := types.ResizeOptions{ 70 | Width: uint(width), 71 | Height: uint(height), 72 | } 73 | _ = e.dockerClient.client.ContainerResize(e.dockerClient.context, term.ContainerID, resize) 74 | 75 | if err != nil { 76 | e.execReadFinish(term, err.Error()) 77 | return 78 | } 79 | HResponse, err = e.dockerClient.client.ContainerExecAttach(e.dockerClient.context, Response.ID, 80 | types.ExecStartCheck{Tty: true}) 81 | if err != nil { 82 | e.execReadFinish(term, err.Error()) 83 | return 84 | } 85 | 86 | c = HResponse.Conn 87 | e.execReadBuffer(term, []byte(commanderWidgets.StyleText("Executed:", "fg:green")), false) 88 | e.execReadBuffer(term, []byte(commanderWidgets.StyleText("Dir -> "+term.WorkDir, "fg:green")), false) 89 | e.execReadBuffer(term, []byte(commanderWidgets.StyleText("Cmd -> "+term.Command, "fg:green")), false) 90 | e.execReadBuffer(term, []byte(commanderWidgets.StyleText("ContainerId -> "+term.ContainerID, "fg:green")), false) 91 | 92 | go func() { 93 | for { 94 | b := make([]byte, bufferReadSize) 95 | if _, err = c.Read(b); err != nil { 96 | _ = c.Close() 97 | e.execReadFinish(term, err.Error()) 98 | return 99 | } 100 | e.execReadBuffer(term, b, e.Color) 101 | e.updateTerminal(term, false) 102 | } 103 | }() 104 | _ = e.dockerClient.client.ContainerExecStart(e.dockerClient.context, Response.ID, types.ExecStartCheck{Tty: true}) 105 | term.ExecID = Response.ID 106 | } 107 | 108 | // execReadBuffer - paste result rom output buffer to display list. 109 | func (e *Exec) execReadBuffer(term *TerminalRun, buff []byte, color bool) { 110 | if len(buff) > 0 { 111 | if color { 112 | var rows []string 113 | for _, line := range strings.Split(string(buff), "\n") { 114 | rows = strings.Split(line, "\b\b") 115 | if len(rows) >= 1 && strings.Contains(line, "\b\b") { 116 | // Remove prev line 117 | term.List.Rows = term.List.Rows[:len(term.List.Rows)-1] 118 | } 119 | line = string(helpers.TTYColorsParse([]byte(rows[len(rows)-1]))) 120 | term.List.Rows = append(term.List.Rows, line) 121 | } 122 | } else { 123 | term.List.Rows = append(term.List.Rows, strings.Split(string(buff), "\n")...) 124 | } 125 | } 126 | } 127 | 128 | // execReadFinish - paste finish info to display list. 129 | func (e *Exec) execReadFinish(term *TerminalRun, buf string) { 130 | e.execReadBuffer(term, []byte(commanderWidgets.StyleText("Finished -> "+term.Command, "fg:green")), false) 131 | if buf != "EOF" { 132 | e.execReadBuffer(term, []byte(buf), e.Color) 133 | } 134 | e.updateTerminal(term, true) 135 | } 136 | 137 | // GetActiveTerminalIndex get array index of active terminal. 138 | func (e *Exec) GetActiveTerminalIndex() int { 139 | for i, term := range e.Terminals { 140 | if term.Active { 141 | return i 142 | } 143 | } 144 | return -1 145 | } 146 | 147 | // GetContainerID - get container id by ContainerName or FromImage or ContainerID params. 148 | func (e *Exec) GetContainerID(config config.Config) string { 149 | containers, err := e.dockerClient.client.ContainerList(e.dockerClient.context, types.ContainerListOptions{}) 150 | if err != nil { 151 | panic(err) 152 | } 153 | for _, c := range containers { 154 | if config.Exec.Connect.FromImage != "" && strings.Contains(c.Image, config.Exec.Connect.FromImage) { 155 | return c.ID 156 | } else if config.Exec.Connect.ContainerID != "" && c.ID == config.Exec.Connect.ContainerID { 157 | return c.ID 158 | } else if config.Exec.Connect.ContainerName != "" { 159 | for _, name := range c.Names { 160 | if strings.Contains(name, config.Exec.Connect.ContainerName) { 161 | return c.ID 162 | } 163 | } 164 | } 165 | } 166 | return "" 167 | } 168 | -------------------------------------------------------------------------------- /docker/main.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "context" 5 | "github.com/docker/docker/api" 6 | "github.com/docker/docker/api/types" 7 | "github.com/docker/docker/client" 8 | "strconv" 9 | ) 10 | 11 | // Docker main structure. 12 | type Docker struct { 13 | client *client.Client 14 | context context.Context 15 | Exec *Exec 16 | Ping types.Ping 17 | } 18 | 19 | // Init initialize connection to docker. 20 | func (d *Docker) Init(ops ...client.Opt) { 21 | var err error 22 | d.context = context.Background() 23 | defer d.context.Done() 24 | if d.client, err = client.NewClientWithOpts(ops...); err != nil { 25 | panic(err) 26 | } 27 | _, err = d.client.Ping(d.context) 28 | if err != nil { 29 | panic(err) 30 | } 31 | min, _ := strconv.ParseFloat(api.MinVersion, 32) 32 | clientAPIVersion, _ := strconv.ParseFloat(d.Ping.APIVersion, 32) 33 | max, _ := strconv.ParseFloat(api.DefaultVersion, 32) 34 | if min <= clientAPIVersion && clientAPIVersion <= max { 35 | ops = append(ops, client.WithVersion(d.Ping.APIVersion)) 36 | } 37 | if d.client, err = client.NewClientWithOpts(ops...); err != nil { 38 | panic(err) 39 | } 40 | d.Exec = &Exec{} 41 | d.Exec.Init(d) 42 | } 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/daylioti/docker-commander 2 | 3 | require ( 4 | github.com/atotto/clipboard v0.1.4 5 | github.com/containerd/containerd v1.5.7 // indirect 6 | github.com/docker/docker v20.10.5+incompatible 7 | github.com/docker/go-connections v0.4.0 // indirect 8 | github.com/gizak/termui/v3 v3.1.0 9 | github.com/golang/protobuf v1.5.1 // indirect 10 | github.com/gorilla/mux v1.8.0 // indirect 11 | github.com/mattn/go-runewidth v0.0.10 12 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 13 | github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect 14 | github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect 15 | github.com/nsf/termbox-go v1.1.0 // indirect 16 | github.com/rivo/uniseg v0.2.0 // indirect 17 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect 18 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 // indirect 19 | google.golang.org/grpc v1.36.0 // indirect 20 | gopkg.in/yaml.v2 v2.4.0 21 | ) 22 | 23 | go 1.16 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= 2 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 5 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 6 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 14 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 15 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 16 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 17 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 18 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 19 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 20 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 21 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 22 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 23 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 24 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 25 | github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 26 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= 27 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 28 | github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 29 | github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 30 | github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= 31 | github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= 32 | github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= 33 | github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= 34 | github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 35 | github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 36 | github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 37 | github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 38 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 39 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 40 | github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= 41 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 42 | github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= 43 | github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= 44 | github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= 45 | github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 46 | github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 47 | github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w= 48 | github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 49 | github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= 50 | github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= 51 | github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= 52 | github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= 53 | github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= 54 | github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= 55 | github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= 56 | github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= 57 | github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= 58 | github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= 59 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 60 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 61 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 62 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 63 | github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= 64 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 65 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 66 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 67 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 68 | github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= 69 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 70 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= 71 | github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 72 | github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 73 | github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= 74 | github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 75 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 76 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 77 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 78 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 79 | github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= 80 | github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 81 | github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 82 | github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 83 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 84 | github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= 85 | github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= 86 | github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= 87 | github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= 88 | github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= 89 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 90 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 91 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 92 | github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= 93 | github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= 94 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 95 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 96 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 97 | github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= 98 | github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= 99 | github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= 100 | github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 101 | github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 102 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 103 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 104 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 105 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 106 | github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= 107 | github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= 108 | github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= 109 | github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= 110 | github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= 111 | github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= 112 | github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= 113 | github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= 114 | github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= 115 | github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= 116 | github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= 117 | github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= 118 | github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= 119 | github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= 120 | github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= 121 | github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= 122 | github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= 123 | github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= 124 | github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= 125 | github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 126 | github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 127 | github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 128 | github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 129 | github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 130 | github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 131 | github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 132 | github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 133 | github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= 134 | github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= 135 | github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= 136 | github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= 137 | github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= 138 | github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= 139 | github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= 140 | github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= 141 | github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= 142 | github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= 143 | github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= 144 | github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= 145 | github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= 146 | github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= 147 | github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= 148 | github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= 149 | github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= 150 | github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= 151 | github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= 152 | github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= 153 | github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= 154 | github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= 155 | github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= 156 | github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= 157 | github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= 158 | github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= 159 | github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= 160 | github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= 161 | github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= 162 | github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= 163 | github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= 164 | github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= 165 | github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= 166 | github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= 167 | github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= 168 | github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= 169 | github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= 170 | github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= 171 | github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= 172 | github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= 173 | github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= 174 | github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= 175 | github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= 176 | github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= 177 | github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= 178 | github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= 179 | github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= 180 | github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= 181 | github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= 182 | github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= 183 | github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= 184 | github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= 185 | github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= 186 | github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= 187 | github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= 188 | github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= 189 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 190 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 191 | github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= 192 | github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= 193 | github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= 194 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 195 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 196 | github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 197 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 198 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 199 | github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= 200 | github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= 201 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 202 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 203 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 204 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 205 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 206 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 207 | github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 208 | github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 209 | github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= 210 | github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= 211 | github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= 212 | github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= 213 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 214 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 215 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 216 | github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= 217 | github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 218 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 219 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 220 | github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= 221 | github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= 222 | github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 223 | github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= 224 | github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 225 | github.com/docker/docker v20.10.5+incompatible h1:o5WL5onN4awYGwrW7+oTn5x9AF2prw7V0Ox8ZEkoCdg= 226 | github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 227 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 228 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 229 | github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= 230 | github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= 231 | github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= 232 | github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 233 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 234 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 235 | github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= 236 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 237 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 238 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 239 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 240 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 241 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 242 | github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 243 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 244 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 245 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 246 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 247 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 248 | github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 249 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 250 | github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= 251 | github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 252 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 253 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 254 | github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= 255 | github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= 256 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 257 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 258 | github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= 259 | github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= 260 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 261 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 262 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 263 | github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 264 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 265 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 266 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 267 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 268 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 269 | github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 270 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= 271 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 272 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= 273 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 274 | github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= 275 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 276 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 277 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 278 | github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= 279 | github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= 280 | github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= 281 | github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 282 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 283 | github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= 284 | github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= 285 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 286 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 287 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 288 | github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 289 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 290 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 291 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 292 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 293 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 294 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 295 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 296 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 297 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 298 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 299 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 300 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 301 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 302 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 303 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 304 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 305 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 306 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 307 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 308 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 309 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 310 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 311 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 312 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 313 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 314 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 315 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 316 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 317 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 318 | github.com/golang/protobuf v1.5.1 h1:jAbXjIeW2ZSW2AwFxlGTDoc2CjI2XujLkV3ArsZFCvc= 319 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 320 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 321 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 322 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 323 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 324 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 325 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 326 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 327 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 328 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 329 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 330 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 331 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 332 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 333 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 334 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 335 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 336 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 337 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 338 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 339 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 340 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 341 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 342 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 343 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 344 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 345 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 346 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 347 | github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= 348 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 349 | github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= 350 | github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 351 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 352 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 353 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 354 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 355 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 356 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 357 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 358 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 359 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 360 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 361 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 362 | github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 363 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 364 | github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= 365 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 366 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 367 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 368 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 369 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 370 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 371 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 372 | github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 373 | github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 374 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 375 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 376 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 377 | github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= 378 | github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 379 | github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 380 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 381 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 382 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 383 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 384 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 385 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 386 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 387 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 388 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 389 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 390 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 391 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 392 | github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 393 | github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 394 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 395 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 396 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 397 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 398 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 399 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 400 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 401 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 402 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 403 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 404 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 405 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 406 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 407 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 408 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 409 | github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 410 | github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= 411 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 412 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 413 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 414 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 415 | github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= 416 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 417 | github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= 418 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 419 | github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 420 | github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 421 | github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= 422 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 423 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 424 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= 425 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= 426 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 427 | github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= 428 | github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= 429 | github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= 430 | github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= 431 | github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= 432 | github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= 433 | github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= 434 | github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= 435 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 436 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 437 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 438 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 439 | github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= 440 | github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 441 | github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= 442 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 443 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 444 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 445 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 446 | github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= 447 | github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= 448 | github.com/nsf/termbox-go v1.1.0 h1:R+GIXVMaDxDQ2VHem5vO5h0mI8ZxLECTUNw1ZzXODzI= 449 | github.com/nsf/termbox-go v1.1.0/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= 450 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 451 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 452 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 453 | github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 454 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 455 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 456 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 457 | github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 458 | github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 459 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 460 | github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 461 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 462 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 463 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 464 | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= 465 | github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 466 | github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 467 | github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 468 | github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 469 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 470 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 471 | github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 472 | github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= 473 | github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 474 | github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 475 | github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 476 | github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 477 | github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 478 | github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= 479 | github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= 480 | github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 481 | github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 482 | github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 483 | github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 484 | github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 485 | github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 486 | github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= 487 | github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= 488 | github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= 489 | github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= 490 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 491 | github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= 492 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 493 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 494 | github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 495 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 496 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 497 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 498 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 499 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 500 | github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= 501 | github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 502 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 503 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 504 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 505 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= 506 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 507 | github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 508 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 509 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 510 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 511 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 512 | github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 513 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 514 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 515 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 516 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 517 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 518 | github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 519 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 520 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 521 | github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 522 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 523 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 524 | github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 525 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 526 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 527 | github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 528 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 529 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 530 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 531 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 532 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 533 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 534 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 535 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 536 | github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= 537 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 538 | github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= 539 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 540 | github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= 541 | github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= 542 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 543 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 544 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 545 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 546 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 547 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 548 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 549 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 550 | github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 551 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 552 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 553 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 554 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 555 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 556 | github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 557 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 558 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 559 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 560 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 561 | github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 562 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 563 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 564 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 565 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 566 | github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= 567 | github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 568 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 569 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 570 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 571 | github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 572 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 573 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 574 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 575 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 576 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 577 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 578 | github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 579 | github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 580 | github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 581 | github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= 582 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 583 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 584 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 585 | github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 586 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 587 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 588 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 589 | github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= 590 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 591 | github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= 592 | github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= 593 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 594 | github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= 595 | github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 596 | github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= 597 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 598 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 599 | github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= 600 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 601 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 602 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 603 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 604 | github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= 605 | github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= 606 | github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= 607 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 608 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 609 | go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 610 | go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= 611 | go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= 612 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 613 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 614 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 615 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 616 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 617 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 618 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 619 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 620 | golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 621 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 622 | golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 623 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 624 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 625 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 626 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 627 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 628 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 629 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 630 | golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 631 | golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 632 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 633 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 634 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 635 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 636 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 637 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 638 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 639 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 640 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 641 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 642 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 643 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 644 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 645 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 646 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 647 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 648 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 649 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 650 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 651 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 652 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 653 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 654 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 655 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 656 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 657 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 658 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 659 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 660 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 661 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 662 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 663 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 664 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 665 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 666 | golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 667 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 668 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 669 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 670 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 671 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 672 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 673 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 674 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 675 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 676 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 677 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 678 | golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 679 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 680 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 681 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 682 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 683 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 684 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 685 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 686 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 687 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 688 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 689 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 690 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 691 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 692 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 693 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 694 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 695 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 696 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 697 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E= 698 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 699 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 700 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 701 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 702 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 703 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 704 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 705 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 706 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 707 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 708 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 709 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 710 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 711 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 712 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 713 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 714 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 715 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 716 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 717 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 718 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 719 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 720 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 721 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 722 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 723 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 724 | golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 725 | golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 726 | golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 727 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 728 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 729 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 730 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 731 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 732 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 733 | golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 734 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 735 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 736 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 737 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 738 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 739 | golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 740 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 741 | golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 742 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 743 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 744 | golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 745 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 746 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 747 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 748 | golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 749 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 750 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 751 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 752 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 753 | golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 754 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 755 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 756 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 757 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 758 | golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 759 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 760 | golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 761 | golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 762 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 763 | golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 764 | golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 765 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 766 | golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 767 | golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 768 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 769 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 770 | golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 771 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 772 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 773 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 774 | golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs= 775 | golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 776 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 777 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 778 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 779 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 780 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 781 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 782 | golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= 783 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 784 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 785 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 786 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 787 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 788 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= 789 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 790 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 791 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 792 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 793 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 794 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 795 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 796 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 797 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 798 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 799 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 800 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 801 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 802 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 803 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 804 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 805 | golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 806 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 807 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 808 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 809 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 810 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 811 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 812 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 813 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 814 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 815 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 816 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 817 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 818 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 819 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 820 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 821 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 822 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 823 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 824 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 825 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 826 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 827 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 828 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 829 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 830 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 831 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 832 | google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 833 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 834 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 835 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 836 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 837 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 838 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 839 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 840 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 841 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 842 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 843 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 844 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 845 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 846 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 847 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 848 | google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= 849 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 850 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 851 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 852 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 853 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 854 | google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 855 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 856 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 857 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 858 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 859 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 860 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 861 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 862 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 863 | google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 864 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 865 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 866 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 867 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 868 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 869 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 870 | google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 871 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 h1:4Xw2NwItrJOFR5s6PnK98PI6Bgw1LhMP1j/rO5WP0S4= 872 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 873 | google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 874 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 875 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 876 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 877 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 878 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 879 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 880 | google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= 881 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 882 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 883 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 884 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 885 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 886 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 887 | google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= 888 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 889 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 890 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 891 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 892 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 893 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 894 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 895 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 896 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 897 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 898 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 899 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 900 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 901 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 902 | gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 903 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 904 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 905 | gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 906 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 907 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 908 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 909 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 910 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 911 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 912 | gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 913 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 914 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 915 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 916 | gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 917 | gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 918 | gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 919 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 920 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 921 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 922 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 923 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 924 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 925 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 926 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 927 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 928 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 929 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 930 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 931 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 932 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 933 | gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= 934 | gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= 935 | gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= 936 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 937 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 938 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 939 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 940 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 941 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 942 | k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= 943 | k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= 944 | k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= 945 | k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= 946 | k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= 947 | k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= 948 | k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= 949 | k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= 950 | k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= 951 | k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= 952 | k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= 953 | k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= 954 | k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= 955 | k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= 956 | k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= 957 | k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= 958 | k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= 959 | k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= 960 | k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= 961 | k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 962 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 963 | k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= 964 | k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= 965 | k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= 966 | k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 967 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 968 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 969 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 970 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= 971 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= 972 | sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 973 | sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 974 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 975 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 976 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/daylioti/docker-commander/config" 9 | "github.com/daylioti/docker-commander/docker" 10 | "github.com/daylioti/docker-commander/ui" 11 | "github.com/daylioti/docker-commander/version" 12 | "github.com/docker/docker/client" 13 | "github.com/gizak/termui/v3" 14 | ) 15 | 16 | func main() { 17 | 18 | var ( 19 | configFileFlag = flag.String("c", "", "system path to yml config file or url, default - ./config.yml") 20 | clientWithHost = flag.String("api-host", "", "docker api host. Example: tcp://127.0.0.1:2376") 21 | tty = flag.Bool("tty", false, "enable docker exec tty option") 22 | color = flag.Bool("color", false, "display ANSI colors in command output.") 23 | versionFlag = flag.Bool("v", false, "output version information and exit") 24 | helpFlag = flag.Bool("h", false, "display this help dialog") 25 | ) 26 | var ops []client.Opt 27 | flag.Parse() 28 | 29 | if *versionFlag { 30 | fmt.Println(version.Version) 31 | os.Exit(0) 32 | } 33 | 34 | if *helpFlag { 35 | printHelp() 36 | os.Exit(0) 37 | } 38 | 39 | if *configFileFlag == "" { 40 | *configFileFlag = "./config.yml" 41 | } 42 | dockerClient := &docker.Docker{} 43 | Cnf := &config.Config{} 44 | CnfUi := &config.UIConfig{} 45 | config.CnfInit(*configFileFlag, Cnf, CnfUi) 46 | Cnf.Init() 47 | if *clientWithHost != "" { 48 | ops = append(ops, client.WithHost(*clientWithHost)) 49 | } 50 | dockerClient.Init(ops...) 51 | dockerClient.Exec.Tty = *tty 52 | dockerClient.Exec.Color = *color 53 | 54 | err := termui.Init() 55 | if err != nil { 56 | panic(err) 57 | } 58 | defer termui.Close() 59 | 60 | UI := new(ui.UI) 61 | UI.Init(Cnf, dockerClient, CnfUi) 62 | 63 | uiEvents := termui.PollEvents() 64 | searchBox := false 65 | for e := range uiEvents { 66 | switch true { 67 | case e.ID == "" || e.ID == "" || e.ID == "" || e.ID == "": 68 | continue 69 | case e.ID == "" || searchBox && e.ID == "": 70 | searchBox = !searchBox 71 | if !searchBox { 72 | UI.Commands.Search.Reset() 73 | } else { 74 | UI.Commands.Search.Search() 75 | } 76 | UI.Render() 77 | case searchBox: 78 | UI.Commands.Search.Handle(e.ID) 79 | if len(UI.Commands.Search.Input) == 0 { 80 | searchBox = false 81 | } 82 | case e.ID == "q" || e.ID == "" || e.ID == "Q": 83 | if len(UI.Commands.Input.Fields) > 0 && e.ID != "" { 84 | UI.Handle(e.ID) 85 | } else { 86 | return 87 | } 88 | 89 | case e.ID == "": 90 | UI.Init(Cnf, dockerClient, CnfUi) 91 | default: 92 | UI.Handle(e.ID) 93 | } 94 | } 95 | } 96 | 97 | var help = `docker-commander - execute commands in docker containers 98 | usage: docker-commander [options] 99 | options: 100 | ` 101 | 102 | func printHelp() { 103 | fmt.Println(help) 104 | flag.PrintDefaults() 105 | } 106 | -------------------------------------------------------------------------------- /ui/commands/commands.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/daylioti/docker-commander/config" 5 | "github.com/daylioti/docker-commander/docker" 6 | "github.com/daylioti/docker-commander/ui/render_lock" 7 | ) 8 | 9 | const ( 10 | KeySelectedCommands = iota 11 | KeySelectedTerminal 12 | ) 13 | 14 | type Commands struct { 15 | ConfigUi *config.UIConfig 16 | TermWidth int 17 | TermHeight int 18 | 19 | DockerClient *docker.Docker 20 | Cnf *config.Config 21 | RenderAll func() 22 | SelectedArea byte 23 | 24 | Menu *Menu 25 | Input *Input 26 | Terminal *Terminal 27 | Search *Search 28 | } 29 | 30 | func (cmd *Commands) Init() { 31 | if cmd.Menu == nil { 32 | cmd.Menu = &Menu{ 33 | DockerClient: cmd.DockerClient, 34 | Commands: cmd, 35 | } 36 | } 37 | cmd.Menu.Init() 38 | if cmd.Terminal == nil { 39 | cmd.Terminal = &Terminal{ 40 | Commands: cmd, 41 | } 42 | } 43 | cmd.Terminal.Init() 44 | if cmd.Input == nil { 45 | cmd.Input = &Input{ 46 | Commands: cmd, 47 | } 48 | } 49 | if cmd.Search == nil { 50 | cmd.Search = &Search{ 51 | Commands: cmd, 52 | } 53 | } 54 | cmd.Search.Init() 55 | } 56 | 57 | // Handle keyboard keys. 58 | func (cmd *Commands) Handle(key string) { 59 | if len(cmd.Input.Fields) > 0 { 60 | cmd.Input.Handle(key) 61 | return 62 | } 63 | switch key { 64 | case "": 65 | cmd.HandleSwitchMenu() 66 | default: 67 | cmd.HandleKeys(key) 68 | } 69 | } 70 | 71 | // HandleSwitchMenu - switch menu 72 | func (cmd *Commands) HandleSwitchMenu() { 73 | switch cmd.SelectedArea { 74 | 75 | case KeySelectedCommands: 76 | cmd.Terminal.Focus() 77 | cmd.Menu.UnFocus() 78 | cmd.SelectedArea = KeySelectedTerminal 79 | case KeySelectedTerminal: 80 | cmd.Terminal.UnFocus() 81 | cmd.Menu.Focus() 82 | cmd.SelectedArea = KeySelectedCommands 83 | } 84 | cmd.RenderAll() 85 | } 86 | 87 | // HandleKeys - handle other keys. 88 | func (cmd *Commands) HandleKeys(key string) { 89 | switch cmd.SelectedArea { 90 | case KeySelectedCommands: 91 | cmd.Menu.Handle(key) 92 | case KeySelectedTerminal: 93 | cmd.Terminal.Handle(key) 94 | } 95 | cmd.RenderAll() 96 | } 97 | 98 | // Render main render function, that render commands ui. 99 | func (cmd *Commands) Render() { 100 | cmd.Menu.Render() 101 | cmd.Terminal.Render() 102 | cmd.Search.Render() 103 | if len(cmd.Input.Fields) > 0 { 104 | for _, field := range cmd.Input.Fields { 105 | render_lock.RenderLock(field) 106 | } 107 | } 108 | render_lock.RenderLock(cmd.Search.Text) 109 | } 110 | -------------------------------------------------------------------------------- /ui/commands/input.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "github.com/atotto/clipboard" 6 | "github.com/daylioti/docker-commander/ui/render_lock" 7 | "github.com/gizak/termui/v3" 8 | "gopkg.in/yaml.v2" 9 | ) 10 | 11 | import ( 12 | "github.com/daylioti/docker-commander/config" 13 | commanderWidgets "github.com/daylioti/docker-commander/ui/widgets" 14 | ) 15 | 16 | // InputFieldHeight - border sizes. 17 | const ( 18 | InputFieldHeight = 3 19 | ) 20 | 21 | // Input UI struct. 22 | type Input struct { 23 | Fields []*commanderWidgets.TextBox 24 | Commands *Commands 25 | ActiveField int 26 | cnf config.Config 27 | } 28 | 29 | // Handle keyboard keys. 30 | func (in *Input) Handle(key string) { 31 | switch key { 32 | case "": 33 | in.Commands.Cnf.ReplacePlaceholders(in.GetInputValues(), &in.cnf) 34 | placeholders := in.Commands.Cnf.GetPlaceholders(in.Commands.Menu.Path(in.Commands.Cnf), make(map[string]string), in.Commands.Cnf) 35 | in.Commands.Cnf.ReplacePlaceholders(placeholders, &in.cnf) 36 | in.Commands.Menu.commandExecProcess(in.cnf) 37 | in.Commands.Menu.UpdateRenderElements(in.Commands.Cnf) 38 | in.Fields = nil 39 | in.Commands.RenderAll() 40 | 41 | case "": 42 | if in.ActiveField+1 >= len(in.Fields) { 43 | in.ActiveField = 0 44 | } else { 45 | in.ActiveField++ 46 | } 47 | in.Render() 48 | case "": 49 | if in.ActiveField != 0 { 50 | in.ActiveField-- 51 | in.Render() 52 | } 53 | case "": 54 | if in.ActiveField+1 < len(in.Fields) { 55 | in.ActiveField++ 56 | in.Render() 57 | } 58 | case "": 59 | in.Fields[in.ActiveField].Backspace() 60 | render_lock.RenderLock(in.Fields[in.ActiveField]) 61 | case "": 62 | in.Fields[in.ActiveField].InsertText(" ") 63 | render_lock.RenderLock(in.Fields[in.ActiveField]) 64 | case "": 65 | in.Fields[in.ActiveField].MoveCursorLeft() 66 | render_lock.RenderLock(in.Fields[in.ActiveField]) 67 | case "": 68 | in.Fields[in.ActiveField].MoveCursorRight() 69 | render_lock.RenderLock(in.Fields[in.ActiveField]) 70 | case "": 71 | // @Todo implement clipboard with better way. 72 | // It requires additional tools xsel, xclip, wl-clipboard. 73 | clip := in.ReadFromClipboard() 74 | if clip != "" { 75 | in.Fields[in.ActiveField].InsertText(clip) 76 | } 77 | render_lock.RenderLock(in.Fields[in.ActiveField]) 78 | case "": 79 | in.Fields = nil 80 | in.Commands.RenderAll() 81 | default: 82 | if in.allowedInput(key) { 83 | in.Fields[in.ActiveField].InsertText(key) 84 | } 85 | render_lock.RenderLock(in.Fields[in.ActiveField]) 86 | } 87 | } 88 | 89 | // Render function, that render input component. 90 | func (in *Input) Render() { 91 | in.Fields[in.ActiveField].BorderStyle = termui.NewStyle(termui.ColorGreen) 92 | for i, field := range in.Fields { 93 | if i != in.ActiveField { 94 | field.BorderStyle = termui.NewStyle(termui.ColorWhite) 95 | } 96 | render_lock.RenderLock(field) 97 | } 98 | } 99 | 100 | // ReadFromClipboard get string from clipboard. 101 | func (in *Input) ReadFromClipboard() string { 102 | clip, err := clipboard.ReadAll() 103 | if err != nil { 104 | return "" 105 | } 106 | return clip 107 | } 108 | 109 | // allowedInput - filter allowed to paste in input field keyboard keys. 110 | func (in *Input) allowedInput(key string) bool { 111 | return len(key) == 1 112 | } 113 | 114 | // GetInputValues - get input values, using chanel. 115 | func (in *Input) GetInputValues() map[string]string { 116 | values := make(map[string]string) 117 | for _, input := range in.Fields { 118 | if input.GetText() != "" { 119 | values[input.ID] = input.GetText() 120 | } 121 | } 122 | return values 123 | } 124 | 125 | // NewInputs - create and render input fields. 126 | func (in *Input) NewInputs(inputs yaml.MapSlice, cnf config.Config) { 127 | var inputsCount int 128 | in.Fields = nil 129 | in.cnf = cnf 130 | var box *commanderWidgets.TextBox 131 | for _, title := range inputs { 132 | box = commanderWidgets.NewTextBox() 133 | box.Title = fmt.Sprintf("%v", title.Value) 134 | box.ID = fmt.Sprintf("%v", title.Key) 135 | box.SetRect(in.Commands.TermWidth/4, inputsCount*InputFieldHeight, in.Commands.TermWidth-in.Commands.TermWidth/4, 136 | inputsCount*InputFieldHeight+InputFieldHeight) 137 | box.ShowCursor = true 138 | in.Fields = append(in.Fields, box) 139 | inputsCount++ 140 | } 141 | in.Fields[0].BorderStyle = termui.NewStyle(termui.ColorGreen) 142 | // Un-focus all other render elements. 143 | for _, list := range in.Commands.Menu.Lists { 144 | list.BorderStyle = termui.NewStyle(termui.ColorWhite) 145 | } 146 | termui.Clear() 147 | in.Commands.Menu.UnFocus() 148 | in.Commands.Terminal.UnFocus() 149 | } 150 | -------------------------------------------------------------------------------- /ui/commands/menu.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/daylioti/docker-commander/config" 5 | "github.com/daylioti/docker-commander/docker" 6 | "github.com/daylioti/docker-commander/ui/render_lock" 7 | "github.com/gizak/termui/v3" 8 | "github.com/gizak/termui/v3/widgets" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // Commands UI struct. 14 | type Menu struct { 15 | DockerClient *docker.Docker 16 | Commands *Commands 17 | Lists []*widgets.List 18 | } 19 | 20 | // Init initialize commands render component. 21 | func (m *Menu) Init() { 22 | m.updateStatus(m.Commands.Cnf, m.Path(m.Commands.Cnf)) 23 | m.UpdateRenderElements(m.Commands.Cnf) 24 | } 25 | 26 | // Handle keyboard keys. 27 | func (m *Menu) Handle(key string) { 28 | switch key { 29 | case "", "K", "k", "": 30 | m.changeSelected(0, -1, m.Commands.Cnf) 31 | case "", "H", "h": 32 | m.changeSelected(-1, 0, m.Commands.Cnf) 33 | case "", "L", "l": 34 | m.changeSelected(1, 0, m.Commands.Cnf) 35 | case "", "J", "j", "": 36 | m.changeSelected(0, 1, m.Commands.Cnf) 37 | case "": 38 | selected := m.getSelected() 39 | if selected.Exec.Cmd != "" { 40 | m.ExecuteSelectedCommand(selected) 41 | } 42 | } 43 | } 44 | 45 | // Render function, that render commands component. 46 | func (m *Menu) Render() { 47 | for listIndex := 0; listIndex < len(m.Lists); listIndex++ { 48 | render_lock.RenderLock(m.Lists[listIndex]) 49 | } 50 | } 51 | 52 | // Focus commands lists, set borders. 53 | func (m *Menu) Focus() { 54 | m.UpdateRenderElements(m.Commands.Cnf) 55 | m.Render() 56 | } 57 | 58 | // UnFocus commands lists, remove borders. 59 | func (m *Menu) UnFocus() { 60 | for _, list := range m.Lists { 61 | list.BorderStyle = termui.NewStyle(termui.ColorWhite) 62 | } 63 | m.Render() 64 | } 65 | 66 | // ExecuteSelectedCommand start execution process, open input if needed. 67 | func (m *Menu) ExecuteSelectedCommand(cnf config.Config) { 68 | if len(cnf.Exec.Input) > 0 { 69 | // Wait for input fields. 70 | m.Commands.Input.NewInputs(cnf.Exec.Input, cnf) 71 | } else { 72 | placeholders := m.Commands.Cnf.GetPlaceholders(m.Path(m.Commands.Cnf), make(map[string]string), m.Commands.Cnf) 73 | m.Commands.Cnf.ReplacePlaceholders(placeholders, &cnf) 74 | m.commandExecProcess(cnf) 75 | } 76 | } 77 | 78 | // commandExecProcess execute command in docker. 79 | func (m *Menu) commandExecProcess(cnf config.Config) { 80 | cnf.Exec.Input = nil 81 | id := m.Commands.Terminal.GetIDFromPath(m.Path(m.Commands.Cnf)) 82 | term := m.Commands.Terminal.NewTerminal(cnf, id) 83 | m.Commands.Terminal.Execute(term) 84 | } 85 | 86 | // SetDockerClient 87 | func (m *Menu) SetDockerClient(client *docker.Docker) { 88 | m.DockerClient = client 89 | } 90 | 91 | // getNearestConfigs return nearest from selected configs. 92 | func (m *Menu) getNearestConfigs() (*config.Config, *config.Config, *config.Config, *config.Config) { 93 | cnf := m.Commands.Cnf 94 | path := m.Path(m.Commands.Cnf) 95 | var c, cp, cu, cd *config.Config 96 | cnf.Selected = false 97 | c = cnf 98 | for i := 0; i <= len(path); i++ { 99 | if i == len(path)-1 { 100 | cp = c 101 | if len(cp.Config)-1 >= path[i]+1 { 102 | cd = &cp.Config[path[i]+1] 103 | } 104 | if len(cp.Config) > 0 && path[i]-1 >= 0 { 105 | cu = &cp.Config[path[i]-1] 106 | } 107 | c = &cp.Config[path[i]] 108 | break 109 | } else if len(path) >= i-1 && len(c.Config) >= path[i] { 110 | c = &c.Config[path[i]] 111 | } 112 | } 113 | return c, cp, cu, cd 114 | } 115 | 116 | // getSelectedConfigsList return list of selected configs. 117 | func (m *Menu) getSelectedConfigsList() []config.Config { 118 | _, cp, _, _ := m.getNearestConfigs() 119 | return cp.Config 120 | } 121 | 122 | // setCommandsSelectedIndex select menu item by index. 123 | func (m *Menu) setCommandsSelectedIndex(index int) { 124 | cfg := m.getSelectedConfigsList() 125 | for i := range cfg { 126 | cfg[i].Selected = false 127 | } 128 | cfg[index].Selected = true 129 | m.UpdateRenderElements(m.Commands.Cnf) 130 | m.Render() 131 | } 132 | 133 | // changeCommandsSelected change selected in cnf struct, depends on current selected item. 134 | // return bool - requires re-render or not. 135 | func (m *Menu) changeCommandsSelected(x, y int, c, cp, cu, cd *config.Config) bool { 136 | switch { 137 | case x == 1 && c.Config != nil: 138 | c.Selected = false 139 | c.Config[0].Selected = true 140 | return true 141 | case x == -1 && cp != nil && cp.Name != "": 142 | c.Selected = false 143 | cp.Selected = true 144 | return true 145 | case y == 1 && cd != nil: 146 | c.Selected = false 147 | cd.Selected = true 148 | if cd.Config != nil || c.Config != nil { 149 | return true 150 | } 151 | case y == -1 && cu != nil: 152 | c.Selected = false 153 | cu.Selected = true 154 | if cu.Config != nil || c.Config != nil { 155 | return true 156 | } 157 | } 158 | return false 159 | } 160 | 161 | // changeSelected change selected, depends on current selected item. 162 | func (m *Menu) changeSelected(x int, y int, cnf *config.Config) { 163 | c, cp, cu, cd := m.getNearestConfigs() 164 | if c.Name == "" { 165 | return 166 | } 167 | clear := m.changeCommandsSelected(x, y, c, cp, cu, cd) 168 | m.updateStatus(cnf, m.Path(cnf)) 169 | m.UpdateRenderElements(m.Commands.Cnf) 170 | if !clear { 171 | // No new elements and elements to remove. 172 | // Just render command lists 173 | m.Render() 174 | } else { 175 | // Re-render all. 176 | termui.Clear() 177 | m.Commands.RenderAll() 178 | } 179 | } 180 | 181 | // getSelected 182 | func (m *Menu) getSelected() config.Config { 183 | c := m.Commands.Cnf 184 | for _, path := range m.Path(m.Commands.Cnf) { 185 | c = &c.Config[path] 186 | } 187 | return *c 188 | } 189 | 190 | // Path get array with path to selected item. 191 | func (m *Menu) Path(cnf *config.Config) []int { 192 | var path []int 193 | var p []int 194 | m.getSelectedPath(&path, cnf) 195 | for i := len(path) - 1; i >= 0; i-- { 196 | if path[i] > 0 { 197 | p = append(p, path[i]-1) 198 | } 199 | } 200 | return p 201 | } 202 | 203 | // Update Status (display flag) prop in commands structure, depends on current selected item. 204 | func (m *Menu) updateStatus(cnf *config.Config, path []int) { 205 | if len(path) == 0 { 206 | return 207 | } 208 | cnf = &cnf.Config[path[0]] 209 | 210 | if len(cnf.Config) > 0 { 211 | for i := 0; i < len(cnf.Config); i++ { 212 | cnf.Config[i].Status = true 213 | } 214 | } 215 | if len(path) == 1 { 216 | cnf.Selected = true 217 | } else { 218 | m.updateStatus(cnf, path[1:]) 219 | } 220 | } 221 | 222 | // Get selected item path via int array 223 | func (m *Menu) getSelectedPath(path *[]int, cnf *config.Config) bool { 224 | if cnf.Selected { 225 | return true 226 | } 227 | for i := 0; i < len(cnf.Config); i++ { 228 | if m.getSelectedPath(path, &cnf.Config[i]) { 229 | *path = append(*path, i+1) 230 | return true 231 | } 232 | } 233 | return false 234 | } 235 | 236 | // UpdateRenderElements update lists from config. 237 | func (m *Menu) UpdateRenderElements(c *config.Config) { 238 | var width, height int 239 | if h, exist := m.Commands.ConfigUi.UI.Commands["height"]; !exist { 240 | height = 5 241 | } else { 242 | height, _ = strconv.Atoi(h) 243 | } 244 | 245 | var menuList *widgets.List 246 | borderSize := 2 247 | widthPrev := 0 248 | path := append(m.Path(m.Commands.Cnf), 0) 249 | m.Lists = nil 250 | for i, pathIndex := range path { 251 | if len(c.Config) <= pathIndex { 252 | break 253 | } 254 | width = 0 255 | m.Lists = append(m.Lists, widgets.NewList()) 256 | menuList = m.Lists[i] 257 | menuList.SelectedRow = 0 258 | for p, cnf := range c.Config { 259 | if cnf.Selected { 260 | menuList.SelectedRow = len(menuList.Rows) 261 | menuList.BorderStyle = termui.NewStyle(termui.ColorGreen) 262 | menuList.SelectedRowStyle = termui.NewStyle(termui.ColorBlack, termui.ColorGreen) 263 | } else if !cnf.Selected && len(path) > i && pathIndex == p && i < len(path)-1 { 264 | menuList.SelectedRow = len(menuList.Rows) 265 | menuList.SelectedRowStyle = termui.NewStyle(termui.ColorGreen) 266 | } 267 | menuList.Rows = append(menuList.Rows, cnf.Name) 268 | if len(cnf.Name) > width { 269 | width = len(cnf.Name) 270 | } 271 | } 272 | menuList.Border = true 273 | width += borderSize 274 | menuList.SetRect(widthPrev, 0, widthPrev+width, height+borderSize) 275 | widthPrev += width 276 | c = &c.Config[pathIndex] 277 | } 278 | } 279 | 280 | // Search return list of config elements array indexes. 281 | func (m *Menu) Search(key string) []int { 282 | var res []int 283 | for index, list := range m.getSelectedConfigsList() { 284 | if strings.Contains(list.Name, key) { 285 | res = append(res, index) 286 | } 287 | } 288 | return res 289 | } 290 | -------------------------------------------------------------------------------- /ui/commands/search.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/daylioti/docker-commander/ui/render_lock" 5 | "github.com/gizak/termui/v3" 6 | "github.com/gizak/termui/v3/widgets" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // Search UI struct 12 | type Search struct { 13 | Commands *Commands 14 | Text *widgets.Paragraph 15 | Input string 16 | searchIndexes []int 17 | selectedIndex int 18 | } 19 | 20 | // Init initialize search render component. 21 | func (s *Search) Init() { 22 | s.Text = widgets.NewParagraph() 23 | s.Text.Border = false 24 | s.Text.PaddingBottom = -1 25 | } 26 | 27 | // Handle keyboard keys. 28 | func (s *Search) Handle(key string) { 29 | switch key { 30 | case "": 31 | if len(s.Input) > 0 { 32 | s.Input = s.Input[:len(s.Input)-1] 33 | } 34 | if len(s.Input) == 0 { 35 | s.Reset() 36 | return 37 | } 38 | case "": 39 | s.Next() 40 | default: 41 | if !strings.Contains(key, "<") { 42 | s.searchIndexes = nil 43 | s.selectedIndex = 0 44 | s.Input += key 45 | } 46 | } 47 | s.Search() 48 | } 49 | 50 | // Render render search box. 51 | func (s *Search) Render() { 52 | var pager string 53 | if len(s.searchIndexes) > 0 { 54 | pager = strconv.Itoa(s.selectedIndex+1) + "/" + strconv.Itoa(len(s.searchIndexes)) 55 | } else { 56 | pager = "0/0" 57 | } 58 | width, _ := termui.TerminalDimensions() 59 | spaces := strings.Repeat(" ", width-len(s.Input)-len(pager)-10) 60 | s.Text.Text = "Search: " + s.Input + spaces + pager 61 | render_lock.RenderLock(s.Text) 62 | } 63 | 64 | // Reset reset search box to defaults. 65 | func (s *Search) Reset() { 66 | s.Init() 67 | s.Input = "" 68 | s.searchIndexes = make([]int, 0) 69 | s.selectedIndex = 0 70 | _, s.Commands.TermHeight = termui.TerminalDimensions() 71 | render_lock.RenderLock(s.Text) 72 | } 73 | 74 | // Search execute search. 75 | func (s *Search) Search() { 76 | _, s.Commands.TermHeight = termui.TerminalDimensions() 77 | s.Commands.TermHeight -= 2 78 | width, _ := termui.TerminalDimensions() 79 | s.Text.SetRect(0, s.Commands.TermHeight, width, s.Commands.TermHeight+2) 80 | if s.Commands.SelectedArea == KeySelectedCommands { 81 | s.searchIndexes = s.Commands.Menu.Search(s.Input) 82 | s.Commands.Menu.setCommandsSelectedIndex(s.getSearchIndex()) 83 | } else if s.Commands.SelectedArea == KeySelectedTerminal { 84 | s.searchIndexes = s.Commands.Terminal.Search(s.Input) 85 | s.Commands.Terminal.DisplayTerminal.SelectedRow = s.getSearchIndex() 86 | s.Commands.Terminal.DisplayTerminalRender() 87 | } 88 | s.Render() 89 | } 90 | 91 | // getSearchIndex return config index. 92 | func (s *Search) getSearchIndex() int { 93 | index := 0 94 | if s.selectedIndex+1 > len(s.searchIndexes) { 95 | s.selectedIndex = 0 96 | } else if len(s.searchIndexes) != 0 { 97 | index = s.searchIndexes[s.selectedIndex] 98 | } 99 | return index 100 | } 101 | 102 | // Next display next search item. 103 | func (s *Search) Next() { 104 | if s.selectedIndex+1 > len(s.searchIndexes) { 105 | s.selectedIndex = 0 106 | } else { 107 | s.selectedIndex++ 108 | } 109 | if s.Commands.SelectedArea == KeySelectedCommands { 110 | s.Commands.Menu.setCommandsSelectedIndex(s.getSearchIndex()) 111 | } else if s.Commands.SelectedArea == KeySelectedTerminal { 112 | s.Commands.Terminal.DisplayTerminal.SelectedRow = s.getSearchIndex() 113 | s.Commands.Terminal.DisplayTerminalRender() 114 | } 115 | s.Render() 116 | } 117 | -------------------------------------------------------------------------------- /ui/commands/terminal.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/daylioti/docker-commander/config" 5 | "github.com/daylioti/docker-commander/docker" 6 | "github.com/daylioti/docker-commander/ui/render_lock" 7 | commanderWidgets "github.com/daylioti/docker-commander/ui/widgets" 8 | "github.com/gizak/termui/v3" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // TerminalUI UI struct. 14 | type Terminal struct { 15 | TabPane *commanderWidgets.TabsPaneStyled 16 | DisplayTerminal *commanderWidgets.TerminalList 17 | Commands *Commands 18 | } 19 | 20 | // Init initialize terminal render component. 21 | func (t *Terminal) Init() { 22 | if t.DisplayTerminal == nil { 23 | t.DisplayTerminal = t.InitDisplayTerminal() 24 | } 25 | t.updateRect() 26 | if t.TabPane == nil { 27 | t.TabPane = commanderWidgets.NewTabPaneStyled() 28 | } 29 | t.TabPane.SetRect(0, t.Commands.ConfigUi.GetCommandsHeight(), 30 | t.Commands.TermWidth, t.Commands.ConfigUi.GetCommandsHeight()+3) 31 | 32 | t.Commands.DockerClient.Exec.SetTerminalUpdateFn(t.TerminalUpdate) 33 | } 34 | 35 | // InitDisplayTerminal initialize command output list. 36 | func (t *Terminal) InitDisplayTerminal() *commanderWidgets.TerminalList { 37 | list := commanderWidgets.NewTerminalList() 38 | t.updateRect() 39 | return list 40 | } 41 | 42 | // updateRect update terminal rect. 43 | func (t *Terminal) updateRect() { 44 | if t.DisplayTerminal != nil { 45 | t.DisplayTerminal.SetRect(0, t.Commands.ConfigUi.GetCommandsHeight()+3, 46 | t.Commands.TermWidth, t.Commands.TermHeight) 47 | } 48 | } 49 | 50 | // Render function, that render terminal component. 51 | func (t *Terminal) Render() { 52 | t.updateRect() 53 | t.TabPaneRender() 54 | t.DisplayTerminalRender() 55 | } 56 | 57 | // TabPaneRender render only tab. 58 | func (t *Terminal) TabPaneRender() { 59 | render_lock.RenderLock(t.TabPane) 60 | } 61 | 62 | // DisplayTerminalRender render only terminal. 63 | func (t *Terminal) DisplayTerminalRender() { 64 | render_lock.RenderLock(t.DisplayTerminal) 65 | } 66 | 67 | // TerminalUpdate calls on receive updates from docker process. 68 | func (t *Terminal) TerminalUpdate(term *docker.TerminalRun, finished bool) { 69 | term.List.SelectedRow = len(term.List.Rows) 70 | if finished { 71 | term.Running = false 72 | term.TabItem.Style = termui.NewStyle(termui.ColorRed) 73 | t.Commands.RenderAll() 74 | } 75 | if term.Active { 76 | t.DisplayTerminalRender() 77 | } 78 | } 79 | 80 | // Handle keyboard keys. 81 | func (t *Terminal) Handle(key string) { 82 | switch key { 83 | case "", "K", "k", "": 84 | if len(t.DisplayTerminal.Rows) > 0 { 85 | t.DisplayTerminal.ScrollUp() 86 | t.DisplayTerminalRender() 87 | } 88 | case "", "J", "j", "": 89 | if len(t.DisplayTerminal.Rows) > 0 { 90 | t.DisplayTerminal.ScrollDown() 91 | t.DisplayTerminalRender() 92 | } 93 | case "": 94 | if len(t.DisplayTerminal.Rows) > 0 { 95 | t.DisplayTerminal.ScrollPageUp() 96 | t.DisplayTerminalRender() 97 | } 98 | case "": 99 | if len(t.DisplayTerminal.Rows) > 0 { 100 | t.DisplayTerminal.ScrollPageDown() 101 | t.DisplayTerminalRender() 102 | } 103 | case "": 104 | if len(t.DisplayTerminal.Rows) > 0 { 105 | t.DisplayTerminal.SelectedRow = 0 106 | t.DisplayTerminalRender() 107 | } 108 | case "": 109 | if len(t.DisplayTerminal.Rows) > 0 { 110 | t.DisplayTerminal.SelectedRow = len(t.DisplayTerminal.Rows) - 1 111 | t.DisplayTerminalRender() 112 | } 113 | case "", "H", "h": 114 | t.TabPane.FocusLeft() 115 | index := t.Commands.DockerClient.Exec.GetActiveTerminalIndex() 116 | if index > 0 { 117 | t.SwitchTerminal(t.Commands.DockerClient.Exec.Terminals[index-1]) 118 | } 119 | case "", "L", "l": 120 | t.TabPane.FocusRight() 121 | index := t.Commands.DockerClient.Exec.GetActiveTerminalIndex() 122 | if index >= 0 && index < len(t.Commands.DockerClient.Exec.Terminals)-1 { 123 | t.SwitchTerminal(t.Commands.DockerClient.Exec.Terminals[index+1]) 124 | } 125 | case "": 126 | index := t.Commands.DockerClient.Exec.GetActiveTerminalIndex() 127 | t.DisplayTerminal = t.InitDisplayTerminal() 128 | t.Commands.DockerClient.Exec.Terminals[index] = t.Commands.DockerClient.Exec.Terminals[len(t.Commands.DockerClient.Exec.Terminals)-1] 129 | t.Commands.DockerClient.Exec.Terminals = t.Commands.DockerClient.Exec.Terminals[:len(t.Commands.DockerClient.Exec.Terminals)-1] 130 | if len(t.Commands.DockerClient.Exec.Terminals) > 0 { 131 | t.Commands.DockerClient.Exec.Terminals[0].Active = true 132 | } 133 | t.UpdateRunningStatus() 134 | t.Commands.RenderAll() 135 | } 136 | } 137 | 138 | // GetIDFromPath get id from selected commands list item. 139 | func (t *Terminal) GetIDFromPath(path []int) string { 140 | id := "0" 141 | for _, i := range path { 142 | id += strconv.Itoa(i) 143 | } 144 | return id 145 | } 146 | 147 | // SwitchTerminal set selected terminal with id. 148 | func (t *Terminal) SwitchTerminal(term *docker.TerminalRun) { 149 | t.deactivateTerminals() 150 | term.Active = true 151 | t.DisplayTerminal = term.List 152 | t.UpdateRunningStatus() 153 | t.Commands.Terminal.DisplayTerminal.BorderStyle = termui.NewStyle(termui.ColorGreen) 154 | t.Commands.RenderAll() 155 | } 156 | 157 | // Focus commands lists, set borders. 158 | func (t *Terminal) Focus() { 159 | t.TabPane.BorderStyle = termui.NewStyle(termui.ColorGreen) 160 | t.DisplayTerminal.BorderStyle = termui.NewStyle(termui.ColorGreen) 161 | t.UpdateRunningStatus() 162 | t.Commands.RenderAll() 163 | } 164 | 165 | // UnFocus commands lists, remove borders. 166 | func (t *Terminal) UnFocus() { 167 | t.UpdateRunningStatus() 168 | t.TabPane.BorderStyle = termui.NewStyle(termui.ColorWhite) 169 | t.DisplayTerminal.BorderStyle = termui.NewStyle(termui.ColorWhite) 170 | t.Commands.Render() 171 | } 172 | 173 | // SetDisplayTerminal change display terminal. 174 | func (t *Terminal) SetDisplayTerminal(term *commanderWidgets.TerminalList) { 175 | t.DisplayTerminal = term 176 | } 177 | 178 | // UpdateRunningStatus change tabs colors depends on selected tab, running or not process in docker. 179 | func (t *Terminal) UpdateRunningStatus() { 180 | var terminal *docker.TerminalRun 181 | t.TabPane.TabNames = nil 182 | for i := 0; i < len(t.Commands.DockerClient.Exec.Terminals); i++ { 183 | terminal = t.Commands.DockerClient.Exec.Terminals[i] 184 | if terminal.Active { 185 | if terminal.Running { 186 | terminal.TabItem.Style = termui.NewStyle(termui.ColorBlack, termui.ColorGreen) 187 | } else { 188 | terminal.TabItem.Style = termui.NewStyle(termui.ColorBlack, termui.ColorRed) 189 | } 190 | t.SetDisplayTerminal(terminal.List) 191 | } else if terminal.Running { 192 | terminal.TabItem.Style = termui.NewStyle(termui.ColorGreen) 193 | } else { 194 | terminal.TabItem.Style = termui.NewStyle(termui.ColorRed) 195 | } 196 | t.TabPane.TabNames = append(t.TabPane.TabNames, terminal.TabItem) 197 | } 198 | } 199 | 200 | // Execute start new process in docker. 201 | func (t *Terminal) Execute(term *docker.TerminalRun) { 202 | t.Commands.DockerClient.Exec.Terminals = append(t.Commands.DockerClient.Exec.Terminals, term) 203 | t.Commands.DockerClient.Exec.CommandRun(term) 204 | t.SwitchTerminal(term) 205 | } 206 | 207 | // removeFinishedTerminals remove first terminal object if finished and length of names bigger that tab width. 208 | func (t *Terminal) removeFinishedTerminals() { 209 | var tabItemsLength int 210 | tabBorder := 3 211 | for _, term := range t.Commands.DockerClient.Exec.Terminals { 212 | tabItemsLength += len(term.TabItem.Name) + tabBorder*2 213 | } 214 | if tabItemsLength-tabBorder*2 >= t.Commands.TermWidth-2 { 215 | for i, term := range t.Commands.DockerClient.Exec.Terminals { 216 | if !term.Running { 217 | t.Commands.DockerClient.Exec.Terminals = append(t.Commands.DockerClient.Exec.Terminals[:i], 218 | t.Commands.DockerClient.Exec.Terminals[i+1:]...) 219 | t.Commands.Render() 220 | return 221 | } 222 | } 223 | } 224 | } 225 | 226 | // deactivateTerminals set active to false on all terminals. 227 | func (t *Terminal) deactivateTerminals() { 228 | for _, term := range t.Commands.DockerClient.Exec.Terminals { 229 | term.Active = false 230 | } 231 | } 232 | 233 | // Search return list of row elements array indexes. 234 | func (t *Terminal) Search(key string) []int { 235 | var res []int 236 | if t.DisplayTerminal != nil { 237 | for index, row := range t.DisplayTerminal.Rows { 238 | if strings.Contains(row, key) { 239 | res = append(res, index) 240 | } 241 | } 242 | } 243 | return res 244 | } 245 | 246 | // NewTerminal return new terminal object, 247 | func (t *Terminal) NewTerminal(config config.Config, id string) *docker.TerminalRun { 248 | list := commanderWidgets.NewTerminalList() 249 | list.SelectedRowStyle = termui.NewStyle(termui.ColorBlack, termui.ColorGreen) 250 | list.SetRect(0, t.Commands.ConfigUi.GetCommandsHeight()+3, 251 | t.Commands.TermWidth, t.Commands.TermHeight) 252 | t.removeFinishedTerminals() 253 | t.deactivateTerminals() 254 | return &docker.TerminalRun{ 255 | TabItem: &commanderWidgets.TabItem{ 256 | Name: config.Name, 257 | Style: termui.NewStyle(termui.ColorGreen), 258 | }, 259 | List: list, 260 | Active: true, 261 | Running: true, 262 | ContainerID: t.Commands.DockerClient.Exec.GetContainerID(config), 263 | Name: config.Name, 264 | ID: id, 265 | WorkDir: config.Exec.WorkingDir, 266 | Command: config.Exec.Cmd, 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /ui/helpers/tty_colors.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | commanderWidgets "github.com/daylioti/docker-commander/ui/widgets" 5 | "github.com/gizak/termui/v3" 6 | "strconv" 7 | "unicode" 8 | ) 9 | 10 | // StyleInit - rune for "[" 11 | const StyleInit = 91 12 | 13 | // StyleEnd - rune for "m" 14 | const StyleEnd = 109 15 | 16 | // StyleAttrLength - means length of "[m" 17 | const StyleAttrLength = 2 18 | 19 | // StyleMinLength - means length of ["2"m 20 | const StyleMinLength = 1 21 | 22 | // DefaultColor - back to default color rune 23 | var resetStyle = []byte{39, 0, 1, 2, 4, 5, 7, 8, 22} 24 | 25 | // vt100Codes16 codes from https://misc.flogisoft.com/bash/tip_colors_and_formatting 26 | var vt100Codes16 = map[byte]string{ 27 | 30: "fg:black", 28 | 31: "fg:red", 29 | 32: "fg:green", 30 | 33: "fg:yellow", 31 | 34: "fg:blue", 32 | 35: "fg:magenta", 33 | 36: "fg:cyan", 34 | 37: "fg:254", 35 | 90: "fg:244", 36 | 91: "fg:160", 37 | 92: "fg:46", 38 | 93: "fg:226", 39 | 94: "fg:111", 40 | 95: "fg:134", 41 | 96: "fg:87", 42 | 97: "fg:white", 43 | 44 | 49: "bg:white", 45 | 40: "bg:black", 46 | 41: "bg:red", 47 | 42: "bg:green", 48 | 43: "bg:yellow", 49 | 44: "bg:blue", 50 | 45: "bg:magenta", 51 | 46: "bg:cyan", 52 | 47: "bg:254", 53 | } 54 | 55 | // GetAllTermColors return all colors that termui can render. 56 | func GetAllTermColors() map[string]termui.Color { 57 | colors := termui.StyleParserColorMap 58 | for i := 0; i < 255; i++ { 59 | colors[strconv.Itoa(i)] = termui.Color(i) 60 | } 61 | return colors 62 | } 63 | 64 | // TTYColorsParse - convert ANSI colors to termui. 65 | func TTYColorsParse(buff []byte) []byte { 66 | var index int 67 | var styleByte byte 68 | var styleLength int 69 | var step int 70 | var exist bool 71 | var style string 72 | var replace []byte 73 | for { 74 | if len(buff) < index+StyleMinLength+StyleAttrLength || len(buff) <= 6 || index < 0 { 75 | return buff 76 | } 77 | if len(buff) > index+StyleAttrLength && buff[index] == StyleInit && unicode.IsNumber(rune(buff[index+1])) { 78 | // Possible style. 79 | if len(buff) > index+StyleAttrLength { 80 | if buff[index+StyleAttrLength] == StyleEnd { 81 | styleByte = convertStyleByte([]byte{buff[index+1]}) 82 | styleLength = 1 83 | } 84 | if unicode.IsNumber(rune(buff[index+2])) { 85 | if len(buff) > index+3 && buff[index+3] == StyleEnd { 86 | styleByte = convertStyleByte([]byte{buff[index+1], buff[index+2]}) 87 | styleLength = 2 88 | } else if len(buff) > index+4 && unicode.IsNumber(rune(buff[index+3])) && buff[index+4] == StyleEnd { 89 | styleByte = convertStyleByte([]byte{buff[index+1], buff[index+2], buff[index+3]}) 90 | styleLength = 3 91 | } 92 | } 93 | if !isReset(styleByte) { 94 | style, exist = vt100Codes16[styleByte] 95 | if exist && len(buff) > index+styleLength+7 { 96 | // 7 means minimum length of closing style. 97 | // Replace start ANSI style to termui start style byte. 98 | replace = []byte(string(commanderWidgets.TokenBeginStyledText)) 99 | buff = append(buff[:index+styleLength-StyleAttrLength], append(replace, buff[index+styleLength+StyleAttrLength:]...)...) 100 | index += len(replace) - styleLength 101 | } else { 102 | // Style not in list, remove them. 103 | if len(buff) < index+styleLength+StyleAttrLength { 104 | step = 1 105 | } else { 106 | step = 2 107 | } 108 | buff = append(buff[:index], buff[index+styleLength+step:]...) 109 | index -= styleLength + step 110 | } 111 | } else { 112 | if exist { 113 | replace = []byte(string(commanderWidgets.TokenEndStyledText) + string(commanderWidgets.TokenBeginStyle) + style + string(commanderWidgets.TokenEndStyle)) 114 | // Paste termui style. 115 | buff = append(buff[:index+styleLength-StyleAttrLength], append(replace, buff[index+styleLength+StyleAttrLength:]...)...) 116 | index += len(replace) 117 | } else { 118 | // Remove reset chars. 119 | if len(buff) < index+styleLength+StyleAttrLength { 120 | step = 1 121 | } else { 122 | step = 2 123 | } 124 | buff = append(buff[:index], buff[index+styleLength+step:]...) 125 | index -= styleLength + step 126 | } 127 | style = "" 128 | exist = false 129 | } 130 | } 131 | } 132 | index++ 133 | } 134 | } 135 | 136 | // isReset check for closing style byte. 137 | func isReset(checkByte byte) bool { 138 | for _, reset := range resetStyle { 139 | if checkByte == reset { 140 | return true 141 | } 142 | } 143 | return false 144 | } 145 | 146 | // convertStyleRune convert color code to int8 format. 147 | func convertStyleByte(style []byte) byte { 148 | var result int64 149 | result, _ = strconv.ParseInt(string(style), 10, 8) 150 | return byte(result) 151 | } 152 | -------------------------------------------------------------------------------- /ui/render_lock/render_lock.go: -------------------------------------------------------------------------------- 1 | package render_lock 2 | 3 | import ( 4 | "github.com/gizak/termui/v3" 5 | "sync" 6 | ) 7 | 8 | type RenderLockStruct struct { 9 | mu sync.Mutex 10 | } 11 | 12 | var Lock = RenderLockStruct{} 13 | 14 | func RenderLock(items ...termui.Drawable) { 15 | Lock.mu.Lock() 16 | termui.Render(items...) 17 | Lock.mu.Unlock() 18 | } 19 | -------------------------------------------------------------------------------- /ui/ui.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "github.com/daylioti/docker-commander/config" 5 | "github.com/daylioti/docker-commander/docker" 6 | "github.com/daylioti/docker-commander/ui/commands" 7 | "github.com/daylioti/docker-commander/ui/helpers" 8 | "github.com/gizak/termui/v3" 9 | ) 10 | 11 | // UI main user interface struct. 12 | type UI struct { 13 | ConfigUi *config.UIConfig 14 | 15 | Cnf *config.Config 16 | DockerClient *docker.Docker 17 | 18 | Commands *commands.Commands 19 | // Flag for switching control between menu and terminal area. 20 | SelectedMenu byte 21 | } 22 | 23 | // Init initialize all render components. 24 | func (ui *UI) Init(cnf *config.Config, dockerClient *docker.Docker, configUi *config.UIConfig) { 25 | 26 | if ui.Cnf == nil { 27 | ui.Cnf = cnf 28 | } 29 | if ui.ConfigUi == nil { 30 | ui.ConfigUi = configUi 31 | } 32 | if ui.Commands == nil { 33 | ui.Commands = &commands.Commands{ 34 | ConfigUi: ui.ConfigUi, 35 | DockerClient: dockerClient, 36 | RenderAll: ui.Render, 37 | Cnf: ui.Cnf, 38 | } 39 | } 40 | ui.Commands.TermWidth, ui.Commands.TermHeight = termui.TerminalDimensions() 41 | ui.Commands.Init() 42 | termui.StyleParserColorMap = helpers.GetAllTermColors() 43 | ui.Render() 44 | } 45 | 46 | // Handle keyboard keys. 47 | func (ui *UI) Handle(key string) { 48 | ui.Commands.Handle(key) 49 | } 50 | 51 | // Render main render function, that render all ui. 52 | func (ui *UI) Render() { 53 | termui.Clear() 54 | ui.Commands.Render() 55 | } 56 | -------------------------------------------------------------------------------- /ui/widgets/tabs_styled.go: -------------------------------------------------------------------------------- 1 | package widgets 2 | 3 | import ( 4 | "github.com/gizak/termui/v3" 5 | "image" 6 | ) 7 | 8 | // TabsPaneStyled widget structure. 9 | type TabsPaneStyled struct { 10 | termui.Block 11 | TabNames []*TabItem 12 | ActiveTabIndex int 13 | ActiveTabStyle termui.Style 14 | InactiveTabStyle termui.Style 15 | } 16 | 17 | // TabItem tab item structure. 18 | type TabItem struct { 19 | Style termui.Style 20 | Name string 21 | } 22 | 23 | // NewTabPaneStyled create new tab widget. 24 | func NewTabPaneStyled() *TabsPaneStyled { 25 | return &TabsPaneStyled{ 26 | Block: *termui.NewBlock(), 27 | ActiveTabStyle: termui.Theme.Tab.Active, 28 | InactiveTabStyle: termui.Theme.Tab.Inactive, 29 | } 30 | } 31 | 32 | // FocusLeft focus element to left. 33 | func (tp *TabsPaneStyled) FocusLeft() { 34 | if tp.ActiveTabIndex > 0 { 35 | tp.ActiveTabIndex-- 36 | } 37 | } 38 | 39 | // FocusRight focus element to right. 40 | func (tp *TabsPaneStyled) FocusRight() { 41 | if tp.ActiveTabIndex < len(tp.TabNames)-1 { 42 | tp.ActiveTabIndex++ 43 | } 44 | } 45 | 46 | // Draw implements the Drawable interface. 47 | func (tp *TabsPaneStyled) Draw(buf *termui.Buffer) { 48 | tp.Block.Draw(buf) 49 | 50 | xCoordinate := tp.Inner.Min.X 51 | for i, name := range tp.TabNames { 52 | ColorPair := name.Style 53 | 54 | buf.SetString( 55 | termui.TrimString(name.Name, tp.Inner.Max.X-xCoordinate), 56 | ColorPair, 57 | image.Pt(xCoordinate, tp.Inner.Min.Y), 58 | ) 59 | 60 | xCoordinate += 1 + len(name.Name) 61 | 62 | if i < len(tp.TabNames)-1 && xCoordinate < tp.Inner.Max.X { 63 | buf.SetCell( 64 | termui.NewCell(termui.VERTICAL_LINE, termui.NewStyle(termui.ColorWhite)), 65 | image.Pt(xCoordinate, tp.Inner.Min.Y), 66 | ) 67 | } 68 | 69 | xCoordinate += 2 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ui/widgets/terminal_list.go: -------------------------------------------------------------------------------- 1 | package widgets 2 | 3 | import ( 4 | "image" 5 | "strings" 6 | 7 | rw "github.com/mattn/go-runewidth" 8 | 9 | . "github.com/gizak/termui/v3" 10 | ) 11 | 12 | const ( 13 | tokenFg = "fg" 14 | tokenBg = "bg" 15 | tokenModifier = "mod" 16 | 17 | tokenItemSeparator = "," 18 | tokenValueSeparator = ":" 19 | 20 | // Use some random unique byte to have possibility to display "]", ")" chars. 21 | TokenBeginStyledText rune = iota + 5000 22 | TokenEndStyledText 23 | TokenBeginStyle 24 | TokenEndStyle 25 | ) 26 | 27 | type parserState uint 28 | 29 | const ( 30 | parserStateDefault parserState = iota 31 | parserStateStyleItems 32 | parserStateStyledText 33 | ) 34 | 35 | type TerminalList struct { 36 | Block 37 | Rows []string 38 | WrapText bool 39 | TextStyle Style 40 | SelectedRow int 41 | topRow int 42 | SelectedRowStyle Style 43 | } 44 | 45 | func NewTerminalList() *TerminalList { 46 | return &TerminalList{ 47 | Block: *NewBlock(), 48 | TextStyle: Theme.List.Text, 49 | SelectedRowStyle: Theme.List.Text, 50 | } 51 | } 52 | 53 | func (self *TerminalList) Draw(buf *Buffer) { 54 | self.Block.Draw(buf) 55 | 56 | point := self.Inner.Min 57 | 58 | // adjusts view into widget 59 | if self.SelectedRow >= self.Inner.Dy()+self.topRow { 60 | self.topRow = self.SelectedRow - self.Inner.Dy() + 1 61 | } else if self.SelectedRow < self.topRow { 62 | self.topRow = self.SelectedRow 63 | } 64 | 65 | // draw rows 66 | for row := self.topRow; row < len(self.Rows) && point.Y < self.Inner.Max.Y; row++ { 67 | cells := TerminalParseStyles(self.Rows[row], self.TextStyle) 68 | if self.WrapText { 69 | cells = WrapCells(cells, uint(self.Inner.Dx())) 70 | } 71 | for j := 0; j < len(cells) && point.Y < self.Inner.Max.Y; j++ { 72 | style := cells[j].Style 73 | if row == self.SelectedRow { 74 | style = self.SelectedRowStyle 75 | } 76 | if cells[j].Rune == '\n' { 77 | point = image.Pt(self.Inner.Min.X, point.Y+1) 78 | } else { 79 | if point.X+1 == self.Inner.Max.X+1 && len(cells) > self.Inner.Dx() { 80 | buf.SetCell(NewCell(ELLIPSES, style), point.Add(image.Pt(-1, 0))) 81 | break 82 | } else { 83 | buf.SetCell(NewCell(cells[j].Rune, style), point) 84 | point = point.Add(image.Pt(rw.RuneWidth(cells[j].Rune), 0)) 85 | } 86 | } 87 | } 88 | point = image.Pt(self.Inner.Min.X, point.Y+1) 89 | } 90 | 91 | // draw UP_ARROW if needed 92 | if self.topRow > 0 { 93 | buf.SetCell( 94 | NewCell(UP_ARROW, NewStyle(ColorWhite)), 95 | image.Pt(self.Inner.Max.X-1, self.Inner.Min.Y), 96 | ) 97 | } 98 | 99 | // draw DOWN_ARROW if needed 100 | if len(self.Rows) > int(self.topRow)+self.Inner.Dy() { 101 | buf.SetCell( 102 | NewCell(DOWN_ARROW, NewStyle(ColorWhite)), 103 | image.Pt(self.Inner.Max.X-1, self.Inner.Max.Y-1), 104 | ) 105 | } 106 | } 107 | 108 | // ScrollAmount scrolls by amount given. If amount is < 0, then scroll up. 109 | // There is no need to set self.topRow, as this will be set automatically when drawn, 110 | // since if the selected item is off screen then the topRow variable will change accordingly. 111 | func (self *TerminalList) ScrollAmount(amount int) { 112 | if len(self.Rows)-int(self.SelectedRow) <= amount { 113 | self.SelectedRow = len(self.Rows) - 1 114 | } else if int(self.SelectedRow)+amount < 0 { 115 | self.SelectedRow = 0 116 | } else { 117 | self.SelectedRow += amount 118 | } 119 | } 120 | 121 | func (self *TerminalList) ScrollUp() { 122 | self.ScrollAmount(-1) 123 | } 124 | 125 | func (self *TerminalList) ScrollDown() { 126 | self.ScrollAmount(1) 127 | } 128 | 129 | func (self *TerminalList) ScrollPageUp() { 130 | // If an item is selected below top row, then go to the top row. 131 | if self.SelectedRow > self.topRow { 132 | self.SelectedRow = self.topRow 133 | } else { 134 | self.ScrollAmount(-self.Inner.Dy()) 135 | } 136 | } 137 | 138 | func (self *TerminalList) ScrollPageDown() { 139 | self.ScrollAmount(self.Inner.Dy()) 140 | } 141 | 142 | func (self *TerminalList) ScrollHalfPageUp() { 143 | self.ScrollAmount(-int(FloorFloat64(float64(self.Inner.Dy()) / 2))) 144 | } 145 | 146 | func (self *TerminalList) ScrollHalfPageDown() { 147 | self.ScrollAmount(int(FloorFloat64(float64(self.Inner.Dy()) / 2))) 148 | } 149 | 150 | func (self *TerminalList) ScrollTop() { 151 | self.SelectedRow = 0 152 | } 153 | 154 | func (self *TerminalList) ScrollBottom() { 155 | self.SelectedRow = len(self.Rows) - 1 156 | } 157 | 158 | func TerminalParseStyles(s string, defaultStyle Style) []Cell { 159 | cells := []Cell{} 160 | runes := []rune(s) 161 | state := parserStateDefault 162 | styledText := []rune{} 163 | styleItems := []rune{} 164 | squareCount := 0 165 | 166 | reset := func() { 167 | styledText = []rune{} 168 | styleItems = []rune{} 169 | state = parserStateDefault 170 | squareCount = 0 171 | } 172 | 173 | rollback := func() { 174 | cells = append(cells, RunesToStyledCells(styledText, defaultStyle)...) 175 | cells = append(cells, RunesToStyledCells(styleItems, defaultStyle)...) 176 | reset() 177 | } 178 | 179 | // chop first and last runes 180 | chop := func(s []rune) []rune { 181 | return s[1 : len(s)-1] 182 | } 183 | for i, _rune := range runes { 184 | switch state { 185 | case parserStateDefault: 186 | if _rune == TokenBeginStyledText { 187 | state = parserStateStyledText 188 | squareCount = 1 189 | styledText = append(styledText, _rune) 190 | } else { 191 | cells = append(cells, Cell{Rune: _rune, Style: defaultStyle}) 192 | } 193 | case parserStateStyledText: 194 | switch { 195 | case squareCount == 0: 196 | switch _rune { 197 | case TokenBeginStyle: 198 | state = parserStateStyleItems 199 | styleItems = append(styleItems, _rune) 200 | default: 201 | rollback() 202 | switch _rune { 203 | case TokenBeginStyledText: 204 | state = parserStateStyledText 205 | squareCount = 1 206 | styleItems = append(styleItems, _rune) 207 | default: 208 | cells = append(cells, Cell{Rune: _rune, Style: defaultStyle}) 209 | } 210 | } 211 | case len(runes) == i+1: 212 | rollback() 213 | styledText = append(styledText, _rune) 214 | case _rune == TokenBeginStyledText: 215 | squareCount++ 216 | styledText = append(styledText, _rune) 217 | case _rune == TokenEndStyledText: 218 | squareCount-- 219 | styledText = append(styledText, _rune) 220 | default: 221 | styledText = append(styledText, _rune) 222 | } 223 | case parserStateStyleItems: 224 | styleItems = append(styleItems, _rune) 225 | if _rune == TokenEndStyle { 226 | style := readStyle(chop(styleItems), defaultStyle) 227 | cells = append(cells, RunesToStyledCells(chop(styledText), style)...) 228 | reset() 229 | } else if len(runes) == i+1 { 230 | rollback() 231 | } 232 | } 233 | } 234 | 235 | return cells 236 | } 237 | 238 | var modifierMap = map[string]Modifier{ 239 | "bold": ModifierBold, 240 | "underline": ModifierUnderline, 241 | "reverse": ModifierReverse, 242 | } 243 | 244 | // readStyle translates an []rune like `fg:red,mod:bold,bg:white` to a style 245 | func readStyle(runes []rune, defaultStyle Style) Style { 246 | style := defaultStyle 247 | split := strings.Split(string(runes), tokenItemSeparator) 248 | for _, item := range split { 249 | pair := strings.Split(item, tokenValueSeparator) 250 | if len(pair) == 2 { 251 | switch pair[0] { 252 | case tokenFg: 253 | style.Fg = StyleParserColorMap[pair[1]] 254 | case tokenBg: 255 | style.Bg = StyleParserColorMap[pair[1]] 256 | case tokenModifier: 257 | style.Modifier = modifierMap[pair[1]] 258 | } 259 | } 260 | } 261 | return style 262 | } 263 | 264 | func StyleText(text string, style string) string { 265 | if style != "" { 266 | return string(TokenBeginStyledText) + text + string(TokenEndStyledText) + string(TokenBeginStyle) + style + string(TokenEndStyle) 267 | } 268 | return text 269 | 270 | } 271 | -------------------------------------------------------------------------------- /ui/widgets/textbox.go: -------------------------------------------------------------------------------- 1 | package widgets 2 | 3 | import ( 4 | "github.com/gizak/termui/v3" 5 | "image" 6 | ) 7 | 8 | // TextBox main text box struct. 9 | type TextBox struct { 10 | termui.Block 11 | WrapText bool 12 | TextStyle termui.Style 13 | CursorStyle termui.Style 14 | ShowCursor bool 15 | ID string 16 | text [][]termui.Cell 17 | cursorPoint image.Point 18 | } 19 | 20 | // TextBoxTheme text box theme. 21 | var TextBoxTheme = TextBoxThemeType{ 22 | Text: termui.NewStyle(termui.ColorWhite), 23 | Cursor: termui.NewStyle(termui.ColorWhite, termui.ColorClear, termui.ModifierReverse), 24 | } 25 | 26 | // TextBoxThemeType theme type. 27 | type TextBoxThemeType struct { 28 | Text termui.Style 29 | Cursor termui.Style 30 | } 31 | 32 | // NewTextBox create new text box. 33 | func NewTextBox() *TextBox { 34 | return &TextBox{ 35 | Block: *termui.NewBlock(), 36 | WrapText: false, 37 | TextStyle: TextBoxTheme.Text, 38 | CursorStyle: TextBoxTheme.Cursor, 39 | 40 | text: [][]termui.Cell{[]termui.Cell{}}, 41 | cursorPoint: image.Pt(1, 1), 42 | } 43 | } 44 | 45 | // Draw implements the Drawable interface. 46 | func (tb *TextBox) Draw(buf *termui.Buffer) { 47 | tb.Block.Draw(buf) 48 | 49 | yCoordinate := 0 50 | for _, line := range tb.text { 51 | if tb.WrapText { 52 | line = termui.WrapCells(line, uint(tb.Inner.Dx())) 53 | } 54 | lines := termui.SplitCells(line, '\n') 55 | for _, line := range lines { 56 | for _, cx := range termui.BuildCellWithXArray(line) { 57 | x, cell := cx.X, cx.Cell 58 | buf.SetCell(cell, image.Pt(x, yCoordinate).Add(tb.Inner.Min)) 59 | } 60 | yCoordinate++ 61 | } 62 | if yCoordinate > tb.Inner.Max.Y { 63 | break 64 | } 65 | } 66 | 67 | if tb.ShowCursor { 68 | point := tb.cursorPoint.Add(tb.Inner.Min).Sub(image.Pt(1, 1)) 69 | cell := buf.GetCell(point) 70 | cell.Style = tb.CursorStyle 71 | buf.SetCell(cell, point) 72 | } 73 | } 74 | 75 | // Backspace remove previous char. 76 | func (tb *TextBox) Backspace() { 77 | if tb.cursorPoint == image.Pt(1, 1) { 78 | return 79 | } 80 | if tb.cursorPoint.X == 1 { 81 | index := tb.cursorPoint.Y - 1 82 | tb.cursorPoint.X = len(tb.text[index-1]) + 1 83 | tb.text = append( 84 | tb.text[:index-1], 85 | append( 86 | [][]termui.Cell{append(tb.text[index-1], tb.text[index]...)}, 87 | tb.text[index+1:len(tb.text)]..., 88 | )..., 89 | ) 90 | tb.cursorPoint.Y-- 91 | } else { 92 | index := tb.cursorPoint.Y - 1 93 | tb.text[index] = append( 94 | tb.text[index][:tb.cursorPoint.X-2], 95 | tb.text[index][tb.cursorPoint.X-1:]..., 96 | ) 97 | tb.cursorPoint.X-- 98 | } 99 | } 100 | 101 | // InsertText inserts the given text at the cursor position. 102 | func (tb *TextBox) InsertText(input string) { 103 | cells := termui.ParseStyles(input, tb.TextStyle) 104 | lines := termui.SplitCells(cells, '\n') 105 | index := tb.cursorPoint.Y - 1 106 | cellsAfterCursor := tb.text[index][tb.cursorPoint.X-1:] 107 | tb.text[index] = append(tb.text[index][:tb.cursorPoint.X-1], lines[0]...) 108 | for i, line := range lines[1:] { 109 | index := tb.cursorPoint.Y + i 110 | tb.text = append(tb.text[:index], append([][]termui.Cell{line}, tb.text[index:]...)...) 111 | } 112 | tb.cursorPoint.Y += len(lines) - 1 113 | index = tb.cursorPoint.Y - 1 114 | tb.text[index] = append(tb.text[index], cellsAfterCursor...) 115 | if len(lines) > 1 { 116 | tb.cursorPoint.X = len(lines[len(lines)-1]) + 1 117 | } else { 118 | tb.cursorPoint.X += len(lines[0]) 119 | } 120 | } 121 | 122 | // ClearText clears the text and resets the cursor position. 123 | func (tb *TextBox) ClearText() { 124 | tb.text = [][]termui.Cell{[]termui.Cell{}} 125 | tb.cursorPoint = image.Pt(1, 1) 126 | } 127 | 128 | // SetText sets the text to the given text. 129 | func (tb *TextBox) SetText(input string) { 130 | tb.ClearText() 131 | tb.InsertText(input) 132 | } 133 | 134 | // GetText get text from box. 135 | func (tb *TextBox) GetText() string { 136 | var text string 137 | for _, r := range tb.text { 138 | for _, t := range r { 139 | text += string(t.Rune) 140 | } 141 | } 142 | return text 143 | } 144 | 145 | // MoveCursorLeft move cursor to left. 146 | func (tb *TextBox) MoveCursorLeft() { 147 | tb.MoveCursor(tb.cursorPoint.X-1, tb.cursorPoint.Y) 148 | } 149 | 150 | // MoveCursorRight move cursor to right. 151 | func (tb *TextBox) MoveCursorRight() { 152 | tb.MoveCursor(tb.cursorPoint.X+1, tb.cursorPoint.Y) 153 | } 154 | 155 | // MoveCursorUp move cursor to up. 156 | func (tb *TextBox) MoveCursorUp() { 157 | tb.MoveCursor(tb.cursorPoint.X, tb.cursorPoint.Y-1) 158 | } 159 | 160 | // MoveCursorDown move cursor to down. 161 | func (tb *TextBox) MoveCursorDown() { 162 | tb.MoveCursor(tb.cursorPoint.X, tb.cursorPoint.Y+1) 163 | } 164 | 165 | // MoveCursor move cursor to coordinates. 166 | func (tb *TextBox) MoveCursor(x, y int) { 167 | tb.cursorPoint.Y = termui.MinInt(termui.MaxInt(1, y), len(tb.text)) 168 | tb.cursorPoint.X = termui.MinInt(termui.MaxInt(1, x), len(tb.text[tb.cursorPoint.Y-1])+1) 169 | } 170 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var Version = "1.2.0" 4 | --------------------------------------------------------------------------------