├── .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 | [](https://github.com/daylioti/docker-commander/releases/latest)
2 | [](https://goreportcard.com/report/github.com/daylioti/docker-commander)
3 | [](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 |
--------------------------------------------------------------------------------