diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..0366bb4a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,27 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG]"
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/help-topic.md b/.github/ISSUE_TEMPLATE/help-topic.md
new file mode 100644
index 00000000..22534579
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/help-topic.md
@@ -0,0 +1,14 @@
+---
+name: Help Topic
+about: Describe this issue template's purpose here.
+title: HELP!!!
+labels: help wanted
+assignees: ''
+
+---
+
+**Description**
+A clear and concise description of the topic with which you want help.
+
+**Additional information**
+Add any other context about the query here.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..f402a099
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,8 @@
+# Odin version(x.y.z)
+
+
+## Features
+
+
+## Resources
+
diff --git a/.github/workflows/go-lint.yaml b/.github/workflows/go-lint.yaml
new file mode 100644
index 00000000..b0dfdbea
--- /dev/null
+++ b/.github/workflows/go-lint.yaml
@@ -0,0 +1,19 @@
+name: golangci-lint
+on:
+ pull_request:
+ branches:
+ - main
+ - development
+permissions:
+ contents: read
+jobs:
+ golangci:
+ name: lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v2
+ with:
+ version: v1.43.0
+ args: -E gofmt -E gci
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 00000000..d369bb21
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,64 @@
+name: odin-release
+on:
+ pull_request:
+ types: [closed]
+jobs:
+ release:
+ name: odin-release
+ if: github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - id: changed_files
+ uses: jitterbit/get-changed-files@v1
+ - name: Check commit details
+ id: commit
+ run: |
+ echo "::set-output name=version::$(go run cmd/getVersion.go)"
+ echo "::set-output name=changelog::$(git log --pretty='- %s')"
+ - name: Create Tag
+ uses: negz/create-tag@v1
+ with:
+ version: ${{ steps.commit.outputs.version }}
+ message: ${{ steps.commit.outputs.version }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ - uses: actions/setup-go@v2
+ with:
+ go-version: '1.17.1'
+ - name: Create Release Asset
+ run: |
+ make compressed-builds
+ - name: Generate Asset SHA256
+ id: sha256
+ run: |
+ echo "::set-output name=darwin_amd64::$(shasum -a 256 bin/odin_darwin_amd64.tar.gz | cut -d' ' -f1)"
+ echo "::set-output name=darwin_arm64::$(shasum -a 256 bin/odin_darwin_arm64.tar.gz | cut -d' ' -f1)"
+ echo "::set-output name=linux_amd64::$(shasum -a 256 bin/odin_linux_amd64.tar.gz | cut -d' ' -f1)"
+ - name: Create Release
+ uses: ncipollo/release-action@v1
+ with:
+ name: Release ${{ steps.commit.outputs.version }}
+ tag: ${{ steps.commit.outputs.version }}
+ artifacts: "bin/*.tar.gz"
+ body: |
+ ### Changelog:
+ ${{ steps.commit.outputs.changelog }}
+ ---
+ ### Assets
+
+ #### Darwin AMD64
+ - Asset Url: https://github.com/dream11/odin/releases/download/${{ steps.commit.outputs.version }}/odin_darwin_amd64.tar.gz
+ - SHA Checksum: `${{ steps.sha256.outputs.darwin_amd64 }}`
+
+ #### Darwin ARM64
+ - Asset Url: https://github.com/dream11/odin/releases/download/${{ steps.commit.outputs.version }}/odin_darwin_arm64.tar.gz
+ - SHA Checksum: `${{ steps.sha256.outputs.darwin_arm64 }}`
+
+ #### Linux AMD64
+ - Asset Url: https://github.com/dream11/odin/releases/download/${{ steps.commit.outputs.version }}/odin_linux_amd64.tar.gz
+ - SHA Checksum: `${{ steps.sha256.outputs.linux_amd64 }}`
+
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 66fd13c9..66f7038c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,13 @@
*.out
# Dependency directories (remove the comment below to include it)
+bin
# vendor/
+
+# Editor files
+*.code-workspace
+*.idea/
+
+# Service definition files
+service.yaml
+service.json
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..5694e53a
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,11 @@
+repos:
+ - repo: git://github.com/golangci/golangci-lint
+ rev: v1.43.0
+ hooks:
+ - id: golangci-lint
+ name: golangci-lint
+ description: Fast linters runner for Go.
+ entry: golangci-lint run -E gofmt -E gci --fix
+ types: [ go ]
+ language: golang
+ pass_filenames: false
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 00000000..ba6dab3b
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,36 @@
+/* groovylint-disable DuplicateStringLiteral, LineLength, NestedBlockDepth, ParameterCount */
+@Library('d11-jenkins-lib@master') _
+pipeline {
+ agent {
+ label "devx-auto"
+ }
+
+ options {
+ ansiColor('xterm')
+ }
+
+ stages {
+ stage('CheckoutCode') {
+ steps {
+ script {
+ cleanWs()
+ checkout scm
+ }
+ }
+ }
+
+ stage('Installation') {
+ steps {
+ script {
+ sh """
+ make install
+ go build .
+ sudo mv ./odin /usr/local/bin
+ odin --version
+ """
+ }
+ }
+ }
+ }
+}
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..53b8816b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+install:
+ go mod download
+ go build .
+ sudo mv ./odin /usr/local/bin
+ odin --version
+
+lint:
+ golangci-lint run -E gofmt -E gci --fix;
+
+build:
+ go mod download
+ mkdir -p bin/odin_darwin_amd64
+ env GOOS=darwin GOARCH=amd64 go build -o bin/odin_darwin_amd64/odin
+ mkdir -p bin/odin_darwin_arm64
+ env GOOS=darwin GOARCH=arm64 go build -o bin/odin_darwin_arm64/odin
+ mkdir -p bin/odin_linux_amd64
+ env GOOS=linux GOARCH=amd64 go build -o bin/odin_linux_amd64/odin
+
+compressed-builds: build
+ cd bin/odin_darwin_amd64 && tar -czvf ../odin_darwin_amd64.tar.gz odin
+ cd bin/odin_darwin_arm64 && tar -czvf ../odin_darwin_arm64.tar.gz odin
+ cd bin/odin_linux_amd64 && tar -czvf ../odin_linux_amd64.tar.gz odin
diff --git a/README.md b/README.md
index 7f1381b8..b41231c3 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,176 @@
-# d11-cli
-Internal framework cli for assistance in artifact creation to production
+#
+
+Internal framework CLI for CRUD operations on environments.
+
+## Installation
+
+```shell
+export HOMEBREW_GITHUB_API_TOKEN=
+brew install dream11/tools/odin
+```
+
+## Development Setup
+
+1. [Download](https://golang.org/dl/go1.17.1.darwin-amd64.pkg) and run the Go installer.
+
+2. Verify Go: `go version`
+
+3. Clone this repo: `git clone https://github.com/dream11/odin`
+
+4. Enter the repository: `cd odin`
+
+5. Install dependencies: `go mod download`
+
+6. Verify the cli: `go run main.go --version`
+
+### Build & Install
+
+1. Run `make install`
+
+Or,
+
+1. Build the executable: `go build .`
+
+2. Move to binary to system path: `sudo mv ./odin /usr/local/bin`
+
+3. Verify the cli: `odin --version`
+
+## Contribution guide
+
+### Code conventions
+
+1. All variables and functions to be named as per Go's standards (camel case).
+ 1. Only the variables & functions, that are to be used across packages should be named in exported convention `ExportedName`, rest all names should be in unexported convention `unexportedName`.
+ 2. All defined command line parameters should follow the following convention - `parameter-name`
+ Example:
+ ```go
+ envType := flagSet.String("env-type", "kube", "environment type to attach with environment")
+ serviceName := flagSet.String("name", "", "name of service to be used")
+ ```
+
+2. The project must follow the following [layout](https://github.com/golang-standards/project-layout).
+
+### Formatting the code
+
+1. Install Lint tool: `brew install golangci-lint`
+
+2. Upgrade to its latest version: `brew upgrade golangci-lint`
+
+3. Run linter: `make lint`
+
+> All these linting checks are also ensured in pre-commit checks provided below.
+
+### Making commits
+
+1. Install pre-commit: `pip install pre-commit`
+
+2. Setup pre-commit: `cd odin && pre-commit install`
+
+3. Now, make commits.
+
+> Now on every commit that you make, pre-commit hook will validate the `go` code and will suggest changes if any.
+
+### Managing the version
+
+Odin's application version is in the semantic version form i.e. `x.y.z` where,
+`x` is the major version, `y` is the minor version and `z` is the patch version.
+
+The version is maintained in [odin/app/app.go](./app/app.go) inside variable named `App`.
+
+Example: if the current version is `1.0.0`, then in case of
+
+1. a bug fix or a patch, the patch version is upgraded i.e. `1.0.1`
+2. a minor change in some existing feature, the minor version is upgraded i.e. `1.1.0`
+3. a major feature addition, the major version is upgraded i.e. `2.0.0`
+
+> Update the version responsibly as this version will be used to create a release against any master/main branch merge.
+
+## Commands
+
+### Structure
+
+All commands are formatted as: odin `` `` ``
+
+Here,
+
+1. `verb` - The action to be performed. Supported verbs are -
+
+ - `create` - For creating/uploading a resource record for future access.
+ - `delete` - For deleting a created resource record.
+ - `update` - For updating
+ - `label` - For attaching a label to a resource.
+ - `list` - For listing all the active entities of that resource.
+ - `describe` - For describing a particular resource.
+ - `status` - For current status of a particular resource
+ - `logs` - For execution logs of resource
+ - `deploy` - For deploying the resource on actual infrastructure.
+ - `destroy` - For destroying a deployed resource.
+
+2. `resource` - The resource on which action will be performed. Example: `env` -
+
+ ```shell
+ odin environment
+ ```
+
+3. `options` - Extra properties required to support the commands. Example: `env` -
+
+ ```shell
+ odin environment --name=demo-1234
+ ```
+
+### [Adding a command](./docs/ADD_COMMAND.md)
+
+### [Hidden commands](./docs/HIDDEN_COMMAND.md)
+
+## Command Line User Interface
+
+To interact with user via Command Line,
+
+```go
+import (
+ "github.com/dream11/odin/internal/ui"
+)
+```
+
+### Logging
+
+```go
+var Logger ui.Logger
+
+func main() {
+ Logger.Info("string")
+ Logger.Success("string")
+ Logger.Warn("string")
+ Logger.Output("string")
+ Logger.Debug("string")
+ Logger.Error("string") // This should be followed by an exit call
+}
+```
+
+> To enable debug logs, set an environment variable `ODIN_DEBUG=yes` & unset to disable if already set.
+
+### Inputs
+
+```go
+var Input ui.Logger
+
+func main() {
+ text := Input.Ask("Input text")
+ secretText := Input.AskSecret("Input secret text")
+}
+```
+
+> For using these interfaces(logging & inputs) within commands, these are available as part of the declared command struct. Follow step 2 [here](./docs/ADD_COMMAND.md).
+
+## Exit status
+
+Define appropriate exit status for commands based on the success/errors,
+
+| Exit Code | Description |
+| --------- | ----------- |
+| 0 | Exit status 0 denotes that your command ran successfully |
+| 1 | Exit status 1 corresponds to any generic error |
+| 2 | Exit status 2 is a permission denied error |
+| 126 | Exit status 126 is an interesting permissions error code. |
+| 127 | Exit status 127 tells you that one of two things has happened: Either the command doesn't exist, or the command isn't in your path `$PATH` |
+| 128 | Exit status 128 is the response received when an out-of-range exit code is used in programming. |
diff --git a/api/auth/auth.go b/api/auth/auth.go
new file mode 100644
index 00000000..323a1fb6
--- /dev/null
+++ b/api/auth/auth.go
@@ -0,0 +1,8 @@
+package auth
+
+// Auth interface
+type Auth struct {
+ AccessToken string `yaml:"access_token,omitempty" json:"access_token,omitempty"`
+ RefreshToken string `yaml:"refresh_token,omitempty" json:"refresh_token,omitempty"`
+ Expired bool `yaml:"expired,omitempty" json:"expired,omitempty"`
+}
diff --git a/api/component/component.go b/api/component/component.go
new file mode 100644
index 00000000..ef423099
--- /dev/null
+++ b/api/component/component.go
@@ -0,0 +1,44 @@
+package component
+
+// Component interface
+type Component struct {
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Type string `yaml:"type,omitempty" json:"type,omitempty"`
+ Version string `yaml:"version,omitempty" json:"version,omitempty"`
+ Config interface{} `yaml:"config,omitempty" json:"config,omitempty"`
+ Deployment interface{} `yaml:"deployment_config,omitempty" json:"deployment_config,omitempty"`
+ Scaling interface{} `yaml:"scaling_config,omitempty" json:"scaling_config,omitempty"`
+ Discovery interface{} `yaml:"discovery_config,omitempty" json:"discovery_config,omitempty"`
+ DeploymentPlatformMapping interface{} `yaml:"deployment_platform_mapping,omitempty" json:"deployment_platform_mapping,omitempty"`
+}
+
+// Type interface
+type Type struct {
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Version string `yaml:"version,omitempty" json:"version,omitempty"`
+ TotalVersions int `yaml:"totalVersions,omitempty" json:"totalVersions,omitempty"`
+ CreatedBy string `yaml:"createdBy,omitempty" json:"createdBy,omitempty"`
+ UpdatedBy string `yaml:"updatedBy,omitempty" json:"updatedBy,omitempty"`
+ CreatedAt string `yaml:"createdAt,omitempty" json:"createdAt,omitempty"`
+ UpdatedAt string `yaml:"updatedAt,omitempty" json:"updatedAt,omitempty"`
+ Config interface{} `yaml:"config,omitempty" json:"config,omitempty"`
+ Deployment interface{} `yaml:"deployment_config,omitempty" json:"deployment_config,omitempty"`
+ Scaling interface{} `yaml:"scaling_config,omitempty" json:"scaling_config,omitempty"`
+ Discovery interface{} `yaml:"discovery_config,omitempty" json:"discovery_config,omitempty"`
+ DeploymentPlatformMapping interface{} `yaml:"deployment_platform_mapping,omitempty" json:"deployment_platform_mapping,omitempty"`
+}
+
+// ListTypeResponse interface
+type ListTypeResponse struct {
+ Response []Type `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// ListTypeResponse interface
+type DetailComponentTypeResponse struct {
+ Response Type `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// ListTypeResponse interface
+type DetailComponentResponse struct {
+ Response Component `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
diff --git a/api/configuration/configuration.go b/api/configuration/configuration.go
new file mode 100644
index 00000000..6526e763
--- /dev/null
+++ b/api/configuration/configuration.go
@@ -0,0 +1,15 @@
+package configuration
+
+// SecretKeys interface
+type SecretKeys struct {
+ AccessKey string `yaml:"access_key,omitempty" json:"access_key,omitempty"`
+ SecretAccessKey string `yaml:"secret_access_key,omitempty" json:"secret_access_key,omitempty"`
+}
+
+// Configuration interface
+type Configuration struct {
+ BackendAddr string `yaml:"backend_addr,omitempty" json:"backend_addr,omitempty"`
+ Keys SecretKeys
+ AccessToken string `yaml:"access_token,omitempty" json:"access_token,omitempty"`
+ RefreshToken string `yaml:"refresh_token,omitempty" json:"refresh_token,omitempty"`
+}
diff --git a/api/environment/env.go b/api/environment/env.go
new file mode 100644
index 00000000..9deae7fc
--- /dev/null
+++ b/api/environment/env.go
@@ -0,0 +1,89 @@
+package environment
+
+import (
+ "github.com/dream11/odin/api/service"
+)
+
+// Env interface
+type Env struct {
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Team string `yaml:"team,omitempty" json:"team,omitempty"`
+ Purpose string `yaml:"purpose,omitempty" json:"purpose,omitempty"`
+ EnvType string `yaml:"envType,omitempty" json:"envType,omitempty"`
+ State string `yaml:"state,omitempty" json:"state,omitempty"`
+ DeletionTime string `yaml:"autoDeletionTime,omitempty" json:"autoDeletionTime,omitempty"`
+ Account string `yaml:"cloudProviderAccount,omitempty" json:"cloudProviderAccount,omitempty"`
+ CreatedBy string `yaml:"createdBy,omitempty" json:"createdBy,omitempty"`
+ UpdatedBy string `yaml:"updatedBy,omitempty" json:"updatedBy,omitempty"`
+ CreatedAt string `yaml:"createdAt,omitempty" json:"createdAt,omitempty"`
+ UpdatedAt string `yaml:"updatedAt,omitempty" json:"updatedAt,omitempty"`
+ Config interface{} `yaml:"config,omitempty" json:"config,omitempty"`
+ MetaInfo interface{} `yaml:"meta_info,omitempty" json:"meta_info,omitempty"`
+ Cost string `yaml:"cost,omitempty" json:"cost,omitempty"`
+ Organization string `yaml:"organization,omitempty" json:"organization,omitempty"`
+ Services []service.Service `yaml:"services,omitempty" json:"services,omitempty"`
+}
+
+type History struct {
+ ID int `yaml:"id,omitempty" json:"id,omitempty"`
+ CreatedBy string `yaml:"modifiedBy,omitempty" json:"createdBy,omitempty"`
+ CreatedAt string `yaml:"lastModified,omitempty" json:"createdAt,omitempty"`
+ EnvId string `yaml:"envName,omitempty" json:"envId,omitempty"`
+ Action string `yaml:"action,omitempty" json:"action,omitempty"`
+ ResourceDetails string `yaml:"resourceDetails,omitempty" json:"resourceDetails,omitempty"`
+ State string `yaml:"state,omitempty" json:"state,omitempty"`
+ AutoDeletionTime string `yaml:"autoDeletionTime,omitempty" json:"autoDeletionTime,omitempty"`
+ EnvConfig interface{} `yaml:"envConfig,omitempty" json:"envConfig,omitempty"`
+}
+
+// CreationResponse interface
+type CreationResponse struct {
+ Response Env `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// ListResponse interface
+type ListResponse struct {
+ Response []Env `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// HistoryListResponse interface
+type HistoryListResponse struct {
+ Response []History `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// EnvStatusResponse interface
+type EnvStatusResponse struct {
+ EnvResponse EnvStatus `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+type EnvServiceStatusResponse struct {
+ ServiceResponse EnvServiceStatus `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+type EnvServiceStatus struct {
+ LastDeployedAt string `yaml:"lastDeployedAt,omitempty" json:"lastDeployedAt,omitempty"`
+ Version string `yaml:"version,omitempty" json:"version,omitempty"`
+ Components []Status `yaml:"components,omitempty" json:"components,omitempty"`
+}
+
+type EnvStatus struct {
+ ServiceStatus []EnvStatusPerService `yaml:"services,omitempty" json:"services,omitempty"`
+}
+
+type EnvStatusPerService struct {
+ Status string `yaml:"status,omitempty" json:"status,omitempty"`
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Version string `yaml:"version,omitempty" json:"version,omitempty"`
+ LastDeployedAt string `yaml:"lastDeployedAt,omitempty" json:"lastDeployedAt,omitempty"`
+}
+
+type Status struct {
+ Status string `yaml:"status,omitempty" json:"status,omitempty"`
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Version string `yaml:"version,omitempty" json:"version,omitempty"`
+}
+
+// DetailResponse interface
+type DetailResponse struct {
+ Response Env `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
diff --git a/api/error/error.go b/api/error/error.go
new file mode 100644
index 00000000..662e83f4
--- /dev/null
+++ b/api/error/error.go
@@ -0,0 +1,6 @@
+package error
+
+// Error interface
+type Error struct {
+ Error string `yaml:"err,omitempty" json:"err,omitempty"`
+}
diff --git a/api/service/service.go b/api/service/service.go
new file mode 100644
index 00000000..50a7ae2d
--- /dev/null
+++ b/api/service/service.go
@@ -0,0 +1,43 @@
+package service
+
+import (
+ "github.com/dream11/odin/api/component"
+)
+
+// Service interface
+type Service struct {
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Version string `yaml:"version,omitempty" json:"version,omitempty"`
+ Team []string `yaml:"team,omitempty" json:"team,omitempty"`
+ Description string `yaml:"description,omitempty" json:"description,omitempty"`
+ Mature *bool `yaml:"isMature,omitempty" json:"isMature,omitempty"`
+ CreatedBy string `yaml:"createdBy,omitempty" json:"createdBy,omitempty"`
+ UpdatedBy string `yaml:"updatedBy,omitempty" json:"updatedBy,omitempty"`
+ CreatedAt string `yaml:"createdAt,omitempty" json:"createdAt,omitempty"`
+ UpdatedAt string `yaml:"updatedAt,omitempty" json:"updatedAt,omitempty"`
+ Active *bool `yaml:"isActive,omitempty" json:"isActive,omitempty"`
+ Tags interface{} `yaml:"tags,omitempty" json:"tags,omitempty"`
+ Components []component.Component `yaml:"components,omitempty" json:"components,omitempty"`
+}
+
+// ListResponse interface
+type ListResponse struct {
+ Response []Service `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// DetailResponse interface
+type DetailResponse struct {
+ Response Service `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
+
+// Status interface
+type Status struct {
+ Name string `yaml:"name,omitempty" json:"name,omitempty"`
+ Ec2 string `yaml:"ec2,omitempty" json:"ec2,omitempty"`
+ Docker string `yaml:"docker,omitempty" json:"docker,omitempty"`
+}
+
+// StatusResponse interface
+type StatusResponse struct {
+ Response []Status `yaml:"resp,omitempty" json:"resp,omitempty"`
+}
diff --git a/app/app.go b/app/app.go
new file mode 100644
index 00000000..d2891f2f
--- /dev/null
+++ b/app/app.go
@@ -0,0 +1,12 @@
+package app
+
+type application struct {
+ Name string
+ Version string
+}
+
+// App (Application) interface
+var App application = application{
+ Name: "odin",
+ Version: "1.0.0-alpha",
+}
diff --git a/app/structure.go b/app/structure.go
new file mode 100644
index 00000000..fc117a9e
--- /dev/null
+++ b/app/structure.go
@@ -0,0 +1,57 @@
+package app
+
+import (
+ "os"
+ "path"
+ "strings"
+
+ "github.com/dream11/odin/internal/ui"
+ "github.com/dream11/odin/pkg/dir"
+)
+
+type workdir struct {
+ Location string
+ ConfigFile string
+ EnvVarPrefix string
+}
+
+// Create : Creates the required working directory
+func (w *workdir) Create() error {
+ wExists, err := dir.Exists(w.Location)
+ if err != nil {
+ return err
+ }
+
+ if wExists {
+ return nil
+ }
+
+ return dir.Create(w.Location, 0755)
+}
+
+// WorkDir interface
+var WorkDir = workdir{
+ Location: path.Join(os.Getenv("HOME"), "."+App.Name),
+ ConfigFile: "config",
+ EnvVarPrefix: strings.ToUpper(App.Name) + "_",
+}
+
+var logger ui.Logger
+
+// initiate dir structure on app initialization
+func init() {
+ err := WorkDir.Create()
+ if err != nil {
+ logger.Error(err.Error())
+ os.Exit(1)
+ }
+
+ secretCredentialsExist, err := dir.Exists(path.Join(WorkDir.Location, WorkDir.ConfigFile))
+ if err != nil {
+ logger.Error(err.Error())
+ }
+
+ if !secretCredentialsExist {
+ logger.Warn("Run, `odin configure` to configure odin")
+ }
+}
diff --git a/bin/README.md b/bin/README.md
new file mode 100644
index 00000000..e4f33985
--- /dev/null
+++ b/bin/README.md
@@ -0,0 +1,3 @@
+# odin/bin
+
+Target dir for `make build` command
\ No newline at end of file
diff --git a/cmd/getVersion.go b/cmd/getVersion.go
new file mode 100644
index 00000000..bc018ab0
--- /dev/null
+++ b/cmd/getVersion.go
@@ -0,0 +1,11 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/dream11/odin/app"
+)
+
+func main() {
+ fmt.Printf(app.App.Version)
+}
diff --git a/docs/ADD_COMMAND.md b/docs/ADD_COMMAND.md
new file mode 100644
index 00000000..ea6886f1
--- /dev/null
+++ b/docs/ADD_COMMAND.md
@@ -0,0 +1,239 @@
+# Adding a command `test` with all its `verbs`
+
+1. Create [test.go](../internal/command/commands/test.go) at `odin/internal/command/commands/` and initiate it,
+
+ ```go
+ package commands
+ ```
+
+2. Import the necessary packages and initiate the command struct,
+
+ ```go
+ import (
+ "os"
+ "flag"
+ "fmt"
+ )
+
+ type Test command
+ ```
+
+ Command initiation using the `command` type, inherits the `verbs` and `cli interface` from it. Using these, when writing a command in `func (t *Test) Run(args []string) int{}`
+
+ `t` can be used to fetch the following verbs -
+
+ - `t.Create` (boolean)
+ - `t.Delete` (boolean)
+ - `t.Describe` (boolean)
+ - `t.List` (boolean)
+ - `t.Status` (boolean)
+ - `t.Logs` (boolean)
+ - `t.Deploy` (boolean)
+ - `t.Destroy` (boolean)
+
+ and these interfaces -
+
+ - `t.Logger`
+ - `t.Logger.Info(string)`
+ - `t.Logger.Success(string)`
+ - `t.Logger.Warn(string)`
+ - `t.Logger.Error(string)`
+ - `t.Logger.Output(string)`
+ - `t.Logger.Debug(string)`
+ - `t.Input`
+ - `t.Input.Ask(string)`
+ - `t.Input.AskSecret(string)`
+
+3. Initiate the required functions for command,
+
+ - `Run(args []string) int {}` - Accepts a list of arguments and return an exit code after processing command based on verbs.
+
+ ```go
+ // Run implements the actual functionality of the command
+ // and return exit codes based on success/failure of tasks performed
+ func (t *Test) Run(args []string) int {
+ // Define a custom flagset
+ flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError)
+
+ // Add required flags to the defined flagset
+ testFlag := flagSet.String("test-flag", "default value", "Help text")
+ // parse the passed flags
+ flagSet.Parse(args)
+ // use the parsed flags
+ t.Logger.Info(fmt.Sprintf("-test-flag=%s", *testFlag))
+
+ if t.Create {
+ // Perform stuff for record creation of test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(create)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.Delete {
+ // Perform stuff for record deletion of test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(delete)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.List {
+ // Perform stuff to list all test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(list)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.Describe {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(describe)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.Status {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(status)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.Logs {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(logs)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.Deploy {
+ // Perform stuff to deploy a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(deploy)! flag value = %s", *testFlag))
+ return 0
+ }
+ if t.Destroy {
+ // Perform stuff to destroy a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(destroy)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ t.Logger.Error("Not a valid command")
+ return 1
+ }
+ ```
+
+ - `Help() string {}` - Accepts nothing and returns an explanatory string.
+
+ ```go
+ // Help should return an explanatory string,
+ // that can explain the command's verbs
+ func (t *Test) Help() string {
+ if t.Create {
+ return commandHelper("create", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.Delete {
+ return commandHelper("delete", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.List {
+ return commandHelper("list", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.Describe {
+ return commandHelper("describe", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.Status {
+ return commandHelper("status", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.Logs {
+ return commandHelper("logs", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.Deploy {
+ return commandHelper("deploy", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+ if t.Destroy {
+ return commandHelper("destroy", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ return defaultHelper()
+ }
+ ```
+
+ - `Synopsis() string {}` - Accepts nothing and returns a short string.
+
+ ```go
+ // Synopsis should return a breif helper text for the command's verbs
+ func (t *Test) Synopsis() string {
+ if t.Create {
+ return "create a test resource"
+ }
+ if t.Delete {
+ return "delete a test resource"
+ }
+ if t.List {
+ return "list all test resources"
+ }
+ if t.Describe {
+ return "describe a test resource"
+ }
+ if t.Status {
+ return "current status of test resource"
+ }
+ if t.Logs {
+ return "execution logs of test resource"
+ }
+ if t.Deploy {
+ return "deploy a test resource"
+ }
+ if t.Destroy {
+ return "destroy a test resource"
+ }
+
+ return defaultHelper()
+ }
+ ```
+
+4. Create the commands at [command_catalog.go](../internal/command/command_catalog.go) at `odin/internal/command/command_catalog.go` in the `CommandCatalog()` function,
+
+ For mapping `command` to `verb`, define the command as,
+
+ ```go
+ "verb resource": func() (cli.Command, error) {
+ return &commands.Resource{Verb: true}, nil
+ }
+ ```
+
+ Like,
+
+ ```go
+ func CommandCatalog() map[string]cli.CommandFactory {
+ return map[string]cli.CommandFactory{
+ "create test": func() (cli.Command, error) {
+ return &commands.Test{Create: true}, nil
+ },
+ "delete test": func() (cli.Command, error) {
+ return &commands.Test{Delete: true}, nil
+ },
+ "list test": func() (cli.Command, error) {
+ return &commands.Test{List: true}, nil
+ },
+ "describe test": func() (cli.Command, error) {
+ return &commands.Test{Describe: true}, nil
+ },
+ "status test": func() (cli.Command, error) {
+ return &commands.Test{Status: true}, nil
+ },
+ "logs test": func() (cli.Command, error) {
+ return &commands.Test{Logs: true}, nil
+ },
+ "deploy test": func() (cli.Command, error) {
+ return &commands.Test{Deploy: true}, nil
+ },
+ "destroy test": func() (cli.Command, error) {
+ return &commands.Test{Destroy: true}, nil
+ },
+ }
+ }
+ ```
+
+> Remove the verbs which are not in scope of that resource.
diff --git a/docs/HIDDEN_COMMAND.md b/docs/HIDDEN_COMMAND.md
new file mode 100644
index 00000000..5b0d88b9
--- /dev/null
+++ b/docs/HIDDEN_COMMAND.md
@@ -0,0 +1,24 @@
+# Hiding a command from help section
+
+To hide a command and prevent it from appearing in cli's help section,
+
+1. Add a command, by following this [Adding a command doc](./ADD_COMMAND.md)
+
+2. Now, add the commands created in step 4 above as an element to `hidenCommands` variable at [cli.go](../internal/cli/cli.go)
+
+ ```go
+ var hidenCommands = []string{
+ "create test",
+ "delete test",
+ "list test",
+ "describe test",
+ "deploy test",
+ "destroy test",
+ }
+ ```
+
+3. Verify,
+
+ ```shell
+ go run main.go --help
+ ```
diff --git a/docs/odin-logo.jpg b/docs/odin-logo.jpg
new file mode 100644
index 00000000..0fa5f815
Binary files /dev/null and b/docs/odin-logo.jpg differ
diff --git a/go.mod b/go.mod
index cd5a1c17..b3803751 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,27 @@
-module github.com/dream11/d11-cli
+module github.com/dream11/odin
go 1.17
+
+require (
+ github.com/mitchellh/cli v1.1.2
+ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
+ github.com/Masterminds/goutils v1.1.1 // indirect
+ github.com/Masterminds/semver v1.5.0 // indirect
+ github.com/Masterminds/sprig v2.22.0+incompatible // indirect
+ github.com/armon/go-radix v1.0.0 // indirect
+ github.com/bgentry/speakeasy v0.1.0 // indirect
+ github.com/fatih/color v1.13.0 // indirect
+ github.com/google/uuid v1.3.0 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
+ github.com/huandu/xstrings v1.3.2 // indirect
+ github.com/imdario/mergo v0.3.12 // indirect
+ github.com/mattn/go-colorable v0.1.11 // indirect
+ github.com/mattn/go-isatty v0.0.14 // indirect
+ github.com/mitchellh/copystructure v1.2.0 // indirect
+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
+ github.com/posener/complete v1.2.3 // indirect
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
+ golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 00000000..7f255329
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,1000 @@
+bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
+github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
+github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w=
+github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
+github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
+github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
+github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
+github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
+github.com/Microsoft/hcsshim v0.8.21 h1:btRfUDThBE5IKcvI8O8jOiIkujUsAMBSRsYDYmEi6oM=
+github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
+github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
+github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
+github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
+github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
+github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
+github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
+github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
+github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
+github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
+github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
+github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
+github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
+github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
+github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
+github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
+github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
+github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
+github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
+github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
+github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM=
+github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
+github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
+github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
+github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
+github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
+github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
+github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
+github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
+github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
+github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
+github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
+github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
+github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
+github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
+github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
+github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
+github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
+github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
+github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
+github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
+github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
+github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
+github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
+github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
+github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
+github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
+github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
+github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
+github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
+github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
+github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
+github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
+github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
+github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
+github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
+github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k=
+github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
+github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
+github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
+github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
+github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
+github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
+github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
+github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
+github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
+github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
+github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
+github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
+github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
+github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg=
+github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
+github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
+github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
+github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
+github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
+github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
+github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
+github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
+go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef h1:fPxZ3Umkct3LZ8gK9nbk+DWDJ9fstZa2grBn+lWVKPs=
+golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
+google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
+google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
+k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
+k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
+k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
+k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
+k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
+k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
+k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
+k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
+k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
+k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
+k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
+k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
+k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
+k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
+k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
+k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/internal/backend/auth.go b/internal/backend/auth.go
new file mode 100644
index 00000000..5848074f
--- /dev/null
+++ b/internal/backend/auth.go
@@ -0,0 +1,52 @@
+package backend
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/dream11/odin/api/auth"
+)
+
+// Auth entity
+type Auth struct{}
+
+// GetToken : get access & refresh tokens
+func (a *Auth) GetToken(accessKey, secretAccessKey string) (auth.Auth, error) {
+ client := newClient()
+
+ reqBody := map[string]string{
+ "client_id": accessKey,
+ "client_secret": secretAccessKey,
+ }
+
+ response := client.action("secure/auth/", "POST", reqBody)
+ response.Process(true) // process response and exit if error
+
+ var authResponse auth.Auth
+ err := json.Unmarshal(response.Body, &authResponse)
+
+ return authResponse, err
+}
+
+// RefreshToken : refresh the tokens
+func (a *Auth) RefreshToken(refreshToken string) (auth.Auth, error) {
+ client := newClient()
+
+ reqBody := map[string]string{
+ "refresh_token": refreshToken,
+ }
+
+ response := client.action("secure/refreshtoken/", "POST", reqBody)
+ response.Process(true) // process response and exit if error
+
+ var authResponse []auth.Auth
+ err := json.Unmarshal(response.Body, &authResponse)
+
+ for _, token := range authResponse {
+ if !token.Expired {
+ return token, err
+ }
+ }
+
+ return auth.Auth{}, fmt.Errorf("unable to find a valid active token")
+}
diff --git a/internal/backend/client.go b/internal/backend/client.go
new file mode 100644
index 00000000..0c68f871
--- /dev/null
+++ b/internal/backend/client.go
@@ -0,0 +1,51 @@
+package backend
+
+import (
+ "github.com/dream11/odin/internal/config"
+ "github.com/dream11/odin/pkg/request"
+)
+
+// initiation of an HTTP client for backend interactions
+type clientProperties struct {
+ address string
+ Headers map[string]string
+ QueryParams map[string]string
+}
+
+// perform HTTP actions on initiated client
+func (c *clientProperties) action(entity, requestType string, body interface{}) request.Response {
+ // TODO: add auth token to required header key
+ req := request.Request{
+ Method: requestType,
+ URL: c.address + entity,
+ Query: c.QueryParams,
+ Header: c.Headers,
+ Body: body,
+ }
+
+ return req.Make()
+}
+
+// initiate a functional backend base-client
+func newClient() clientProperties {
+ var appConfig = config.Get()
+
+ return clientProperties{
+ address: appConfig.BackendAddr + "/",
+ Headers: map[string]string{
+ "Content-Type": "application/json",
+ },
+ QueryParams: map[string]string{},
+ }
+}
+
+// initiate an API integration client on top of base-client
+func newApiClient() clientProperties {
+ var appConfig = config.Get()
+
+ apiClient := newClient()
+ apiClient.address += "api/integration/cli/v2/"
+ apiClient.Headers["Authorization"] = "Bearer " + appConfig.AccessToken
+
+ return apiClient
+}
diff --git a/internal/backend/componentType.go b/internal/backend/componentType.go
new file mode 100644
index 00000000..85052b34
--- /dev/null
+++ b/internal/backend/componentType.go
@@ -0,0 +1,39 @@
+package backend
+
+import (
+ "encoding/json"
+ "path"
+
+ "github.com/dream11/odin/api/component"
+)
+
+// ComponentType entity
+type ComponentType struct{}
+
+// ListComponentTypes : list all available component types
+func (c *ComponentType) ListComponentTypes(componentTypeName, version string) ([]component.Type, error) {
+ client := newApiClient()
+ client.QueryParams["version"] = version
+ client.QueryParams["name"] = componentTypeName
+ response := client.action("componenttypes", "GET", nil)
+
+ response.Process(true) // process response and exit if error
+
+ var componentTypeResponse component.ListTypeResponse
+ err := json.Unmarshal(response.Body, &componentTypeResponse)
+
+ return componentTypeResponse.Response, err
+}
+
+// DescribeComponentTypes : describe a component type
+func (c *ComponentType) DescribeComponentType(componentTypeName, version string) (component.Type, error) {
+ client := newApiClient()
+ client.QueryParams["version"] = version
+ response := client.action(path.Join("componenttypes", componentTypeName), "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ var componentTypeResponse component.DetailComponentTypeResponse
+ err := json.Unmarshal(response.Body, &componentTypeResponse)
+
+ return componentTypeResponse.Response, err
+}
diff --git a/internal/backend/env.go b/internal/backend/env.go
new file mode 100644
index 00000000..2e779637
--- /dev/null
+++ b/internal/backend/env.go
@@ -0,0 +1,125 @@
+package backend
+
+import (
+ "encoding/json"
+ "path"
+
+ envResp "github.com/dream11/odin/api/environment"
+)
+
+// Env entity
+type Env struct{}
+
+// root entity
+var envEntity = "envs"
+
+// CreateEnv : create an empty Env
+func (e *Env) CreateEnv(envDetails interface{}) (envResp.Env, error) {
+ client := newApiClient()
+
+ response := client.action(envEntity+"/", "POST", envDetails)
+ response.Process(true) // process response and exit if error
+
+ var envResponse envResp.CreationResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.Response, err
+}
+
+// DescribeEnv : describe an Env
+func (e *Env) DescribeEnv(env, service, component string) (envResp.Env, error) {
+ client := newApiClient()
+ client.QueryParams["service"] = service
+ client.QueryParams["component"] = component
+ response := client.action(path.Join(envEntity, env)+"/", "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ var envResponse envResp.DetailResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.Response, err
+}
+
+// ListEnv : list all environment(s) with filters
+func (e *Env) ListEnv(name, team, env, providerAccount string) ([]envResp.Env, error) {
+ client := newApiClient()
+ client.QueryParams["name"] = name
+ client.QueryParams["team"] = team
+ client.QueryParams["envType"] = env
+ client.QueryParams["cloudProviderAccount"] = providerAccount
+ response := client.action(envEntity+"/", "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ var envResponse envResp.ListResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.Response, err
+}
+
+// DeleteEnv : delete an created environment
+func (e *Env) DeleteEnv(env string) {
+ client := newApiClient()
+
+ response := client.action(path.Join(envEntity, env)+"/", "DELETE", nil)
+ response.Process(true) // process response and exit if error
+}
+
+// UpdateEnv : update a created environment
+func (e *Env) UpdateEnv(env string, config interface{}) {
+ client := newApiClient()
+
+ response := client.action(path.Join(envEntity, env)+"/", "PUT", config)
+ response.Process(true) // process response and exit if error
+}
+
+// GetHistoryEnv : get historical changes in an Env
+func (e *Env) GetHistoryEnv(env string) ([]envResp.History, error) {
+ client := newApiClient()
+
+ response := client.action(path.Join("envhistory", env)+"/", "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ var envResponse envResp.HistoryListResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.Response, err
+}
+
+// DescribeHistoryEnv : describe a historical changes in an Env
+func (e *Env) DescribeHistoryEnv(env string, id string) ([]envResp.History, error) {
+ client := newApiClient()
+ client.QueryParams["id"] = id
+
+ response := client.action(path.Join("envhistory", env)+"/", "GET", nil)
+ response.Process(false) // process response and exit if error
+
+ var envResponse envResp.HistoryListResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.Response, err
+}
+
+// EnvStatus : Fetch status of the env
+func (e *Env) EnvStatus(env string) (envResp.EnvStatus, error) {
+ client := newApiClient()
+
+ response := client.action(path.Join(envEntity, env)+"/status", "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ var envResponse envResp.EnvStatusResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.EnvResponse, err
+}
+
+func (e *Env) EnvServiceStatus(env, serviceName string) (envResp.EnvServiceStatus, error) {
+ client := newApiClient()
+
+ response := client.action(path.Join(envEntity, env)+"/services/"+serviceName+"/status", "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ var envResponse envResp.EnvServiceStatusResponse
+ err := json.Unmarshal(response.Body, &envResponse)
+
+ return envResponse.ServiceResponse, err
+}
diff --git a/internal/backend/profile.go b/internal/backend/profile.go
new file mode 100644
index 00000000..dc50a9ff
--- /dev/null
+++ b/internal/backend/profile.go
@@ -0,0 +1,50 @@
+package backend
+
+import (
+ "path"
+)
+
+// Profile entity
+type Profile struct{}
+
+// root entity
+var profileEntity = "profiles"
+
+// CreateProfile : register a profile version with backend
+func (p *Profile) CreateProfile(profile interface{}) {
+ client := newApiClient()
+
+ response := client.action(profileEntity, "POST", profile)
+ response.Process(true) // process response and exit if error
+}
+
+// DescribeProfile : describe a profile version or all versions of a profile
+func (p *Profile) DescribeProfile(profile, version string) {
+ client := newApiClient()
+ client.QueryParams["version"] = version
+
+ response := client.action(path.Join(profileEntity, profile), "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ // TODO: parse response.Body into required structure and return
+}
+
+// ListProfiles : list profiles per team and describe versions
+func (p *Profile) ListProfiles(team, version string) {
+ client := newApiClient()
+ client.QueryParams["team"] = team
+ client.QueryParams["version"] = version
+
+ response := client.action(profileEntity, "GET", nil)
+ response.Process(true) // process response and exit if error
+
+ // TODO: parse response.Body into required structure and return
+}
+
+// DeleteProfile : delete a profile version
+func (p *Profile) DeleteProfile(profile, version string) {
+ client := newApiClient()
+
+ response := client.action(path.Join(profileEntity, profile, "version", version), "DELETE", nil)
+ response.Process(true) // process response and exit if error
+}
diff --git a/internal/backend/service.go b/internal/backend/service.go
new file mode 100644
index 00000000..24d28804
--- /dev/null
+++ b/internal/backend/service.go
@@ -0,0 +1,115 @@
+package backend
+
+import (
+ "encoding/json"
+ "fmt"
+ "path"
+
+ "github.com/dream11/odin/api/service"
+)
+
+// Service entity
+type Service struct{}
+
+// root entity
+var serviceEntity = "services"
+
+// CreateService : register a service version with backend
+func (s *Service) CreateService(service interface{}) {
+ client := newApiClient()
+
+ response := client.action(serviceEntity+"/", "POST", service)
+ response.Process(true) // process response and exit if error
+}
+
+// Rebuild Service : rebuild a service
+func (s *Service) RebuildService(service, version string) {
+ client := newApiClient()
+
+ response := client.action(path.Join(serviceEntity, service, "versions", version, "rebuild")+"/", "PUT", nil)
+ response.Process(true)
+}
+
+// DescribeService : describe a service version or all versions of a service
+func (s *Service) DescribeService(name, version, component string) (service.Service, error) {
+ client := newApiClient()
+ client.QueryParams["version"] = version
+ client.QueryParams["component"] = component
+ response := client.action(path.Join(serviceEntity, name), "GET", nil)
+ response.Process(true)
+
+ var serviceResponse service.DetailResponse
+ err := json.Unmarshal(response.Body, &serviceResponse)
+
+ return serviceResponse.Response, err
+}
+
+// ListServices : list services per team and describe versions
+func (s *Service) ListServices(team, version, serviceName string, maturity bool) ([]service.Service, error) {
+ client := newApiClient()
+ client.QueryParams["team"] = team
+ client.QueryParams["version"] = version
+ client.QueryParams["name"] = serviceName
+ // if maturity then only pass isMature in query params
+ if maturity {
+ client.QueryParams["isMature"] = fmt.Sprintf("%v", maturity)
+ }
+
+ response := client.action(serviceEntity, "GET", nil)
+ response.Process(true)
+
+ var serviceResponse service.ListResponse
+ err := json.Unmarshal(response.Body, &serviceResponse)
+
+ return serviceResponse.Response, err
+}
+
+// UndeployService: To remove a service from a given env
+func (s *Service) UndeployService(serviceName, env_name string) {
+ client := newApiClient()
+ client.QueryParams["env_name"] = env_name
+
+ response := client.action(path.Join(serviceEntity, "undeploy", serviceName)+"/", "DELETE", nil)
+ response.Process(true)
+
+}
+
+// DeleteService : delete a service version
+func (s *Service) DeleteService(service, version string) {
+ client := newApiClient()
+
+ response := client.action(path.Join(serviceEntity, service, "versions", version)+"/", "DELETE", nil)
+ response.Process(true)
+}
+
+// MarkMature : mark a service as mature
+func (s *Service) MarkMature(service, version string) {
+ client := newApiClient()
+
+ response := client.action(path.Join(serviceEntity, service, "versions", version, "mature")+"/", "PUT", nil)
+ response.Process(true)
+}
+
+// DeployService : deploy a service
+func (s *Service) DeployService(service, version, env string, force, rebuild bool) {
+ client := newApiClient()
+ client.QueryParams["env_name"] = env
+ client.QueryParams["force"] = fmt.Sprintf("%v", force)
+ client.QueryParams["rebuild"] = fmt.Sprintf("%v", rebuild)
+
+ response := client.action(path.Join(serviceEntity, "deploy", service, "versions", version)+"/", "POST", nil)
+ response.Process(true)
+}
+
+// StatusService : get status of a service
+func (s *Service) StatusService(serviceName, version string) ([]service.Status, error) {
+ client := newApiClient()
+
+ response := client.action(path.Join(serviceEntity, serviceName, "versions", version, "status")+"/", "GET", nil)
+ response.Process(true)
+
+ var serviceResponse service.StatusResponse
+ err := json.Unmarshal(response.Body, &serviceResponse)
+
+ return serviceResponse.Response, err
+}
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
new file mode 100644
index 00000000..4769337a
--- /dev/null
+++ b/internal/cli/cli.go
@@ -0,0 +1,28 @@
+package cli
+
+import (
+ "os"
+
+ "github.com/dream11/odin/internal/command"
+ "github.com/mitchellh/cli"
+)
+
+// add commands to hide from help section
+var hiddenCommands []string
+
+// Cli : initiate the cli framework
+func Cli(appName, appVersion string) *cli.CLI {
+ // initiate cli
+ // for more refer https://github.com/mitchellh/cli/blob/master/cli.go#L49
+ return &cli.CLI{
+ Name: appName,
+ Version: appVersion,
+ Args: os.Args[1:],
+ Commands: command.CommandsCatalog(),
+ HelpFunc: cli.BasicHelpFunc(appName),
+ Autocomplete: true,
+ HelpWriter: os.Stdout,
+ ErrorWriter: os.Stderr,
+ HiddenCommands: hiddenCommands,
+ }
+}
diff --git a/internal/command/command_catalog.go b/internal/command/command_catalog.go
new file mode 100644
index 00000000..64406333
--- /dev/null
+++ b/internal/command/command_catalog.go
@@ -0,0 +1,145 @@
+package command
+
+import (
+ "github.com/dream11/odin/internal/command/commands"
+ "github.com/mitchellh/cli"
+)
+
+/*
+Command Structure:
+
+ odin
+
+Verbs are essentially the actions that will be performed,
+like: create, list, delete, etc...
+
+Verb convention:
+ - create
+ - update
+ - delete
+ - describe
+ - list
+ - status
+ - logs
+ - deploy
+ - destroy
+
+Resources are the entities on with the verbs will run,
+like: environment, profile, etc...
+
+Options are merely the flags that are required with the
+command.
+*/
+
+/*
+TODO:
+- status & logs verbs for env resource
+- status & logs verbs for service resource
+- add verbs for profile resource
+*/
+
+// CommandsCatalog : initiate commands catalog
+func CommandsCatalog() map[string]cli.CommandFactory {
+ return map[string]cli.CommandFactory{
+ "configure": func() (cli.Command, error) {
+ return &commands.Configure{}, nil
+ },
+
+ // Verbs for `env` resource
+ "create env": func() (cli.Command, error) {
+ return &commands.Env{Create: true}, nil
+ },
+ "update env": func() (cli.Command, error) {
+ return &commands.Env{Update: true}, nil
+ },
+ "describe env": func() (cli.Command, error) {
+ return &commands.Env{Describe: true}, nil
+ },
+ "list env": func() (cli.Command, error) {
+ return &commands.Env{List: true}, nil
+ },
+ "delete env": func() (cli.Command, error) {
+ return &commands.Env{Delete: true}, nil
+ },
+ "get-history env": func() (cli.Command, error) {
+ return &commands.Env{GetHistory: true}, nil
+ },
+ "describe-history env": func() (cli.Command, error) {
+ return &commands.Env{DescribeHistory: true}, nil
+ },
+ "status env": func() (cli.Command, error) {
+ return &commands.Env{Status: true}, nil
+ },
+
+ // Verbs for `component-type` resource
+ "list component-type": func() (cli.Command, error) {
+ return &commands.ComponentType{List: true}, nil
+ },
+
+ // Verbs for `component` resource
+ "describe component-type": func() (cli.Command, error) {
+ return &commands.ComponentType{Describe: true}, nil
+ },
+
+ // Verbs for `service` resource
+ "create service": func() (cli.Command, error) {
+ return &commands.Service{Create: true}, nil
+ },
+ "describe service": func() (cli.Command, error) {
+ return &commands.Service{Describe: true}, nil
+ },
+ "list service": func() (cli.Command, error) {
+ return &commands.Service{List: true}, nil
+ },
+ "label service": func() (cli.Command, error) {
+ return &commands.Service{Label: true}, nil
+ },
+ "deploy service": func() (cli.Command, error) {
+ return &commands.Service{Deploy: true}, nil
+ },
+ "delete service": func() (cli.Command, error) {
+ return &commands.Service{Delete: true}, nil
+ },
+ "undeploy service": func() (cli.Command, error) {
+ return &commands.Service{Undeploy: true}, nil
+ },
+ "status service": func() (cli.Command, error) {
+ return &commands.Service{Status: true}, nil
+ },
+ /*
+ Sample commands -
+
+ "create test": func() (cli.Command, error) {
+ return &commands.Test{Create: true}, nil
+ },
+ "update test": func() (cli.Command, error) {
+ return &commands.Test{Update: true}, nil
+ },
+ "delete test": func() (cli.Command, error) {
+ return &commands.Test{Delete: true}, nil
+ },
+ "list test": func() (cli.Command, error) {
+ return &commands.Test{List: true}, nil
+ },
+ "describe test": func() (cli.Command, error) {
+ return &commands.Test{Describe: true}, nil
+ },
+ "label test": func() (cli.Command, error) {
+ return &commands.Test{Label: true}, nil
+ },
+ "status test": func() (cli.Command, error) {
+ return &commands.Test{Status: true}, nil
+ },
+ "logs test": func() (cli.Command, error) {
+ return &commands.Test{Logs: true}, nil
+ },
+ "deploy test": func() (cli.Command, error) {
+ return &commands.Test{Deploy: true}, nil
+ },
+ "destroy test": func() (cli.Command, error) {
+ return &commands.Test{Destroy: true}, nil
+ },
+
+ */
+ }
+}
diff --git a/internal/command/commands/command.go b/internal/command/commands/command.go
new file mode 100644
index 00000000..42ffd982
--- /dev/null
+++ b/internal/command/commands/command.go
@@ -0,0 +1,60 @@
+package commands
+
+import (
+ "fmt"
+ "strings"
+
+ odin "github.com/dream11/odin/app"
+ "github.com/dream11/odin/internal/ui"
+)
+
+/*
+command : interface for resources
+The verbs can be associated with any resource
+*/
+type command struct {
+ Create bool // Create a resource record
+ Delete bool // Delete a resource record
+ Update bool // Update a resource record
+ Describe bool // Describe a resource
+ Label bool // Label a resource
+ List bool // List the resources
+ Status bool // current Status of resource
+ Logs bool // execution Logs of resource
+ Deploy bool // Deploy resource
+ Undeploy bool // Undeploy resource
+ Destroy bool // Destroy the deployed resource
+ GetHistory bool // Get changelog of resource
+ DescribeHistory bool // Describe a changelog of resource
+
+ Logger ui.Logger // Use this to log messages
+ Input ui.Input // Use this to take inputs
+}
+
+// help text generator
+func commandHelper(verb, resource string, options []string) string {
+ var opts string
+ if len(options) > 0 {
+ opts = "[Options]\n\nOptions:\n"
+ }
+
+ for _, opt := range options {
+ opts = opts + fmt.Sprintf("\t%s\n", opt)
+ }
+ return fmt.Sprintf("Usage: %s %s %s %s", odin.App.Name, verb, resource, opts)
+}
+
+func defaultHelper() string {
+ return fmt.Sprintf("Usage: %s --help", odin.App.Name)
+}
+
+// get empty parameter list
+func emptyParameters(params map[string]string) string {
+ emptyParameters := []string{}
+ for key, val := range params {
+ if len(val) == 0 {
+ emptyParameters = append(emptyParameters, key)
+ }
+ }
+ return strings.Join(emptyParameters, ", ")
+}
diff --git a/internal/command/commands/componentType.go b/internal/command/commands/componentType.go
new file mode 100644
index 00000000..ce6d7e3b
--- /dev/null
+++ b/internal/command/commands/componentType.go
@@ -0,0 +1,122 @@
+package commands
+
+import (
+ "flag"
+ "fmt"
+
+ "github.com/dream11/odin/internal/backend"
+ "github.com/dream11/odin/pkg/table"
+ "gopkg.in/yaml.v3"
+)
+
+// initiate backend client for component type
+var componentTypeClient backend.ComponentType
+
+// Component Type : command declaration
+type ComponentType command
+
+// Run : implements the actual functionality of the command
+func (c *ComponentType) Run(args []string) int {
+ flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError)
+ // create flags
+ componentTypeName := flagSet.String("name", "", "name of component type")
+ componentTypeVersion := flagSet.String("version", "", "version of component type")
+
+ err := flagSet.Parse(args)
+ if err != nil {
+ c.Logger.Error("Unable to parse flags! " + err.Error())
+ return 1
+ }
+ if c.List {
+ c.Logger.Info("Listing all component types")
+ componentTypeList, err := componentTypeClient.ListComponentTypes(*componentTypeName, *componentTypeVersion)
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+ var tableHeaders []string
+ var tableData [][]interface{}
+ if len(*componentTypeName) == 0 {
+ tableHeaders = []string{"Component Name", "Latest Version", "Total Versions Available"}
+ for _, componentType := range componentTypeList {
+ tableData = append(tableData, []interface{}{
+ componentType.Name,
+ componentType.Version,
+ componentType.TotalVersions,
+ })
+ }
+ } else {
+ tableHeaders = []string{"Component Name", "Version"}
+ for _, componentType := range componentTypeList {
+ tableData = append(tableData, []interface{}{
+ componentType.Name,
+ componentType.Version,
+ })
+ }
+ }
+
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+ c.Logger.Output("\nCommand to describe component types")
+ c.Logger.ItalicEmphasize("odin describe component-type --name --version ")
+ return 0
+ }
+
+ if c.Describe {
+ emptyParameters := emptyParameters(map[string]string{"--name": *componentTypeName})
+ if len(emptyParameters) == 0 {
+ c.Logger.Info("Describing component type: " + *componentTypeName + "@" + *componentTypeVersion)
+ componentTypeResp, err := componentTypeClient.DescribeComponentType(*componentTypeName, *componentTypeVersion)
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ details, err := yaml.Marshal(componentTypeResp)
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ c.Logger.Output(string(details))
+
+ return 0
+ }
+
+ c.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+
+ return 1
+ }
+ c.Logger.Error("Not a valid command")
+ return 127
+}
+
+// Help : returns an explanatory string
+func (c *ComponentType) Help() string {
+ if c.List {
+ return commandHelper("list", "component-type", []string{
+ "--name=name of component type",
+ "--version=of component type"})
+ }
+ if c.Describe {
+ return commandHelper("list", "component-type", []string{
+ "--name=name of component type (required)",
+ "--version=of component type (deafult latest)"})
+ }
+
+ return defaultHelper()
+}
+
+// Synopsis : returns a brief helper text for the command's verbs
+func (c *ComponentType) Synopsis() string {
+ if c.List {
+ return "list all components types"
+ }
+ if c.Describe {
+ return "describe component type"
+ }
+ return defaultHelper()
+}
diff --git a/internal/command/commands/configure.go b/internal/command/commands/configure.go
new file mode 100644
index 00000000..493ce508
--- /dev/null
+++ b/internal/command/commands/configure.go
@@ -0,0 +1,148 @@
+package commands
+
+import (
+ "flag"
+ "path"
+
+ "github.com/dream11/odin/api/configuration"
+ "github.com/dream11/odin/app"
+ "github.com/dream11/odin/internal/backend"
+ "github.com/dream11/odin/pkg/file"
+ "gopkg.in/yaml.v3"
+)
+
+// initiate backend client for auth
+var authClient backend.Auth
+
+// Configure : command declaration
+type Configure command
+
+// Run : implements the actual functionality of the command
+func (c *Configure) Run(args []string) int {
+ // Define flag set
+ flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError)
+ // create flags
+ refresh := flagSet.Bool("refresh", false, "refresh token using existing tokens")
+ hardRefresh := flagSet.Bool("hard", false, "hard refresh token using existing keys")
+
+ err := flagSet.Parse(args)
+ if err != nil {
+ c.Logger.Error("Unable to parse flags! " + err.Error())
+ return 1
+ }
+
+ configPath := path.Join(app.WorkDir.Location, app.WorkDir.ConfigFile)
+
+ var config configuration.Configuration
+
+ // run only when --refresh is not applied
+ if *refresh {
+ // read secret keys from workdir
+ preConfigYaml, err := file.Read(configPath)
+ if err != nil {
+ c.Logger.Error("Unable to read configured keys. " + err.Error())
+ return 1
+ }
+
+ // parse secret keys
+ err = yaml.Unmarshal(preConfigYaml, &config)
+ if err != nil {
+ c.Logger.Error("Unable to parse secret keys. " + err.Error())
+ return 1
+ }
+
+ if *hardRefresh {
+ authResponse, err := authClient.GetToken(config.Keys.AccessKey, config.Keys.SecretAccessKey)
+ if err != nil {
+ c.Logger.Error("Unable to hard refresh the tokens. " + err.Error())
+ return 1
+ }
+
+ config.AccessToken = authResponse.AccessToken
+ config.RefreshToken = authResponse.RefreshToken
+ } else {
+ authResponse, err := authClient.RefreshToken(config.RefreshToken)
+ if err != nil {
+ c.Logger.Error("Unable to refresh the tokens. " + err.Error())
+ return 1
+ }
+
+ config.AccessToken = authResponse.AccessToken
+ config.RefreshToken = authResponse.RefreshToken
+ }
+ } else {
+ // get access key from user
+ config.BackendAddr, err = c.Input.Ask("Enter Backend Address:")
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ // get access key from user
+ config.Keys.AccessKey, err = c.Input.Ask("Enter Access Key:")
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ // get secret access key from user
+ config.Keys.SecretAccessKey, err = c.Input.AskSecret("Enter Secret Access Key:")
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ // generate yaml
+ configYaml, err := yaml.Marshal(config)
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ // store pre configs
+ err = file.Write(configPath, string(configYaml), 0755)
+ if err != nil {
+ c.Logger.Error("Unable to write configuration." + err.Error())
+ return 1
+ }
+
+ authResponse, err := authClient.GetToken(config.Keys.AccessKey, config.Keys.SecretAccessKey)
+ if err != nil {
+ c.Logger.Error("Unable to refresh the tokens. " + err.Error())
+ return 1
+ }
+
+ config.AccessToken = authResponse.AccessToken
+ config.RefreshToken = authResponse.RefreshToken
+ }
+
+ // generate yaml
+ configYaml, err := yaml.Marshal(config)
+ if err != nil {
+ c.Logger.Error(err.Error())
+ return 1
+ }
+
+ // store configs
+ err = file.Write(configPath, string(configYaml), 0755)
+ if err != nil {
+ c.Logger.Error("Unable to write configuration." + err.Error())
+ return 1
+ }
+
+ c.Logger.Success("Configured!")
+ return 0
+}
+
+// Help : returns an explanatory string
+func (c *Configure) Help() string {
+ return commandHelper("configure", "", []string{
+ "--refresh (to enable only token refresh using pre fetched refresh tokens)",
+ "--hard (to enable token refresh using pre entered access keys | works with --refresh)",
+ })
+}
+
+// Synopsis : returns a brief helper text for the command's verbs
+func (c *Configure) Synopsis() string {
+ return "configure the cli authentication"
+}
diff --git a/internal/command/commands/env.go b/internal/command/commands/env.go
new file mode 100644
index 00000000..5f3a44cd
--- /dev/null
+++ b/internal/command/commands/env.go
@@ -0,0 +1,432 @@
+package commands
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/dream11/odin/api/environment"
+ "github.com/dream11/odin/internal/backend"
+ "github.com/dream11/odin/pkg/datetime"
+ "github.com/dream11/odin/pkg/file"
+ "github.com/dream11/odin/pkg/table"
+ "gopkg.in/yaml.v3"
+)
+
+// initiate backend client for environment
+var envClient backend.Env
+
+// Env : command declaration
+type Env command
+
+// Run : implements the actual functionality of the command
+func (e *Env) Run(args []string) int {
+ // Define flag set
+ flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError)
+ // create flags
+ name := flagSet.String("name", "", "name of environment")
+ team := flagSet.String("team", "", "display environments created by a team")
+ purpose := flagSet.String("purpose", "", "reason to create environment")
+ env := flagSet.String("env-type", "kube", "environment to attach with environment")
+ service := flagSet.String("service", "", "service name to filter out describe environment")
+ component := flagSet.String("component", "", "component name to filter out describe environment")
+ providerAccount := flagSet.String("account", "", "account name to provision the environment in")
+ filePath := flagSet.String("file", "environment.yaml", "file to read environment config")
+ id := flagSet.Int("id", 0, "unique id of a changelog of an env")
+
+ err := flagSet.Parse(args)
+ if err != nil {
+ e.Logger.Error("Unable to parse flags! " + err.Error())
+ return 1
+ }
+
+ if e.Create {
+ emptyParameters := emptyParameters(map[string]string{"--env-type": *env})
+ if len(emptyParameters) == 0 {
+ e.Logger.Info("Creating environment for team: " + *team)
+ envConfig := environment.Env{
+ Team: *team,
+ Purpose: *purpose,
+ EnvType: *env,
+ Account: *providerAccount,
+ }
+
+ response, err := envClient.CreateEnv(envConfig)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ e.Logger.Success("Env: " + response.Name + " created!")
+
+ return 0
+ }
+
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+
+ return 1
+ }
+
+ if e.Status {
+ emptyParameters := emptyParameters(map[string]string{"--name": *name})
+ if len(emptyParameters) == 0 {
+ e.Logger.Info("Fetching status for environment: " + *name + ", service: " + *service)
+
+ if *service != "" {
+
+ envServiceStatus, err := envClient.EnvServiceStatus(*name, *service)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ relativeDeployedSinceTime := datetime.DateTimeFromNow(envServiceStatus.LastDeployedAt)
+ e.Logger.Output("Service version: " + string(envServiceStatus.Version))
+ e.Logger.Output("Last deployed: " + relativeDeployedSinceTime)
+ e.Logger.Output("Component details: ")
+
+ tableHeaders := []string{"Name", "Version", "Status"}
+ var tableData [][]interface{}
+
+ for _, component := range envServiceStatus.Components {
+ tableData = append(tableData, []interface{}{
+ component.Name,
+ component.Version,
+ component.Status,
+ })
+ }
+
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ } else {
+
+ envStatus, err := envClient.EnvStatus(*name)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ tableHeaders := []string{"Name", "Version", "Status", "Last deployed"}
+ var tableData [][]interface{}
+
+ for _, serviceStatus := range envStatus.ServiceStatus {
+ relativeDeployedSinceTime := datetime.DateTimeFromNow(serviceStatus.LastDeployedAt)
+ tableData = append(tableData, []interface{}{
+ serviceStatus.Name,
+ serviceStatus.Version,
+ serviceStatus.Status,
+ relativeDeployedSinceTime,
+ })
+ }
+
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+ }
+
+ return 0
+ }
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if e.Update {
+ emptyParameters := emptyParameters(map[string]string{"--name": *name})
+ if len(emptyParameters) == 0 {
+ e.Logger.Warn("Updating environment: " + *name)
+
+ configData, err := file.Read(*filePath)
+ if err != nil {
+ e.Logger.Error("Unable to read from " + *filePath + "\n" + err.Error())
+ return 1
+ }
+
+ var parsedConfig interface{}
+
+ if strings.Contains(*filePath, ".yaml") || strings.Contains(*filePath, ".yml") {
+ err = yaml.Unmarshal(configData, &parsedConfig)
+ if err != nil {
+ e.Logger.Error("Unable to parse YAML. " + err.Error())
+ return 1
+ }
+ } else if strings.Contains(*filePath, ".json") {
+ err = json.Unmarshal(configData, &parsedConfig)
+ if err != nil {
+ e.Logger.Error("Unable to parse JSON. " + err.Error())
+ return 1
+ }
+ } else {
+ e.Logger.Error("Unrecognized file format")
+ return 1
+ }
+
+ envClient.UpdateEnv(*name, parsedConfig)
+
+ return 0
+ }
+
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if e.Describe {
+ emptyParameters := emptyParameters(map[string]string{"--name": *name})
+ if len(emptyParameters) == 0 {
+ e.Logger.Info("Describing " + *name)
+ envResp, err := envClient.DescribeEnv(*name, *service, *component)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ details, err := yaml.Marshal(envResp)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ e.Logger.Output(string(details))
+ if *service == "" && *component == "" {
+ e.Logger.Output("\nCommand to descibe env")
+ e.Logger.ItalicEmphasize(fmt.Sprintf("odin describe env --name %s --service --component ", *name))
+ }
+ return 0
+ }
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if e.List {
+ e.Logger.Info("Listing all environment(s)")
+ envList, err := envClient.ListEnv(*name, *team, *env, *providerAccount)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ tableHeaders := []string{"Name", "Team", "Env Type", "State", "Account", "Deletion Time", "Purpose", "CreatedAt", "CreatedBy", "UpdatedAt", "UpdatedBy"}
+ var tableData [][]interface{}
+
+ for _, env := range envList {
+ relativeDeletionTimestamp := datetime.DateTimeFromNow(env.DeletionTime)
+ relativeCreatedAtTimestamp := datetime.DateTimeFromNow(env.CreatedAt)
+ relativeUpdatedAtTimestamp := datetime.DateTimeFromNow(env.UpdatedAt)
+ tableData = append(tableData, []interface{}{
+ env.Name,
+ env.Team,
+ env.EnvType,
+ env.State,
+ env.Account,
+ relativeDeletionTimestamp,
+ env.Purpose,
+ relativeCreatedAtTimestamp,
+ env.CreatedBy,
+ relativeUpdatedAtTimestamp,
+ env.UpdatedBy,
+ })
+ }
+
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+ return 0
+ }
+
+ if e.Delete {
+ emptyParameters := emptyParameters(map[string]string{"--name": *name})
+ if len(emptyParameters) == 0 {
+ e.Logger.Warn("Deleting environment:" + *name)
+ envClient.DeleteEnv(*name)
+
+ return 0
+ }
+
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if e.GetHistory {
+ emptyParameters := emptyParameters(map[string]string{"--name": *name})
+ if len(emptyParameters) == 0 {
+ e.Logger.Info("Fetching changelog for env: " + *name)
+ envResp, err := envClient.GetHistoryEnv(*name)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ tableHeaders := []string{"ID", "Action", "Resource Details", "Modified by", "Last Modified"}
+ var tableData [][]interface{}
+
+ for _, env := range envResp {
+ relativeCreationTimestamp := datetime.DateTimeFromNow(env.CreatedAt)
+ tableData = append(tableData, []interface{}{
+ env.ID,
+ env.Action,
+ env.ResourceDetails,
+ env.CreatedBy,
+ relativeCreationTimestamp,
+ })
+ }
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ e.Logger.Output("\nCommand to describe a changelog in detail")
+ e.Logger.ItalicEmphasize("odin describe-history env --name --id ")
+ return 0
+ }
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if e.DescribeHistory {
+ s := ""
+ if *id > 0 {
+ s = strconv.Itoa(*id)
+ }
+
+ emptyParameters := emptyParameters(map[string]string{"--name": *name, "--id": s})
+ if len(emptyParameters) == 0 {
+ e.Logger.Info("Detailed description of a changelog for env: " + *name + " with ID: " + s)
+ envResp, err := envClient.DescribeHistoryEnv(*name, s)
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ if len(envResp) == 0 {
+ e.Logger.Output("\nCommand to get the correct ID of the changelog")
+ e.Logger.ItalicEmphasize("odin get-history env --name " + *name)
+ return 1
+ }
+
+ details, err := yaml.Marshal(envResp[0])
+ if err != nil {
+ e.Logger.Error(err.Error())
+ return 1
+ }
+
+ e.Logger.Output(string(details))
+
+ return 0
+ }
+ e.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ e.Logger.Error("Not a valid command")
+ return 127
+}
+
+// Help : returns an explanatory string
+func (e *Env) Help() string {
+ if e.Create {
+ return commandHelper("create", "environment", []string{
+ "--team=team name to associate the environment with",
+ "--purpose=reason to create environment",
+ "--env-type=type of environment",
+ "--account=account name to provision the environment in (optional)",
+ })
+ }
+
+ if e.Update {
+ return commandHelper("update", "environment", []string{
+ "--name=name of environment to update",
+ "--file=file path to pick update config",
+ })
+ }
+
+ if e.List {
+ return commandHelper("list", "environment", []string{
+ "--name=name of env",
+ "--team=name of team",
+ "--env-type=env type of the environment",
+ "--account=cloud provider account name",
+ })
+ }
+
+ if e.Describe {
+ return commandHelper("describe", "environment", []string{
+ "--name=name of environment to describe",
+ "--service service config that is deployed on env",
+ "--component component config that is deployed on env",
+ })
+ }
+
+ if e.Delete {
+ return commandHelper("delete", "environment", []string{
+ "--name=name of environment to delete",
+ })
+ }
+
+ if e.GetHistory {
+ return commandHelper("get-history", "environment", []string{
+ "--name=name of environment fetch changelog for",
+ })
+ }
+
+ if e.DescribeHistory {
+ return commandHelper("describe-history", "environment", []string{
+ "--name=name of environment to fetch changelog for",
+ "--id=unique id of a changelog for the specified env to get details for (positive integer)",
+ })
+ }
+
+ if e.Status {
+ return commandHelper("status", "environment", []string{
+ "--name=name of environment",
+ "--service=name of service",
+ })
+ }
+
+ return defaultHelper()
+}
+
+// Synopsis : returns a brief helper text for the command's verbs
+func (e *Env) Synopsis() string {
+ if e.Create {
+ return "create an environment"
+ }
+
+ if e.Update {
+ return "update an environment"
+ }
+
+ if e.List {
+ return "list all active environment"
+ }
+
+ if e.Describe {
+ return "describe an environment"
+ }
+
+ if e.Delete {
+ return "delete an environment"
+ }
+
+ if e.GetHistory {
+ return "get changelog of an environment"
+ }
+
+ if e.DescribeHistory {
+ return "get env config details for a changelog of an environment"
+ }
+
+ if e.Status {
+ return "Fetch deployment status of the environment"
+ }
+ return defaultHelper()
+}
diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go
new file mode 100644
index 00000000..d551746c
--- /dev/null
+++ b/internal/command/commands/service.go
@@ -0,0 +1,356 @@
+package commands
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "strings"
+
+ "github.com/dream11/odin/internal/backend"
+ "github.com/dream11/odin/pkg/file"
+ "github.com/dream11/odin/pkg/table"
+ "gopkg.in/yaml.v3"
+)
+
+// initiate backend client for service
+var serviceClient backend.Service
+
+// Service : command declaration
+type Service command
+
+// Run : implements the actual functionality of the command
+func (s *Service) Run(args []string) int {
+ // Define flag set
+ flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError)
+ // create flags
+ filePath := flagSet.String("file", "service.yaml", "file to read service config")
+ serviceName := flagSet.String("name", "", "name of service to be used")
+ serviceVersion := flagSet.String("version", "", "version of service to be used")
+ force := flagSet.Bool("force", false, "forcefully deploy the new version of the service")
+ envName := flagSet.String("env", "", "name of environment to deploy the service in")
+ teamName := flagSet.String("team", "", "name of user's team")
+ isMature := flagSet.Bool("mature", false, "mark service version as matured")
+ rebuild := flagSet.Bool("rebuild", false, "rebuild executor for creating images or deploying services")
+ component := flagSet.String("component", "", "name of service component")
+
+ err := flagSet.Parse(args)
+ if err != nil {
+ s.Logger.Error("Unable to parse flags! " + err.Error())
+ return 1
+ }
+
+ if s.Create {
+
+ if *rebuild {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName, "--version": *serviceVersion})
+ if len(emptyParameters) == 0 {
+ serviceClient.RebuildService(*serviceName, *serviceVersion)
+ s.Logger.Output("Command to check status of images")
+ s.Logger.ItalicEmphasize(fmt.Sprintf("odin status service --name %s --version %s", *serviceName, *serviceVersion))
+ return 0
+ }
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ configData, err := file.Read(*filePath)
+ if err != nil {
+ s.Logger.Error("Unable to read from " + *filePath + "\n" + err.Error())
+ return 1
+ }
+
+ var parsedConfig interface{}
+
+ if strings.Contains(*filePath, ".yaml") || strings.Contains(*filePath, ".yml") {
+ err = yaml.Unmarshal(configData, &parsedConfig)
+ if err != nil {
+ s.Logger.Error("Unable to parse YAML. " + err.Error())
+ return 1
+ }
+ } else if strings.Contains(*filePath, ".json") {
+ err = json.Unmarshal(configData, &parsedConfig)
+ if err != nil {
+ s.Logger.Error("Unable to parse JSON. " + err.Error())
+ return 1
+ }
+ } else {
+ s.Logger.Error("Unrecognized file format")
+ return 1
+ }
+
+ serviceClient.CreateService(parsedConfig)
+
+ s.Logger.Output("Command to check status of images")
+ s.Logger.ItalicEmphasize("odin status service --name --version ")
+ return 0
+ }
+
+ if s.Describe {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName})
+ if len(emptyParameters) == 0 {
+ s.Logger.Info("Describing service: " + *serviceName)
+ serviceResp, err := serviceClient.DescribeService(*serviceName, *serviceVersion, *component)
+ if err != nil {
+ s.Logger.Error(err.Error())
+ return 1
+ }
+
+ var details []byte
+ if len(*component) == 0 {
+ s.Logger.Info(serviceResp.Name + "@" + serviceResp.Version + " details!")
+ details, err = yaml.Marshal(serviceResp)
+ } else {
+ s.Logger.Info(fmt.Sprintf("%s component details for %s@%s", *component, serviceResp.Name, serviceResp.Version))
+ details, err = yaml.Marshal(serviceResp.Components[0])
+ }
+
+ if err != nil {
+ s.Logger.Error(err.Error())
+ return 1
+ }
+
+ s.Logger.Output(string(details))
+ if len(*component) == 0 {
+ s.Logger.Output("Command to get component details")
+ s.Logger.ItalicEmphasize(fmt.Sprintf("odin describe service --name %s --version --component ", *serviceName))
+ }
+ return 0
+ }
+
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if s.List {
+ s.Logger.Info("Listing all services")
+ serviceList, err := serviceClient.ListServices(*teamName, *serviceVersion, *serviceName, *isMature)
+ if err != nil {
+ s.Logger.Error(err.Error())
+ return 1
+ }
+
+ tableHeaders := []string{"Name", "Version", "Description", "Team", "Mature"}
+ var tableData [][]interface{}
+
+ for _, service := range serviceList {
+ tableData = append(tableData, []interface{}{
+ service.Name,
+ service.Version,
+ service.Description,
+ strings.Join(service.Team, ","),
+ *service.Mature,
+ })
+ }
+
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ s.Logger.Error(err.Error())
+ return 1
+ }
+ s.Logger.Output("\nCommand to describe service")
+ s.Logger.ItalicEmphasize("odin describe service --name --version ")
+ return 0
+ }
+
+ if s.Label {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName, "--version": *serviceVersion})
+ if len(emptyParameters) == 0 {
+
+ // Add more labels to this condition
+ if !*isMature {
+ s.Logger.Error("No label specified")
+ return 1
+ }
+
+ if *isMature {
+ s.Logger.Info("Marking " + *serviceName + "@" + *serviceVersion + " as mature")
+ serviceClient.MarkMature(*serviceName, *serviceVersion)
+ }
+ return 0
+ }
+
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if s.Deploy {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName, "--version": *serviceVersion, "--env": *envName})
+ if len(emptyParameters) == 0 {
+ s.Logger.Info("Deploying service: " + *serviceName + "@" + *serviceVersion + " in " + *envName)
+
+ serviceClient.DeployService(*serviceName, *serviceVersion, *envName, *force, *rebuild)
+
+ return 0
+ }
+
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if s.Undeploy {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName, "--env": *envName})
+ if len(emptyParameters) == 0 {
+ s.Logger.Info("Undeploying service: " + *serviceName + " from environment" + *envName)
+ serviceClient.UndeployService(*serviceName, *envName)
+
+ s.Logger.Success("Job Triggered to undeploy your service " + *serviceName + " from the env " + *envName)
+
+ return 0
+ }
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if s.Delete {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName, "--version": *serviceVersion})
+ if len(emptyParameters) == 0 {
+ s.Logger.Info("Deleting service: " + *serviceName + "@" + *serviceVersion)
+ serviceClient.DeleteService(*serviceName, *serviceVersion)
+
+ return 0
+ }
+
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ if s.Status {
+ emptyParameters := emptyParameters(map[string]string{"--name": *serviceName, "--version": *serviceVersion})
+ if len(emptyParameters) == 0 {
+ s.Logger.Info("Getting status of service: " + *serviceName + "@" + *serviceVersion)
+ serviceStatus, err := serviceClient.StatusService(*serviceName, *serviceVersion)
+ if err != nil {
+ s.Logger.Error(err.Error())
+ return 1
+ }
+
+ tableHeaders := []string{"Component Name", "AMI", "DOCKER IMAGE"}
+ var tableData [][]interface{}
+ for _, componentStatus := range serviceStatus {
+ tableData = append(tableData, []interface{}{
+ componentStatus.Name,
+ componentStatus.Ec2,
+ componentStatus.Docker,
+ })
+ }
+
+ err = table.Write(tableHeaders, tableData)
+ if err != nil {
+ s.Logger.Error(err.Error())
+ return 1
+ }
+ s.Logger.Output("\nCommand to deploy service")
+ s.Logger.ItalicEmphasize(fmt.Sprintf("odin deploy service --name %s --version %s --env ", *serviceName, *serviceVersion))
+ return 0
+ }
+
+ s.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters))
+ return 1
+ }
+
+ s.Logger.Error("Not a valid command")
+ return 127
+}
+
+// Help : returns an explanatory string
+func (s *Service) Help() string {
+ if s.Create {
+ return commandHelper("create", "service", []string{
+ "--file=yaml file to read service definition",
+ })
+ }
+
+ if s.Describe {
+ return commandHelper("describe", "service", []string{
+ "--name=name of service to describe",
+ "--version=version of service to describe",
+ "--component=name of component to describe",
+ })
+ }
+
+ if s.List {
+ return commandHelper("list", "service", []string{
+ "--team=name of team",
+ "--version=version of services to be listed",
+ "--mature (mature marked service versions)",
+ "--detailed (get a detailed view)",
+ })
+ }
+
+ if s.Label {
+ return commandHelper("label", "service", []string{
+ "--name=name of service to label",
+ "--version=version of service to label",
+ "--mature (mark service version as mature)",
+ })
+ }
+
+ if s.Deploy {
+ return commandHelper("deploy", "service", []string{
+ "--name=name of service to deploy",
+ "--version=version of service to deploy",
+ "--force=forcefully deploy your service",
+ "--rebuild=rebuild your executor job again for service deployment",
+ "--env=name of environment to deploy service in",
+ })
+ }
+ if s.Undeploy {
+ return commandHelper("deploy", "service", []string{
+ "--name=name of service to undeploy",
+ "--env=name of environment to undeploy service in",
+ })
+ }
+
+ if s.Delete {
+ return commandHelper("delete", "service", []string{
+ "--name=name of service to delete",
+ "--version=version of service to delete",
+ })
+ }
+
+ if s.Status {
+ return commandHelper("status", "service", []string{
+ "--name=name of service",
+ "--version=version of service",
+ })
+ }
+
+ return defaultHelper()
+}
+
+// Synopsis : returns a brief helper text for the command's verbs
+func (s *Service) Synopsis() string {
+ if s.Create {
+ return "create a service"
+ }
+
+ if s.Describe {
+ return "describe a service version"
+ }
+
+ if s.List {
+ return "list all services"
+ }
+
+ if s.Label {
+ return "label a service version"
+ }
+
+ if s.Deploy {
+ return "deploy a service"
+ }
+
+ if s.Undeploy {
+ return "undeploy a service"
+ }
+
+ if s.Delete {
+ return "delete a service version"
+ }
+
+ if s.Status {
+ return "get status of a service version"
+ }
+
+ return defaultHelper()
+}
diff --git a/internal/command/commands/test.go b/internal/command/commands/test.go
new file mode 100644
index 00000000..9ee1b21b
--- /dev/null
+++ b/internal/command/commands/test.go
@@ -0,0 +1,205 @@
+package commands
+
+import (
+ "flag"
+ "fmt"
+)
+
+// Test : Sample command declaration
+type Test command
+
+// Run implements the actual functionality of the command
+// and return exit codes based on success/failure of tasks performed
+func (t *Test) Run(args []string) int {
+ // Define a custom flag set
+ flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError)
+ // flag.ContinueOnError allows execution if flags have errors
+ // flag.ExitOnError gracefully stops execution if flags have errors
+ // flag.PanicOnError creates a panic if flags have errors
+
+ // Add required flags to the defined flag set
+ testFlag := flagSet.String("test-flag", "default value", "Help text")
+ // Positional parse the flags depending upon commands and sub commands
+ err := flagSet.Parse(args)
+ if err != nil {
+ t.Logger.Error("Unable to parse flags! " + err.Error())
+ return 1
+ }
+
+ // use the parsed flags
+ t.Logger.Info(fmt.Sprintf("-test-flag=%s", *testFlag))
+
+ if t.Create {
+ // Perform stuff for record creation of test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(create)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Delete {
+ // Perform stuff for record deletion of test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(delete)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.List {
+ // Perform stuff to list all test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(list)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Describe {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(describe)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Label {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(label)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Update {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(update)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Status {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(status)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Logs {
+ // Perform stuff to describe a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(logs)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Deploy {
+ // Perform stuff to deploy a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(deploy)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ if t.Destroy {
+ // Perform stuff to destroy a test resource
+ t.Logger.Info(fmt.Sprintf("Test Run(destroy)! flag value = %s", *testFlag))
+ return 0
+ }
+
+ t.Logger.Error("Not a valid command")
+ return 127
+}
+
+// Help should return an explanatory string,
+// that can explain the command's verbs
+func (t *Test) Help() string {
+ if t.Create {
+ return commandHelper("create", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Delete {
+ return commandHelper("delete", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.List {
+ return commandHelper("list", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Describe {
+ return commandHelper("describe", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Label {
+ return commandHelper("lable", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Update {
+ return commandHelper("update", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Status {
+ return commandHelper("status", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Logs {
+ return commandHelper("logs", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Deploy {
+ return commandHelper("deploy", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ if t.Destroy {
+ return commandHelper("destroy", "test", []string{
+ "--test-flag=required value",
+ })
+ }
+
+ return defaultHelper()
+}
+
+// Synopsis should return a brief helper text for the command's verbs
+func (t *Test) Synopsis() string {
+ if t.Create {
+ return "create a test resource"
+ }
+
+ if t.Delete {
+ return "delete a test resource"
+ }
+
+ if t.List {
+ return "list all test resources"
+ }
+
+ if t.Describe {
+ return "describe a test resource"
+ }
+
+ if t.Label {
+ return "label a test resource"
+ }
+
+ if t.Update {
+ return "update a test resource"
+ }
+
+ if t.Status {
+ return "current status of test resource"
+ }
+
+ if t.Logs {
+ return "execution logs of test resource"
+ }
+
+ if t.Deploy {
+ return "deploy a test resource"
+ }
+
+ if t.Destroy {
+ return "destroy a test resource"
+ }
+
+ return defaultHelper()
+}
diff --git a/internal/config/credential.go b/internal/config/credential.go
new file mode 100644
index 00000000..8e61077c
--- /dev/null
+++ b/internal/config/credential.go
@@ -0,0 +1,39 @@
+package config
+
+import (
+ "path"
+
+ "github.com/dream11/odin/api/configuration"
+ "github.com/dream11/odin/app"
+ "github.com/dream11/odin/internal/ui"
+ "github.com/dream11/odin/pkg/file"
+ "gopkg.in/yaml.v3"
+)
+
+var logger ui.Logger
+var secretText = ""
+
+// Get : fetch credentials from all sources
+func Get() configuration.Configuration {
+ configPath := path.Join(app.WorkDir.Location, app.WorkDir.ConfigFile)
+ var configs configuration.Configuration
+
+ rawConfig, _ := file.Read(configPath)
+ err := yaml.Unmarshal(rawConfig, &configs)
+ if err != nil {
+ logger.Error("Unable to parse configuration. " + err.Error())
+ }
+
+ // if len(configs.AccessToken) == 0 {
+ // logger.Debug("Access Token not found at: " + app.WorkDir.ConfigFile)
+ // }
+
+ // do not expose access key, secret access key & refresh tokens
+ configs.Keys = configuration.SecretKeys{
+ AccessKey: secretText,
+ SecretAccessKey: secretText,
+ }
+ configs.RefreshToken = secretText
+
+ return configs
+}
diff --git a/internal/ui/input.go b/internal/ui/input.go
new file mode 100644
index 00000000..1a837239
--- /dev/null
+++ b/internal/ui/input.go
@@ -0,0 +1,14 @@
+package ui
+
+// Input : interface declaration
+type Input struct{}
+
+// Ask : asks for a generic text input
+func (i *Input) Ask(description string) (string, error) {
+ return userInterface.Ask(description)
+}
+
+// AskSecret : asks for a secret text input
+func (i *Input) AskSecret(description string) (string, error) {
+ return userInterface.AskSecret(description)
+}
diff --git a/internal/ui/interface.go b/internal/ui/interface.go
new file mode 100644
index 00000000..d17f9f18
--- /dev/null
+++ b/internal/ui/interface.go
@@ -0,0 +1,30 @@
+package ui
+
+import (
+ "os"
+
+ "github.com/mitchellh/cli"
+)
+
+type ui struct {
+ cli.Ui
+}
+
+// interact with cli
+// take inputs, secret inputs, throw outputs/error etc.
+// for more, refer https://github.com/mitchellh/cli/blob/master/ui.go
+var userInterface *cli.PrefixedUi = &cli.PrefixedUi{
+ AskPrefix: "",
+ AskSecretPrefix: "(Secret) ",
+ OutputPrefix: "",
+ InfoPrefix: "[ INFO ] ",
+ ErrorPrefix: "[ ERROR ] ",
+ WarnPrefix: "[ WARNING ] ",
+ Ui: &ui{
+ &cli.BasicUi{
+ Writer: os.Stdout,
+ ErrorWriter: os.Stderr,
+ Reader: os.Stdin,
+ },
+ },
+}
diff --git a/internal/ui/logger.go b/internal/ui/logger.go
new file mode 100644
index 00000000..09c2b97c
--- /dev/null
+++ b/internal/ui/logger.go
@@ -0,0 +1,52 @@
+package ui
+
+import (
+ "fmt"
+ "os"
+)
+
+// Logger : interface declaration
+type Logger struct{}
+
+var successColor = "\033[1;32m%s\033[0m"
+var infoColor = "\033[0;34m%s\033[0m"
+var warningColor = "\033[1;33m%s\033[0m"
+var errorColor = "\033[1;31m%s\033[0m"
+var italicEmphasize = "\033[3m\033[1m%s\033[0m"
+
+// Info : informative messages
+func (l *Logger) Info(message string) {
+ userInterface.Info(fmt.Sprintf(infoColor, message))
+}
+
+// Success : success messages
+func (l *Logger) Success(message string) {
+ userInterface.Output(fmt.Sprintf(successColor, message))
+}
+
+// Warn : warning messages
+func (l *Logger) Warn(message string) {
+ userInterface.Warn(fmt.Sprintf(warningColor, message))
+}
+
+// Error : error/fatal messages
+func (l *Logger) Error(message string) {
+ userInterface.Error(fmt.Sprintf(errorColor, message))
+}
+
+// Output : generic messages
+func (l *Logger) Output(message string) {
+ userInterface.Output(message)
+}
+
+// Emphasize : generic messages
+func (l *Logger) ItalicEmphasize(message string) {
+ userInterface.Output(fmt.Sprintf(italicEmphasize, message))
+}
+
+// Debug : debugging messages
+func (l *Logger) Debug(message string) {
+ if os.Getenv("ODIN_DEBUG") == "yes" {
+ userInterface.Output(fmt.Sprintf("[ DEBUG ] %s", message))
+ }
+}
diff --git a/main.go b/main.go
new file mode 100644
index 00000000..17ea996b
--- /dev/null
+++ b/main.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+ "os"
+
+ odin "github.com/dream11/odin/app"
+ "github.com/dream11/odin/internal/cli"
+ "github.com/dream11/odin/internal/ui"
+)
+
+var logger ui.Logger
+
+func main() {
+ c := cli.Cli(odin.App.Name, odin.App.Version)
+ exitStatus, err := c.Run()
+ if err != nil {
+ logger.Error(err.Error())
+ os.Exit(1)
+ }
+
+ os.Exit(exitStatus)
+}
+
+// TODO: https://github.com/mitchellh/go-glint
+// TODO: https://github.com/charmbracelet/bubbletea for advanced interactions with user
diff --git a/pkg/datetime/datetime.go b/pkg/datetime/datetime.go
new file mode 100644
index 00000000..63c4fd1c
--- /dev/null
+++ b/pkg/datetime/datetime.go
@@ -0,0 +1,125 @@
+package datetime
+
+import (
+ "fmt"
+ "time"
+)
+
+var layout = "2006-01-02T15:04:05Z"
+
+type datetime struct {
+ Year int
+ Month int
+ Day int
+ Hour int
+ Min int
+ Sec int
+}
+
+func (t *datetime) normalize(y1 int, M1 time.Month) {
+ // Normalize negative values
+ if t.Sec < 0 {
+ t.Sec += 60
+ t.Min--
+ }
+ if t.Min < 0 {
+ t.Min += 60
+ t.Hour--
+ }
+ if t.Hour < 0 {
+ t.Hour += 24
+ t.Day--
+ }
+ if t.Day < 0 {
+ s := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC)
+ t.Day += 32 - s.Day()
+ t.Month--
+ }
+ if t.Month < 0 {
+ t.Month += 12
+ t.Year--
+ }
+}
+
+func diff(a, current time.Time) (out datetime, suffix string) {
+ if a.Location() != current.Location() {
+ current = current.In(a.Location())
+ }
+ suffix = "ago"
+ if a.After(current) {
+ a, current = current, a
+ suffix = "left"
+ }
+ y1, M1, d1 := a.Date()
+ y2, M2, d2 := current.Date()
+
+ h1, m1, s1 := a.Clock()
+ h2, m2, s2 := current.Clock()
+
+ dt := datetime{
+ Year: int(y2 - y1),
+ Month: int(M2 - M1),
+ Day: int(d2 - d1),
+ Hour: int(h2 - h1),
+ Min: int(m2 - m1),
+ Sec: int(s2 - s1),
+ }
+
+ dt.normalize(y1, M1)
+ return dt, suffix
+}
+
+// DateTime - description
+func DateTimeFromNow(val string) string {
+
+ t1, err := time.Parse(layout, val)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ t2 := time.Now().UTC()
+ dt, flag := diff(t1, t2)
+ out := ""
+ if dt.Year != 0 {
+ if dt.Month != 0 {
+ out = fmt.Sprintf("%vyr %vmonth %v", dt.Year, dt.Month, flag)
+ } else if dt.Day != 0 {
+ out = fmt.Sprintf("%vyr %vday %v", dt.Year, dt.Day, flag)
+ } else {
+ out = fmt.Sprintf("%vyr %v", dt.Year, flag)
+ }
+ } else if dt.Month != 0 {
+ if dt.Day != 0 {
+ out = fmt.Sprintf("%vmonth %vday %v", dt.Month, dt.Day, flag)
+ } else if dt.Hour != 0 {
+ out = fmt.Sprintf("%vmonth %vhour %v", dt.Month, dt.Hour, flag)
+ } else {
+ out = fmt.Sprintf("%vmonth %v", dt.Month, flag)
+ }
+ } else if dt.Day != 0 {
+ if dt.Hour != 0 {
+ out = fmt.Sprintf("%vday %vhour %v", dt.Day, dt.Hour, flag)
+ } else if dt.Min != 0 {
+ out = fmt.Sprintf("%vday %vmin %v", dt.Day, dt.Min, flag)
+ } else {
+ out = fmt.Sprintf("%vday %v", dt.Day, flag)
+ }
+ } else if dt.Hour != 0 {
+ if dt.Min != 0 {
+ out = fmt.Sprintf("%vhour %vmin %v", dt.Hour, dt.Min, flag)
+ } else if dt.Sec != 0 {
+ out = fmt.Sprintf("%vhour %vsec %v", dt.Hour, dt.Sec, flag)
+ } else {
+ out = fmt.Sprintf("%vhour %v", dt.Hour, flag)
+ }
+ } else if dt.Min != 0 {
+ if dt.Sec != 0 {
+ out = fmt.Sprintf("%vmin %vsec %v", dt.Min, dt.Sec, flag)
+ } else {
+ out = fmt.Sprintf("%vmin %v", dt.Min, flag)
+ }
+ } else {
+ out = fmt.Sprintf("%vsec %v", dt.Sec, flag)
+ }
+ return out
+}
diff --git a/pkg/dir/dir.go b/pkg/dir/dir.go
new file mode 100644
index 00000000..79efd25d
--- /dev/null
+++ b/pkg/dir/dir.go
@@ -0,0 +1,63 @@
+package dir
+
+import (
+ "io/ioutil"
+ "os"
+)
+
+// Create : create a directory
+func Create(dirPath string, permission os.FileMode) error {
+ err := os.Mkdir(dirPath, permission)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// SubDirs : get a list of all subdirectories
+func SubDirs(dirPath string) ([]string, error) {
+ subDirs := []string{}
+
+ files, err := ioutil.ReadDir(dirPath)
+ if err != nil {
+ return subDirs, err
+ }
+
+ for _, f := range files {
+ subDirs = append(subDirs, f.Name())
+ }
+
+ return subDirs, err
+}
+
+// IsDir : check if given path is dir or not
+func IsDir(filePath string) (bool, error) {
+ file, err := os.Open(filePath)
+ if err != nil {
+ return false, err
+ }
+
+ defer file.Close()
+
+ fileInfo, err := file.Stat()
+ if err != nil {
+ return false, err
+ }
+
+ return fileInfo.IsDir(), err
+}
+
+// Exists : check if directory exists or not
+func Exists(path string) (bool, error) {
+ _, err := os.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+
+ return false, err
+}
diff --git a/pkg/file/file.go b/pkg/file/file.go
new file mode 100644
index 00000000..a82250ff
--- /dev/null
+++ b/pkg/file/file.go
@@ -0,0 +1,19 @@
+package file
+
+import (
+ "io/fs"
+ "os"
+)
+
+// Read : read data from file
+func Read(filePath string) ([]byte, error) {
+ return os.ReadFile(filePath)
+}
+
+// Write : write data to file
+func Write(filePath, data string, permission fs.FileMode) error {
+ byteData := []byte(data)
+ err := os.WriteFile(filePath, byteData, permission)
+
+ return err
+}
diff --git a/pkg/request/request.go b/pkg/request/request.go
new file mode 100644
index 00000000..75942a74
--- /dev/null
+++ b/pkg/request/request.go
@@ -0,0 +1,114 @@
+package request
+
+import (
+ "bytes"
+ "encoding/json"
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "github.com/dream11/odin/internal/ui"
+)
+
+// Request structure
+type Request struct {
+ Method string
+ URL string
+ Header map[string]string
+ Query map[string]string
+ Body interface{}
+}
+
+// Response structure
+type Response struct {
+ Status string
+ StatusCode int
+ Body []byte
+ Error error
+}
+
+var logger ui.Logger
+
+// Make : make a generated request
+func (r *Request) Make() Response {
+ payload := new(bytes.Buffer)
+ err := json.NewEncoder(payload).Encode(r.Body)
+ if err != nil {
+ return Response{Error: err}
+ }
+
+ client := &http.Client{}
+ request, err := http.NewRequest(r.Method, r.URL, payload)
+ if err != nil {
+ return Response{Error: err}
+ }
+
+ q := request.URL.Query()
+ for key, val := range r.Query {
+ if len(val) > 0 {
+ q.Add(key, val)
+ }
+ }
+ request.URL.RawQuery = q.Encode()
+
+ logger.Debug("URL: " + request.URL.String())
+
+ for key, value := range r.Header {
+ if len(value) > 0 {
+ request.Header.Set(key, value)
+ }
+ }
+
+ // what if the internet is not present on the client side
+ response, err := client.Do(request)
+
+ if err != nil {
+ return Response{Error: err}
+ }
+
+ respBody, err := ioutil.ReadAll(response.Body)
+
+ if err != nil {
+ return Response{Error: err}
+ }
+
+ return Response{
+ Status: response.Status,
+ StatusCode: response.StatusCode,
+ Body: respBody,
+ Error: nil,
+ }
+}
+
+// Process : process request response to generate valid output
+// Exit on error, only if specified
+func (r *Response) Process(exitOnError bool) {
+ // Parse error and display error message
+ if r.Error != nil {
+ logger.Error(r.Error.Error())
+ handleExit(1, exitOnError)
+ } else {
+ if matchStatusCode(r.StatusCode, 200) {
+ logger.Debug(string(r.Body))
+ } else if matchStatusCode(r.StatusCode, 300) {
+ logger.Debug(r.Status)
+ logger.Debug(string(r.Body))
+ } else if matchStatusCode(r.StatusCode, 400) || matchStatusCode(r.StatusCode, 500) {
+ logger.Debug(r.Status)
+ logger.Error(string(r.Body))
+ handleExit(1, exitOnError)
+ }
+ }
+}
+
+// handle exit calls, allow exit if allowed via boolean
+func handleExit(code int, exit bool) {
+ if exit {
+ os.Exit(code)
+ }
+}
+
+// match the status code range
+func matchStatusCode(statusCode, matchCode int) bool {
+ return (statusCode - matchCode) < 100
+}
diff --git a/pkg/shell/exec.go b/pkg/shell/exec.go
new file mode 100644
index 00000000..9e6c2a1e
--- /dev/null
+++ b/pkg/shell/exec.go
@@ -0,0 +1,45 @@
+package shell
+
+import (
+ "bufio"
+ "os/exec"
+
+ "github.com/dream11/odin/internal/ui"
+)
+
+var logger ui.Logger
+
+// Exec : execute given command
+func Exec(command string) int {
+ logger.Warn("Executing:" + command)
+
+ cmd := exec.Command("bash", "-c", command)
+ stdout, _ := cmd.StdoutPipe()
+ stderr, _ := cmd.StderrPipe()
+
+ err := cmd.Start()
+ if err != nil {
+ logger.Error("Unable to start cmd execution. " + err.Error())
+ return 1
+ }
+
+ scannerOut := bufio.NewScanner(stdout)
+ for scannerOut.Scan() {
+ m := scannerOut.Text()
+ logger.Output(m)
+ }
+
+ scannerErr := bufio.NewScanner(stderr)
+ for scannerErr.Scan() {
+ m := scannerErr.Text()
+ logger.Error(m)
+ }
+
+ err = cmd.Wait()
+ if err != nil {
+ logger.Error(err.Error())
+ return 1
+ }
+
+ return cmd.ProcessState.ExitCode()
+}
diff --git a/pkg/table/table.go b/pkg/table/table.go
new file mode 100644
index 00000000..82a93c58
--- /dev/null
+++ b/pkg/table/table.go
@@ -0,0 +1,55 @@
+package table
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+)
+
+// Write : write provided input as tabular format
+func Write(headers []string, data [][]interface{}) error {
+ w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
+
+ var tabbedHeader string
+ for _, val := range headers {
+ tabbedHeader += fmt.Sprintf("*%s*\t|\t", val)
+ }
+
+ _, err := fmt.Fprintln(w, tabbedHeader)
+ if err != nil {
+ return err
+ }
+
+ for _, dataSet := range data {
+ var tabbedData string
+ for _, val := range dataSet {
+ tabbedData += fmt.Sprintf("%v\t|\t", val)
+ }
+
+ _, err := fmt.Fprintln(w, tabbedData)
+ if err != nil {
+ return err
+ }
+ }
+
+ return w.Flush()
+}
+
+/*
+Usage -
+
+func main() {
+ headers := []string{
+ "h1",
+ "h2",
+ "h3",
+ }
+
+ data := [][]interface{}{
+ {"Row", "number", 3},
+ {"Row", "number", 2},
+ }
+
+ Write(headers, data)
+}
+*/