Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7de7c85426 | ||
|
|
8e65c555e0 | ||
|
|
2557e6b73d | ||
|
|
4f75ee06a0 | ||
|
|
717adc9e9c | ||
|
|
15adeb7cfa | ||
|
|
0b06036a39 | ||
|
|
f7d4dcbff4 | ||
|
|
8334f2b0b2 | ||
|
|
5cccb1caa0 | ||
|
|
db9cdeb1c9 | ||
|
|
3abae8e49e | ||
|
|
bedb356b02 | ||
|
|
34a7dc57b2 | ||
|
|
d0dbb42492 | ||
|
|
1393b117cf | ||
|
|
cf407c63a2 | ||
|
|
3f00e8ef6d | ||
|
|
758b03de36 | ||
|
|
76d55f8e44 | ||
|
|
b9392ab4c9 | ||
|
|
dadf2d1bde | ||
|
|
74c2a3d6e7 | ||
|
|
2454fcf47c | ||
|
|
96a7a429e1 | ||
|
|
f29800ed5b | ||
|
|
15ce9170f3 | ||
|
|
9a144c1c6f | ||
|
|
f897c0f197 | ||
|
|
671fa949df | ||
|
|
612352d69f | ||
|
|
ef31263797 | ||
|
|
f64fbd4a2e | ||
|
|
dffb8a726b | ||
|
|
14d4886e61 | ||
|
|
0012b0a797 | ||
|
|
f6b2a8c66d | ||
|
|
c0a1d31c8d | ||
|
|
329ae055ee | ||
|
|
7ab0f33201 | ||
|
|
2e8ba39b8f | ||
|
|
c5f9f71e45 | ||
|
|
9bb597f366 | ||
|
|
786bddc478 | ||
|
|
d9edcf2f48 | ||
|
|
2262445b75 | ||
|
|
0c486e76c0 | ||
|
|
7d50f4e620 | ||
|
|
b2ec371709 | ||
|
|
549cdd9bfb | ||
|
|
44787b31cf | ||
|
|
e75e653c58 | ||
|
|
3eae013286 | ||
|
|
2221fb22a0 | ||
|
|
9dc3e6a12d | ||
|
|
de0024cd03 | ||
|
|
ed4e970194 | ||
|
|
b0067e31ba | ||
|
|
7e8ba4709e | ||
|
|
be7b2642fc | ||
|
|
896d9e3f72 | ||
|
|
be6666373b | ||
|
|
91df9392c0 | ||
|
|
bd058ee912 | ||
|
|
997f951d0c | ||
|
|
09886ad933 | ||
|
|
0de0b5d0cb | ||
|
|
0141eecef1 | ||
|
|
65ecc20c90 | ||
|
|
f86a4a93a7 | ||
|
|
5cae4b5adf | ||
|
|
d8afa3861d |
26
.github/workflows/release.yaml
vendored
@@ -6,9 +6,33 @@ on:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
pre_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get plugin version
|
||||
id: get_plugin_version
|
||||
run: echo "PLUGIN_VERSION=$(cat plugin.yaml | grep "version" | cut -d '"' -f 2)" >> $GITHUB_OUTPUT
|
||||
- name: Get tag name
|
||||
id: get_tag_name
|
||||
run: echo "TAG_NAME=$(echo ${{ github.ref_name }} | cut -d 'v' -f2)" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
plugin_version: ${{ steps.get_plugin_version.outputs.PLUGIN_VERSION }}
|
||||
tag_name: ${{ steps.get_tag_name.outputs.TAG_NAME }}
|
||||
|
||||
release:
|
||||
needs: pre_release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
#- name: Plugin version/Tag name Check
|
||||
# if: needs.pre_release.outputs.release_tag != needs.pre_release.outputs.plugin_version
|
||||
# uses: actions/github-script@v3
|
||||
# with:
|
||||
# script: |
|
||||
# core.setFailed('Plugin version and tag name are not equivalent!')
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
itiel@komodor.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
27
Makefile
@@ -1,9 +1,24 @@
|
||||
pull:
|
||||
git pull
|
||||
DATE ?= $(shell date +%FT%T%z)
|
||||
VERSION ?= $(git describe --tags --always --dirty --match=v* 2> /dev/null || \
|
||||
cat $(CURDIR)/.version 2> /dev/null || echo "v0")
|
||||
|
||||
build:
|
||||
go build -o bin/dashboard .
|
||||
.PHONY: test
|
||||
test: ; $(info $(M) start unit testing...) @
|
||||
@go test $$(go list ./... | grep -v /mocks/) --race -v -short -coverprofile=profile.cov
|
||||
@echo "\n*****************************"
|
||||
@echo "** TOTAL COVERAGE: $$(go tool cover -func profile.cov | grep total | grep -Eo '[0-9]+\.[0-9]+')% **"
|
||||
@echo "*****************************\n"
|
||||
|
||||
.PHONY: pull
|
||||
pull: ; $(info $(M) Pulling source...) @
|
||||
@git pull
|
||||
|
||||
debug:
|
||||
DEBUG=1 ./bin/dashboard
|
||||
.PHONY: build
|
||||
build: $(BIN) ; $(info $(M) Building executable...) @ ## Build program binary
|
||||
go build \
|
||||
-ldflags '-X main.version=$(VERSION) -X main.buildDate=$(DATE)' \
|
||||
-o bin/dashboard .
|
||||
|
||||
.PHONY: debug
|
||||
debug: ; $(info $(M) Running dashboard in debug mode...) @
|
||||
@DEBUG=1 ./bin/dashboard
|
||||
110
README.md
@@ -1,4 +1,4 @@
|
||||
# <img src="pkg/dashboard/static/logo.png" height=30 style="height: 2rem"> Helm Dashboard
|
||||
# 
|
||||
|
||||
A simplified way of working with Helm.
|
||||
|
||||
@@ -6,11 +6,23 @@ A simplified way of working with Helm.
|
||||
|
||||
## What it Does?
|
||||
|
||||
The _Helm Dashboard_ plugin offers a UI-driven way to view the installed Helm charts, see their revision history and corresponding k8s resources. Also, you can perform simple actions like roll back to a revision or upgrade to newer version.
|
||||
The _Helm Dashboard_ plugin offers a UI-driven way to view the installed Helm charts, see their revision history and
|
||||
corresponding k8s resources. Also, you can perform simple actions like roll back to a revision or upgrade to newer
|
||||
version.
|
||||
|
||||
This project is part of [Komodor's](https://komodor.com/?utm_campaign=Helm-Dash&utm_source=helm-dash-gh) vision of helping Kubernetes users to navigate and troubleshoot their clusters.
|
||||
This project is part of [Komodor's](https://komodor.com/?utm_campaign=Helm-Dash&utm_source=helm-dash-gh) vision of
|
||||
helping Kubernetes users to navigate and troubleshoot their clusters.
|
||||
|
||||
## Installing
|
||||
Some of the key capabilities of the tool:
|
||||
|
||||
- See all installed charts and their revision history
|
||||
- See manifest diff of the past revisions
|
||||
- Browse k8s resources resulting from the chart
|
||||
- Easy rollback or upgrade version with a clear and easy manifest diff
|
||||
- Integration with popular problem scanners
|
||||
- Easy switch between multiple clusters
|
||||
|
||||
## Installing
|
||||
|
||||
To install it, simply run Helm command:
|
||||
|
||||
@@ -19,6 +31,7 @@ helm plugin install https://github.com/komodorio/helm-dashboard.git
|
||||
```
|
||||
|
||||
To update the plugin to the latest version, run:
|
||||
|
||||
```shell
|
||||
helm plugin update dashboard
|
||||
```
|
||||
@@ -29,74 +42,58 @@ To uninstall, run:
|
||||
helm plugin uninstall dashboard
|
||||
```
|
||||
|
||||
> In case standard Helm plugin way did not work for you, you can just download the appropriate [release package](https://github.com/komodorio/helm-dashboard/releases) for your platform, unpack it and just run `dashboard` binary from it.
|
||||
|
||||
## Running
|
||||
|
||||
To use the plugin, your machine needs to have working `helm` and also `kubectl` commands.
|
||||
To use the plugin, your machine needs to have working `helm` and also `kubectl` commands. Helm version 3.4.0+ is required.
|
||||
|
||||
After installing, start the UI by running:
|
||||
|
||||
```shell
|
||||
helm dashboard
|
||||
```
|
||||
|
||||
The command above will launch the local Web server and will open the UI in new browser tab. The command will hang waiting for you to terminate it in command-line or web UI.
|
||||
The command above will launch the local Web server and will open the UI in new browser tab. The command will hang
|
||||
waiting for you to terminate it in command-line or web UI.
|
||||
|
||||
By default, the web server is only available locally. You can change that by specifying `HD_BIND` environment variable to the desired value. For example, `0.0.0.0` would bind to all IPv4 addresses or `[::0]` would be all IPv6 addresses.
|
||||
You can see the list of available command-line flags by running `helm dashboard --help`.
|
||||
|
||||
If your port 8080 is busy, you can specify a different port to use via `HD_PORT` environment variable.
|
||||
By default, the web server is only available locally. You can change that by specifying `HD_BIND` environment variable
|
||||
to the desired value. For example, `0.0.0.0` would bind to all IPv4 addresses or `[::0]` would be all IPv6 addresses.
|
||||
This can also be specified using flag `--bind <host>`, for example `--bind=0.0.0.0` or `--bind 0.0.0.0`.
|
||||
> Precedence order: flag `--bind=<host>` > env `HD_BIND=<host>` > default value `localhost`
|
||||
|
||||
If you don't want browser tab to automatically open, set `HD_NOBROWSER=1` in your environment variables.
|
||||
If your port 8080 is busy, you can specify a different port to use via `--port <number>` command-line flag.
|
||||
|
||||
If you want to increase the logging verbosity and see all the debug info, set `DEBUG=1` environment variable.
|
||||
If you need to limit the operations to a specific namespace, please use `--namespace=...` in your command-line.
|
||||
|
||||
If you don't want browser tab to automatically open, add `--no-browser` flag in your command line.
|
||||
|
||||
If you want to increase the logging verbosity and see all the debug info, use the `--verbose` flag.
|
||||
|
||||
> Disclaimer: For the sake of improving the project quality, there is user analytics collected by the tool. You can disable this collecting with `--no-analytics` option. The collection is done via DataDog RUM and Heap Analytics. Only the anonymous data is collected, no sensitive information is used.
|
||||
|
||||
## Scanner Integrations
|
||||
|
||||
Upon startup, Helm Dashboard detects the presence of [Trivy](https://github.com/aquasecurity/trivy)
|
||||
and [Checkov](https://github.com/bridgecrewio/checkov) scanners. When available, these scanners are offered on k8s
|
||||
resources page, as well as install/upgrade preview page.
|
||||
|
||||
You can request scanning of the specific k8s resource in your cluster:
|
||||

|
||||
|
||||
If you want to validate the k8s manifest prior to installing/reconfiguring a Helm chart, look for "Scan for Problems"
|
||||
button at the bottom of the dialog:
|
||||

|
||||
|
||||
## Support Channels
|
||||
|
||||
We have two main channels for supporting the Helm Dashboard users: [Slack community](https://komodorkommunity.slack.com/archives/C044U1B0265) for general conversations
|
||||
We have two main channels for supporting the Helm Dashboard
|
||||
users: [Slack community](https://join.slack.com/t/komodorkommunity/shared_invite/zt-1dm3cnkue-ov1Yh~_95teA35QNx5yuMg) for general conversations
|
||||
and [GitHub issues](https://github.com/komodorio/helm-dashboard/issues) for real bugs.
|
||||
|
||||
|
||||
## Roadmap & Ideas
|
||||
|
||||
### First Public Version
|
||||
|
||||
- CLI launcher
|
||||
- Web Server with REST API
|
||||
- Listing the installed applications
|
||||
- View k8s resources created by the application (describe, status)
|
||||
- Viewing revision history for application
|
||||
- View manifest diffs between revisions, also changelogs etc
|
||||
- Analytics reporting (telemetry)
|
||||
- Rollback to a revision
|
||||
- Check for repo updates & upgrade flow
|
||||
- Uninstalling the app completely
|
||||
- Switch clusters
|
||||
- Show manifest/describe upon clicking on resource
|
||||
|
||||
- Helm Plugin Packaging
|
||||
- Styled properly
|
||||
|
||||
### Further Ideas
|
||||
- solve umbrella-chart case
|
||||
- use `--dry-run` instead of `template`
|
||||
- Have cleaner idea on the web API structure
|
||||
- Recognise & show ArgoCD-originating charts/objects, those `helm ls` does not show
|
||||
- Recognise the revisions that are rollbacks by their description and mark in timeline
|
||||
|
||||
#### Topic "Validating Manifests"
|
||||
|
||||
- Validate manifests before deploy and get better errors
|
||||
- See if we can build in Chechov or Validkube validation
|
||||
|
||||
#### Iteration "Value Setting"
|
||||
|
||||
- Setting parameter values and installing
|
||||
- Reconfiguring the application
|
||||
|
||||
#### Iteration "Repo View"
|
||||
|
||||
- Browsing repositories
|
||||
- Adding new repository
|
||||
- Installing new app from repo
|
||||
|
||||
## Local Dev Testing
|
||||
|
||||
Prerequisites: `helm` and `kubectl` binaries installed and operational.
|
||||
@@ -107,13 +104,16 @@ There is a need to build binary for plugin to function, run:
|
||||
go build -o bin/dashboard .
|
||||
```
|
||||
|
||||
You can just run the `bin/dashboard` binary directly, it will just work.
|
||||
|
||||
To install, checkout the source code and run from source dir:
|
||||
|
||||
```shell
|
||||
helm plugin install .
|
||||
```
|
||||
|
||||
Local installation of plugin just creates a symlink, so making the changes and rebuilding the binary would not require to
|
||||
Local installation of plugin just creates a symlink, so making the changes and rebuilding the binary would not require
|
||||
to
|
||||
reinstall a plugin.
|
||||
|
||||
To use the plugin, run in your terminal:
|
||||
|
||||
103
go.mod
@@ -3,104 +3,69 @@ module github.com/komodorio/helm-dashboard
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/eko/gocache/v3 v3.1.1
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hexops/gotextdiff v1.0.3
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/toqueteos/webbrowser v1.2.0
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
helm.sh/helm/v3 v3.9.4
|
||||
k8s.io/kubectl v0.24.2
|
||||
k8s.io/apimachinery v0.25.0-alpha.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-logr/logr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/goccy/go-json v0.9.11 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/lithammer/dedent v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/pegasus-kv/thrift v0.13.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.3 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday v1.5.2 // indirect
|
||||
github.com/spf13/cobra v1.4.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.2 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.33.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.24.2 // indirect
|
||||
k8s.io/apimachinery v0.24.2 // indirect
|
||||
k8s.io/cli-runtime v0.24.2 // indirect
|
||||
k8s.io/client-go v0.24.2 // indirect
|
||||
k8s.io/component-base v0.24.2 // indirect
|
||||
k8s.io/component-helpers v0.24.2 // indirect
|
||||
k8s.io/klog/v2 v2.60.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect
|
||||
k8s.io/metrics v0.24.2 // indirect
|
||||
k8s.io/klog/v2 v2.70.0 // indirect
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.11.4 // indirect
|
||||
sigs.k8s.io/kustomize/kustomize/v4 v4.5.4 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
539
go.sum
@@ -13,11 +13,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
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=
|
||||
@@ -26,7 +21,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
@@ -37,138 +31,89 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/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/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
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 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10=
|
||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY=
|
||||
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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
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/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/coocood/freecache v1.2.1 h1:/v1CqMq45NFH9mp/Pt142reundeBM0dVUD3osQBeu/U=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/eko/gocache/v3 v3.1.1 h1:r3CBwLnqPkcK56h9Do2CWw1kZ4TeKK0wDE1Oo/YZnhs=
|
||||
github.com/eko/gocache/v3 v3.1.1/go.mod h1:UpP/LyHAioP/a/dizgl0MpgZ3A3CkS4NbG/mWkGTQ9M=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/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/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
|
||||
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
|
||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
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-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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
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-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
@@ -177,18 +122,21 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/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/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/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/groupcache v0.0.0-20210331224755-41bb18bfe9da/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=
|
||||
@@ -196,7 +144,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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=
|
||||
@@ -212,17 +161,10 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
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/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e h1:KhcknUwkWHKZPbFy2P7jH5LKJ3La+0ZeknkkmrSgqb0=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
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/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
||||
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=
|
||||
@@ -230,19 +172,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/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/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
@@ -250,79 +189,42 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
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 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.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.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0 h1:fWY+zXdWhvWndXqnMj4SyC/vi8sK508OjhGCtMzsA9M=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
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/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
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/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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/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/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
@@ -332,206 +234,126 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
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/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
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/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pegasus-kv/thrift v0.13.0 h1:4ESwaNoHImfbHa9RUGJiJZ4hrxorihZHk5aarYwY8d4=
|
||||
github.com/pegasus-kv/thrift v0.13.0/go.mod h1:Gl9NT/WHG6ABm6NsrbfE8LiJN0sAyneCrvB4qN4NPqQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.3 h1:h9JoA60e1dVEOpp0PFwJSmt1Htu057NUq9/bUwaO61s=
|
||||
github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
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 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.33.0 h1:rHgav/0a6+uYgGdNt3jwz8FNSesO/Hsang3O0T9A5SE=
|
||||
github.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
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 v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs=
|
||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
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/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/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/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
|
||||
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
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.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
|
||||
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
|
||||
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
|
||||
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
|
||||
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
|
||||
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
|
||||
go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
|
||||
go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
|
||||
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-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-20190820162420-60c769a6c586/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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -544,6 +366,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
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/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw=
|
||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
|
||||
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=
|
||||
@@ -556,8 +380,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
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/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/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=
|
||||
@@ -566,16 +388,11 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
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/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-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=
|
||||
@@ -587,7 +404,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-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-20191105084925-a882066a44e0/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=
|
||||
@@ -598,40 +416,24 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
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/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
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=
|
||||
@@ -642,12 +444,12 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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=
|
||||
@@ -658,12 +460,9 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/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=
|
||||
@@ -678,74 +477,56 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-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-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
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-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/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-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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -765,7 +546,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -773,20 +553,10 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
@@ -804,12 +574,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||
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=
|
||||
@@ -841,26 +605,12 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -873,16 +623,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
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=
|
||||
@@ -895,26 +635,26 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
||||
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=
|
||||
@@ -922,13 +662,9 @@ 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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY=
|
||||
helm.sh/helm/v3 v3.9.4/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -938,35 +674,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
|
||||
k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
|
||||
k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
|
||||
k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
|
||||
k8s.io/cli-runtime v0.24.2 h1:KxY6tSgPGsahA6c1/dmR3uF5jOxXPx2QQY6C5ZrLmtE=
|
||||
k8s.io/cli-runtime v0.24.2/go.mod h1:1LIhKL2RblkhfG4v5lZEt7FtgFG5mVb8wqv5lE9m5qY=
|
||||
k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
|
||||
k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
|
||||
k8s.io/code-generator v0.24.2/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
|
||||
k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU=
|
||||
k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM=
|
||||
k8s.io/component-helpers v0.24.2 h1:gtXmI/TjVINtkAdZn7m5p8+Vd0Mk4d1q8kwJMMLBdwY=
|
||||
k8s.io/component-helpers v0.24.2/go.mod h1:TRQPBQKfmqkmV6c0HAmUs8cXVNYYYLsXy4zu8eODi9g=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.25.0-alpha.2 h1:y6uTWaiqsPTPRewnXJ15IFyGmBo2qPt6enm4zszG8Z0=
|
||||
k8s.io/apimachinery v0.25.0-alpha.2/go.mod h1:h34FtK3eCxige6ZIACdBSYExtDaKAUxoc7hVe2LOxzw=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=
|
||||
k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk=
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs=
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0=
|
||||
k8s.io/kubectl v0.24.2 h1:+RfQVhth8akUmIc2Ge8krMl/pt66V7210ka3RE/p0J4=
|
||||
k8s.io/kubectl v0.24.2/go.mod h1:+HIFJc0bA6Tzu5O/YcuUt45APAxnNL8LeMuXwoiGsPg=
|
||||
k8s.io/metrics v0.24.2 h1:3lgEq973VGPWAEaT9VI/p0XmI0R5kJgb/r9Ufr5fz8k=
|
||||
k8s.io/metrics v0.24.2/go.mod h1:5NWURxZ6Lz5gj8TFU83+vdWIVASx7W8lwPpHYCqopMo=
|
||||
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/klog/v2 v2.70.0 h1:GMmmjoFOrNepPN0ZeGCzvD2Gh5IKRwdFx8W5PBxVTQU=
|
||||
k8s.io/klog/v2 v2.70.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
@@ -974,16 +691,8 @@ 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/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
|
||||
sigs.k8s.io/kustomize/api v0.11.4 h1:/0Mr3kfBBNcNPOW5Qwk/3eb8zkswCwnqQxxKtmrTkRo=
|
||||
sigs.k8s.io/kustomize/api v0.11.4/go.mod h1:k+8RsqYbgpkIrJ4p9jcdPqe8DprLxFUUO0yNOq8C+xI=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9WCER0uBS1SW2Rco=
|
||||
sigs.k8s.io/kustomize/kustomize/v4 v4.5.4 h1:rzGrL+DA4k8bT6SMz7/U+2z3iiZf1t2RaYJWx8OeTmE=
|
||||
sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.6 h1:eF+wsn4J7GOAXlvajv6OknSunxpcOBQQqsnPxObtkGs=
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
99
main.go
@@ -1,50 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/toqueteos/webbrowser"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jessevdk/go-flags"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard"
|
||||
"github.com/pkg/browser"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
version = "0.0.0"
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
setupLogging()
|
||||
type options struct {
|
||||
Version bool `long:"version" description:"Show tool version"`
|
||||
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
|
||||
NoBrowser bool `short:"b" long:"no-browser" description:"Do not attempt to open Web browser upon start"`
|
||||
NoTracking bool `long:"no-analytics" description:"Disable user analytics (GA, DataDog etc.)"`
|
||||
BindHost string `long:"bind" description:"Host binding to start server (default: localhost)"` // default should be printed but not assigned as the precedence: flag > env > default
|
||||
Port uint `short:"p" long:"port" description:"Port to start server on" default:"8080"` // TODO: better default port to clash less?
|
||||
Namespace string `short:"n" long:"namespace" description:"Limit operations to a specific namespace"`
|
||||
}
|
||||
|
||||
// TODO: proper command-line parsing
|
||||
if len(os.Args) > 1 { // dirty thing to allow --help to work
|
||||
os.Exit(0)
|
||||
func main() {
|
||||
opts := parseFlags()
|
||||
if opts.BindHost == "" {
|
||||
host := os.Getenv("HD_BIND")
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
opts.BindHost = host
|
||||
}
|
||||
|
||||
address, webServerDone := dashboard.StartServer()
|
||||
setupLogging(opts.Verbose)
|
||||
|
||||
if os.Getenv("HD_NOBROWSER") == "" {
|
||||
server := dashboard.Server{
|
||||
Version: version,
|
||||
Namespace: opts.Namespace,
|
||||
Address: fmt.Sprintf("%s:%d", opts.BindHost, opts.Port),
|
||||
Debug: opts.Verbose,
|
||||
NoTracking: opts.NoTracking,
|
||||
}
|
||||
address, webServerDone := server.StartServer()
|
||||
|
||||
if !opts.NoTracking {
|
||||
log.Infof("User analytics is collected to improve the quality, disable it with --no-analytics")
|
||||
}
|
||||
|
||||
if opts.NoBrowser {
|
||||
log.Infof("Access web UI at: %s", address)
|
||||
} else {
|
||||
log.Infof("Opening web UI: %s", address)
|
||||
err := webbrowser.Open(address)
|
||||
err := browser.OpenURL(address)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to open Web browser for URL: %s", err)
|
||||
}
|
||||
} else {
|
||||
log.Infof("Access web UI at: %s", address)
|
||||
}
|
||||
|
||||
<-webServerDone
|
||||
log.Infof("Done.")
|
||||
}
|
||||
|
||||
func setupLogging() {
|
||||
if os.Getenv("DEBUG") == "" {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
} else {
|
||||
func parseFlags() options {
|
||||
ns := os.Getenv("HELM_NAMESPACE")
|
||||
if ns == "default" {
|
||||
ns = ""
|
||||
}
|
||||
|
||||
opts := options{Namespace: ns}
|
||||
args, err := flags.Parse(&opts)
|
||||
if err != nil {
|
||||
if e, ok := err.(*flags.Error); ok {
|
||||
if e.Type == flags.ErrHelp {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// we rely on default behavior to print the problem inside `flags` library
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.Version {
|
||||
fmt.Println(version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
panic("The program does not take argumants, see --help for usage")
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func setupLogging(verbose bool) {
|
||||
if verbose {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
gin.SetMode(gin.DebugMode)
|
||||
log.Debugf("Debug logging is enabled")
|
||||
} else {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
log.Infof("Helm Dashboard by Komodor, version %s (%s @ %s)", version, commit, date)
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@ package dashboard
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/handlers"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//go:embed static/*
|
||||
@@ -33,9 +35,26 @@ func errorHandler(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouter(abortWeb ControlChan, data *DataLayer) *gin.Engine {
|
||||
func contextSetter(data *subproc.DataLayer) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if ctx, ok := c.Request.Header["X-Kubecontext"]; ok {
|
||||
log.Debugf("Setting current context to: %s", ctx)
|
||||
if data.KubeContext != ctx[0] {
|
||||
err := data.Cache.Clear()
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
data.KubeContext = ctx[0]
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouter(abortWeb utils.ControlChan, data *subproc.DataLayer, debug bool) *gin.Engine {
|
||||
var api *gin.Engine
|
||||
if os.Getenv("DEBUG") == "" {
|
||||
if debug {
|
||||
api = gin.New()
|
||||
api.Use(gin.Recovery())
|
||||
} else {
|
||||
@@ -52,35 +71,64 @@ func NewRouter(abortWeb ControlChan, data *DataLayer) *gin.Engine {
|
||||
return api
|
||||
}
|
||||
|
||||
func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
|
||||
func configureRoutes(abortWeb utils.ControlChan, data *subproc.DataLayer, api *gin.Engine) {
|
||||
// server shutdown handler
|
||||
api.DELETE("/", func(c *gin.Context) {
|
||||
abortWeb <- struct{}{}
|
||||
c.Status(http.StatusAccepted)
|
||||
})
|
||||
|
||||
api.GET("/status", func(c *gin.Context) {
|
||||
c.Header("X-Application-Name", "Helm Dashboard by Komodor.io") // to identify ourselves by ourselves
|
||||
c.IndentedJSON(http.StatusOK, data.GetStatus())
|
||||
})
|
||||
|
||||
api.GET("/api/cache", func(c *gin.Context) {
|
||||
c.IndentedJSON(http.StatusOK, data.Cache)
|
||||
})
|
||||
|
||||
api.DELETE("/api/cache", func(c *gin.Context) {
|
||||
err := data.Cache.Clear()
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusAccepted)
|
||||
})
|
||||
|
||||
configureHelms(api.Group("/api/helm"), data)
|
||||
configureKubectls(api.Group("/api/kube"), data)
|
||||
configureScanners(api.Group("/api/scanners"), data)
|
||||
}
|
||||
|
||||
func configureHelms(api *gin.RouterGroup, data *DataLayer) {
|
||||
h := HelmHandler{Data: data}
|
||||
func configureHelms(api *gin.RouterGroup, data *subproc.DataLayer) {
|
||||
h := handlers.HelmHandler{Data: data}
|
||||
|
||||
api.GET("/charts", h.GetCharts)
|
||||
api.DELETE("/charts", h.Uninstall)
|
||||
api.POST("/charts/rollback", h.Rollback)
|
||||
|
||||
api.GET("/charts/history", h.History)
|
||||
api.GET("/charts/resources", h.Resources)
|
||||
api.GET("/charts/:section", h.GetInfoSection)
|
||||
api.GET("/charts/show", h.Show)
|
||||
api.POST("/charts/install", h.Install)
|
||||
api.POST("/charts/rollback", h.Rollback)
|
||||
|
||||
api.GET("/repo", h.RepoList)
|
||||
api.POST("/repo", h.RepoAdd)
|
||||
api.DELETE("/repo", h.RepoDelete)
|
||||
api.GET("/repo/charts", h.RepoCharts)
|
||||
api.GET("/repo/search", h.RepoSearch)
|
||||
api.POST("/repo/update", h.RepoUpdate)
|
||||
api.GET("/charts/install", h.InstallPreview)
|
||||
api.POST("/charts/install", h.Install)
|
||||
api.GET("/charts/:section", h.GetInfoSection)
|
||||
api.GET("/repo/values", h.RepoValues)
|
||||
}
|
||||
|
||||
func configureKubectls(api *gin.RouterGroup, data *DataLayer) {
|
||||
h := KubeHandler{Data: data}
|
||||
func configureKubectls(api *gin.RouterGroup, data *subproc.DataLayer) {
|
||||
h := handlers.KubeHandler{Data: data}
|
||||
api.GET("/contexts", h.GetContexts)
|
||||
api.GET("/resources/:kind", h.GetResourceInfo)
|
||||
api.GET("/describe/:kind", h.Describe)
|
||||
api.GET("/namespaces", h.GetNameSpaces)
|
||||
}
|
||||
|
||||
func configureStatic(api *gin.Engine) {
|
||||
@@ -113,36 +161,9 @@ func configureStatic(api *gin.Engine) {
|
||||
}
|
||||
}
|
||||
|
||||
func contextSetter(data *DataLayer) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if context, ok := c.Request.Header["X-Kubecontext"]; ok {
|
||||
log.Debugf("Setting current context to: %s", context)
|
||||
data.KubeContext = context[0]
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
type QueryProps struct {
|
||||
Namespace string
|
||||
Name string
|
||||
Revision int
|
||||
}
|
||||
|
||||
func getQueryProps(c *gin.Context, revRequired bool) (*QueryProps, error) {
|
||||
qp := QueryProps{}
|
||||
|
||||
qp.Namespace = c.Query("namespace")
|
||||
qp.Name = c.Query("name")
|
||||
if qp.Name == "" {
|
||||
return nil, errors.New("missing required query string parameter: name")
|
||||
}
|
||||
|
||||
cRev, err := strconv.Atoi(c.Query("revision"))
|
||||
if err != nil && revRequired {
|
||||
return nil, err
|
||||
}
|
||||
qp.Revision = cRev
|
||||
|
||||
return &qp, nil
|
||||
func configureScanners(api *gin.RouterGroup, data *subproc.DataLayer) {
|
||||
h := handlers.ScannersHandler{Data: data}
|
||||
api.GET("", h.List)
|
||||
api.POST("/manifests", h.ScanDraftManifest)
|
||||
api.GET("/resource/:kind", h.ScanResource)
|
||||
}
|
||||
|
||||
@@ -1,442 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hexops/gotextdiff"
|
||||
"github.com/hexops/gotextdiff/myers"
|
||||
"github.com/hexops/gotextdiff/span"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"io/ioutil"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CmdError struct {
|
||||
Command []string
|
||||
OrigError error
|
||||
StdErr []byte
|
||||
}
|
||||
|
||||
func (e CmdError) Error() string {
|
||||
//return fmt.Sprintf("failed to run command %s:\nError: %s\nSTDERR:%s", e.Command, e.OrigError, e.StdErr)
|
||||
return string(e.StdErr)
|
||||
}
|
||||
|
||||
type DataLayer struct {
|
||||
KubeContext string
|
||||
Helm string
|
||||
Kubectl string
|
||||
}
|
||||
|
||||
func (d *DataLayer) runCommand(cmd ...string) (string, error) {
|
||||
log.Debugf("Starting command: %s", cmd)
|
||||
prog := exec.Command(cmd[0], cmd[1:]...)
|
||||
prog.Env = os.Environ()
|
||||
prog.Env = append(prog.Env, "HELM_KUBECONTEXT="+d.KubeContext)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
prog.Stdout = &stdout
|
||||
|
||||
var stderr bytes.Buffer
|
||||
prog.Stderr = &stderr
|
||||
|
||||
if err := prog.Run(); err != nil {
|
||||
log.Warnf("Failed command: %s", cmd)
|
||||
serr := stderr.Bytes()
|
||||
if serr != nil {
|
||||
log.Warnf("STDERR:\n%s", serr)
|
||||
}
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
return "", CmdError{
|
||||
Command: cmd,
|
||||
StdErr: serr,
|
||||
OrigError: eerr,
|
||||
}
|
||||
}
|
||||
|
||||
return "", CmdError{
|
||||
Command: cmd,
|
||||
StdErr: serr,
|
||||
OrigError: err,
|
||||
}
|
||||
}
|
||||
|
||||
sout := stdout.Bytes()
|
||||
serr := stderr.Bytes()
|
||||
log.Debugf("Command STDOUT:\n%s", sout)
|
||||
log.Debugf("Command STDERR:\n%s", serr)
|
||||
return string(sout), nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) runCommandHelm(cmd ...string) (string, error) {
|
||||
if d.Helm == "" {
|
||||
d.Helm = "helm"
|
||||
}
|
||||
|
||||
cmd = append([]string{d.Helm}, cmd...)
|
||||
if d.KubeContext != "" {
|
||||
cmd = append(cmd, "--kube-context", d.KubeContext)
|
||||
}
|
||||
|
||||
return d.runCommand(cmd...)
|
||||
}
|
||||
|
||||
func (d *DataLayer) runCommandKubectl(cmd ...string) (string, error) {
|
||||
// TODO: migrate into using kubectl "k8s.io/kubectl/pkg/cmd" and kube API
|
||||
if d.Kubectl == "" {
|
||||
d.Kubectl = "kubectl"
|
||||
}
|
||||
|
||||
cmd = append([]string{d.Kubectl}, cmd...)
|
||||
|
||||
if d.KubeContext != "" {
|
||||
cmd = append(cmd, "--context", d.KubeContext)
|
||||
}
|
||||
|
||||
return d.runCommand(cmd...)
|
||||
}
|
||||
|
||||
func (d *DataLayer) CheckConnectivity() error {
|
||||
contexts, err := d.ListContexts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(contexts) < 1 {
|
||||
return errors.New("did not find any kubectl contexts configured")
|
||||
}
|
||||
|
||||
_, err = d.runCommandHelm("--help") // no point in doing is, since the default context may be invalid
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type KubeContext struct {
|
||||
IsCurrent bool
|
||||
Name string
|
||||
Cluster string
|
||||
AuthInfo string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
|
||||
out, err := d.runCommandKubectl("config", "get-contexts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// kubectl has no JSON output for it, we'll have to do custom text parsing
|
||||
lines := strings.Split(out, "\n")
|
||||
|
||||
// find field positions
|
||||
fields := regexp.MustCompile(`(\w+\s+)`).FindAllString(lines[0], -1)
|
||||
cur := len(fields[0])
|
||||
name := cur + len(fields[1])
|
||||
cluster := name + len(fields[2])
|
||||
auth := cluster + len(fields[3])
|
||||
|
||||
// read items
|
||||
for _, line := range lines[1:] {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, KubeContext{
|
||||
IsCurrent: strings.TrimSpace(line[0:cur]) == "*",
|
||||
Name: strings.TrimSpace(line[cur:name]),
|
||||
Cluster: strings.TrimSpace(line[name:cluster]),
|
||||
AuthInfo: strings.TrimSpace(line[cluster:auth]),
|
||||
Namespace: strings.TrimSpace(line[auth:]),
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ListInstalled() (res []releaseElement, err error) {
|
||||
out, err := d.runCommandHelm("ls", "--all", "--all-namespaces", "--output", "json", "--time-format", time.RFC3339)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartHistory(namespace string, chartName string) (res []*historyElement, err error) {
|
||||
// TODO: there is `max` but there is no `offset`
|
||||
out, err := d.runCommandHelm("history", chartName, "--namespace", namespace, "--output", "json", "--max", "18")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, elm := range res {
|
||||
chartRepoName, curVer, err := chartAndVersion(elm.Chart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elm.ChartName = chartRepoName
|
||||
elm.ChartVer = curVer
|
||||
elm.Updated.Time = elm.Updated.Time.Round(time.Second)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoVersions(chartName string) (res []repoChartElement, err error) {
|
||||
cmd := []string{"search", "repo", "--regexp", "/" + chartName + "\v", "--versions", "--output", "json"}
|
||||
out, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type SectionFn = func(string, string, int, bool) (string, error) // TODO: rework it into struct-based argument?
|
||||
|
||||
func (d *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
cmd := []string{"get", "manifest", chartName, "--namespace", namespace}
|
||||
if revision > 0 {
|
||||
cmd = append(cmd, "--revision", strconv.Itoa(revision))
|
||||
}
|
||||
|
||||
out, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string, revision int) ([]*GenericResource, error) {
|
||||
out, err := d.RevisionManifests(namespace, chartName, revision, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec := yaml.NewDecoder(bytes.NewReader([]byte(out)))
|
||||
|
||||
res := make([]*GenericResource, 0)
|
||||
var tmp interface{}
|
||||
for dec.Decode(&tmp) == nil {
|
||||
// k8s libs uses only JSON tags defined, say hello to https://github.com/go-yaml/yaml/issues/424
|
||||
// bug we can juggle it
|
||||
jsoned, err := json.Marshal(tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var doc GenericResource
|
||||
err = json.Unmarshal(jsoned, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, &doc)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionNotes(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
out, err := d.runCommandHelm("get", "notes", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionValues(namespace string, chartName string, revision int, onlyUserDefined bool) (res string, err error) {
|
||||
cmd := []string{"get", "values", chartName, "--namespace", namespace, "--output", "yaml"}
|
||||
|
||||
if revision > 0 {
|
||||
cmd = append(cmd, "--revision", strconv.Itoa(revision))
|
||||
}
|
||||
|
||||
if !onlyUserDefined {
|
||||
cmd = append(cmd, "--all")
|
||||
}
|
||||
out, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) GetResource(namespace string, def *GenericResource) (*GenericResource, error) {
|
||||
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "json")
|
||||
if err != nil {
|
||||
if strings.HasSuffix(strings.TrimSpace(err.Error()), " not found") {
|
||||
return &GenericResource{
|
||||
Status: v1.CarpStatus{
|
||||
Phase: "NotFound",
|
||||
Message: err.Error(),
|
||||
Reason: "not found",
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var res GenericResource
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(res.Status.Conditions, func(i, j int) bool {
|
||||
// some condition types always bubble up
|
||||
if res.Status.Conditions[i].Type == "Available" {
|
||||
return false
|
||||
}
|
||||
|
||||
if res.Status.Conditions[j].Type == "Available" {
|
||||
return true
|
||||
}
|
||||
|
||||
t1 := res.Status.Conditions[i].LastTransitionTime
|
||||
t2 := res.Status.Conditions[j].LastTransitionTime
|
||||
return t1.Time.Before(t2.Time)
|
||||
})
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) DescribeResource(namespace string, kind string, name string) (string, error) {
|
||||
out, err := d.runCommandKubectl("describe", strings.ToLower(kind), name, "--namespace", namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) UninstallChart(namespace string, name string) error {
|
||||
_, err := d.runCommandHelm("uninstall", name, "--namespace", namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) Revert(namespace string, name string, rev int) error {
|
||||
_, err := d.runCommandHelm("rollback", name, strconv.Itoa(rev), "--namespace", namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoUpdate(name string) error {
|
||||
cmd := []string{"repo", "update"}
|
||||
if name != "" {
|
||||
cmd = append(cmd, name)
|
||||
}
|
||||
|
||||
_, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartUpgrade(namespace string, name string, repoChart string, version string, justTemplate bool) (string, error) {
|
||||
oldVals, err := d.RevisionValues(namespace, name, 0, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
file, err := ioutil.TempFile("", "helm_vals_")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
err = ioutil.WriteFile(file.Name(), []byte(oldVals), 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd := []string{"upgrade", name, repoChart, "--version", version, "--namespace", namespace, "--values", file.Name(), "--output", "json"}
|
||||
if justTemplate {
|
||||
cmd = append(cmd, "--dry-run")
|
||||
}
|
||||
|
||||
out, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if justTemplate {
|
||||
res := release.Release{}
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
manifests, err := d.RevisionManifests(namespace, name, 0, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out = getDiff(strings.TrimSpace(manifests), strings.TrimSpace(res.Manifest), "current.yaml", "upgraded.yaml")
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) {
|
||||
if revision1 == 0 || revision2 == 0 {
|
||||
log.Debugf("One of revisions is zero: %d %d", revision1, revision2)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
manifest1, err := functor(namespace, name, revision1, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
manifest2, err := functor(namespace, name, revision2, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
diff := getDiff(manifest1, manifest2, strconv.Itoa(revision1)+ext, strconv.Itoa(revision2)+ext)
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func getDiff(text1 string, text2 string, name1 string, name2 string) string {
|
||||
edits := myers.ComputeEdits(span.URIFromPath(""), text1, text2)
|
||||
unified := gotextdiff.ToUnified(name1, name2, text1, edits)
|
||||
diff := fmt.Sprint(unified)
|
||||
log.Debugf("The diff is: %s", diff)
|
||||
return diff
|
||||
}
|
||||
|
||||
type GenericResource = v1.Carp
|
||||
@@ -1,16 +1,18 @@
|
||||
package dashboard
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
)
|
||||
|
||||
type HelmHandler struct {
|
||||
Data *DataLayer
|
||||
Data *subproc.DataLayer
|
||||
}
|
||||
|
||||
func (h *HelmHandler) GetCharts(c *gin.Context) {
|
||||
@@ -22,15 +24,13 @@ func (h *HelmHandler) GetCharts(c *gin.Context) {
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
// TODO: helm show chart komodorio/k8s-watcher to get the icon URL
|
||||
|
||||
func (h *HelmHandler) Uninstall(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
err = h.Data.UninstallChart(qp.Namespace, qp.Name)
|
||||
err = h.Data.ReleaseUninstall(qp.Namespace, qp.Name)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
@@ -39,13 +39,13 @@ func (h *HelmHandler) Uninstall(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *HelmHandler) Rollback(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, true)
|
||||
qp, err := utils.GetQueryProps(c, true)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.Data.Revert(qp.Namespace, qp.Name, qp.Revision)
|
||||
err = h.Data.Rollback(qp.Namespace, qp.Name, qp.Revision)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
@@ -54,13 +54,13 @@ func (h *HelmHandler) Rollback(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *HelmHandler) History(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.Data.ChartHistory(qp.Namespace, qp.Name)
|
||||
res, err := h.Data.ReleaseHistory(qp.Namespace, qp.Name)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
@@ -69,7 +69,7 @@ func (h *HelmHandler) History(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *HelmHandler) Resources(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, true)
|
||||
qp, err := utils.GetQueryProps(c, true)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
@@ -84,7 +84,7 @@ func (h *HelmHandler) Resources(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *HelmHandler) RepoSearch(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
@@ -98,8 +98,23 @@ func (h *HelmHandler) RepoSearch(c *gin.Context) {
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) RepoCharts(c *gin.Context) {
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.Data.ChartRepoCharts(qp.Name)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) RepoUpdate(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
@@ -113,34 +128,56 @@ func (h *HelmHandler) RepoUpdate(c *gin.Context) {
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) InstallPreview(c *gin.Context) {
|
||||
out, err := chartInstall(c, h.Data, true)
|
||||
func (h *HelmHandler) Show(c *gin.Context) {
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.Data.ShowChart(qp.Name)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, out)
|
||||
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) Install(c *gin.Context) {
|
||||
out, err := chartInstall(c, h.Data, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
justTemplate := c.Query("flag") != "true"
|
||||
isInitial := c.Query("initial") != "true"
|
||||
out, err := h.Data.ChartInstall(qp.Namespace, qp.Name, c.Query("chart"), c.Query("version"), justTemplate, c.PostForm("values"), isInitial)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := release.Release{}
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
if justTemplate {
|
||||
manifests := ""
|
||||
if isInitial {
|
||||
manifests, err = h.Data.RevisionManifests(qp.Namespace, qp.Name, 0, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
out = subproc.GetDiff(strings.TrimSpace(manifests), out, "current.yaml", "upgraded.yaml")
|
||||
} else {
|
||||
c.Header("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
c.IndentedJSON(http.StatusAccepted, res)
|
||||
c.String(http.StatusAccepted, out)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) GetInfoSection(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, true)
|
||||
qp, err := utils.GetQueryProps(c, true)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
@@ -156,21 +193,50 @@ func (h *HelmHandler) GetInfoSection(c *gin.Context) {
|
||||
c.String(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func chartInstall(c *gin.Context, data *DataLayer, justTemplate bool) (string, error) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
func (h *HelmHandler) RepoValues(c *gin.Context) {
|
||||
out, err := h.Data.ShowValues(c.Query("chart"), c.Query("version"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
out, err := data.ChartUpgrade(qp.Namespace, qp.Name, c.Query("chart"), c.Query("version"), justTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
c.String(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func handleGetSection(data *DataLayer, section string, rDiff string, qp *QueryProps, flag bool) (string, error) {
|
||||
sections := map[string]SectionFn{
|
||||
func (h *HelmHandler) RepoList(c *gin.Context) {
|
||||
out, err := h.Data.ChartRepoList()
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) RepoAdd(c *gin.Context) {
|
||||
_, err := h.Data.ChartRepoAdd(c.PostForm("name"), c.PostForm("url"))
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *HelmHandler) RepoDelete(c *gin.Context) {
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Data.ChartRepoDelete(qp.Name)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func handleGetSection(data *subproc.DataLayer, section string, rDiff string, qp *utils.QueryProps, flag bool) (string, error) {
|
||||
sections := map[string]subproc.SectionFn{
|
||||
"manifests": data.RevisionManifests,
|
||||
"values": data.RevisionValues,
|
||||
"notes": data.RevisionNotes,
|
||||
@@ -192,16 +258,16 @@ func handleGetSection(data *DataLayer, section string, rDiff string, qp *QueryPr
|
||||
ext = ".txt"
|
||||
}
|
||||
|
||||
res, err := RevisionDiff(functor, ext, qp.Namespace, qp.Name, cRevDiff, qp.Revision, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res, nil
|
||||
} else {
|
||||
res, err := functor(qp.Namespace, qp.Name, qp.Revision, flag)
|
||||
res, err := subproc.RevisionDiff(functor, ext, qp.Namespace, qp.Name, cRevDiff, qp.Revision, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res, err := functor(qp.Namespace, qp.Name, qp.Revision, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package dashboard
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
)
|
||||
|
||||
type KubeHandler struct {
|
||||
Data *DataLayer
|
||||
Data *subproc.DataLayer
|
||||
}
|
||||
|
||||
func (h *KubeHandler) GetContexts(c *gin.Context) {
|
||||
@@ -21,13 +24,13 @@ func (h *KubeHandler) GetContexts(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.Data.GetResource(qp.Namespace, &GenericResource{
|
||||
res, err := h.Data.GetResource(qp.Namespace, &v12.Carp{
|
||||
TypeMeta: v1.TypeMeta{Kind: c.Param("kind")},
|
||||
ObjectMeta: v1.ObjectMeta{Name: qp.Name},
|
||||
})
|
||||
@@ -36,6 +39,7 @@ func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// custom logic to provide most meaningful status for the resource
|
||||
if res.Status.Phase == "Active" || res.Status.Phase == "Error" {
|
||||
_ = res.Name + ""
|
||||
} else if res.Status.Phase == "" && len(res.Status.Conditions) > 0 {
|
||||
@@ -53,7 +57,7 @@ func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *KubeHandler) Describe(c *gin.Context) {
|
||||
qp, err := getQueryProps(c, false)
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
@@ -67,3 +71,13 @@ func (h *KubeHandler) Describe(c *gin.Context) {
|
||||
|
||||
c.String(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *KubeHandler) GetNameSpaces(c *gin.Context) {
|
||||
res, err := h.Data.GetNameSpaces()
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
||||
76
pkg/dashboard/handlers/scannerHandlers.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ScannersHandler struct {
|
||||
Data *subproc.DataLayer
|
||||
}
|
||||
|
||||
func (h *ScannersHandler) List(c *gin.Context) {
|
||||
type ScannerInfo struct {
|
||||
SupportedResourceKinds []string
|
||||
ManifestScannable bool
|
||||
}
|
||||
res := map[string]ScannerInfo{}
|
||||
for _, scanner := range h.Data.Scanners {
|
||||
res[scanner.Name()] = ScannerInfo{
|
||||
SupportedResourceKinds: scanner.SupportedResourceKinds(),
|
||||
ManifestScannable: scanner.ManifestIsScannable(),
|
||||
}
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *ScannersHandler) ScanDraftManifest(c *gin.Context) {
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
reuseVals := c.Query("initial") != "true"
|
||||
mnf, err := h.Data.ChartInstall(qp.Namespace, qp.Name, c.Query("chart"), c.Query("version"), true, c.PostForm("values"), reuseVals)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
reps := map[string]*subproc.ScanResults{}
|
||||
for _, scanner := range h.Data.Scanners {
|
||||
sr, err := scanner.ScanManifests(mnf)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
reps[scanner.Name()] = sr
|
||||
}
|
||||
|
||||
c.IndentedJSON(http.StatusOK, reps)
|
||||
}
|
||||
|
||||
func (h *ScannersHandler) ScanResource(c *gin.Context) {
|
||||
qp, err := utils.GetQueryProps(c, false)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
reps := map[string]*subproc.ScanResults{}
|
||||
for _, scanner := range h.Data.Scanners {
|
||||
sr, err := scanner.ScanResource(qp.Namespace, c.Param("kind"), qp.Name)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
reps[scanner.Name()] = sr
|
||||
}
|
||||
|
||||
c.IndentedJSON(http.StatusOK, reps)
|
||||
}
|
||||
164
pkg/dashboard/scanners/checkov.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package scanners
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Checkov struct {
|
||||
Data *subproc.DataLayer
|
||||
}
|
||||
|
||||
func (c *Checkov) ManifestIsScannable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Checkov) SupportedResourceKinds() []string {
|
||||
// from https://github.com/bridgecrewio/checkov//blob/master/docs/5.Policy%20Index/kubernetes.md
|
||||
return []string{
|
||||
"AdmissionConfiguration",
|
||||
"ClusterRole",
|
||||
"ClusterRoleBinding",
|
||||
"ConfigMap",
|
||||
"CronJob",
|
||||
"DaemonSet",
|
||||
"Deployment",
|
||||
"DeploymentConfig",
|
||||
"Ingress",
|
||||
"Job",
|
||||
"Pod",
|
||||
"PodSecurityPolicy",
|
||||
"PodTemplate",
|
||||
"Policy",
|
||||
"ReplicaSet",
|
||||
"ReplicationController",
|
||||
"Role",
|
||||
"RoleBinding",
|
||||
"Secret",
|
||||
"Service",
|
||||
"ServiceAccount",
|
||||
"StatefulSet",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Checkov) Name() string {
|
||||
return "Checkov"
|
||||
}
|
||||
|
||||
func (c *Checkov) Test() bool {
|
||||
utils.FailLogLevel = log.DebugLevel
|
||||
defer func() { utils.FailLogLevel = log.WarnLevel }()
|
||||
|
||||
res, err := utils.RunCommand([]string{"checkov", "--version"}, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
log.Infof("Discovered Checkov version: %s", strings.TrimSpace(res))
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Checkov) ScanManifests(mnf string) (*subproc.ScanResults, error) {
|
||||
fname, fclose, err := utils.TempFile(mnf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fclose()
|
||||
|
||||
cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "json", "--file", fname}
|
||||
out, err := utils.RunCommand(cmd, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &subproc.ScanResults{}
|
||||
|
||||
err = json.Unmarshal([]byte(out), res.OrigReport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Checkov) ScanResource(ns string, kind string, name string) (*subproc.ScanResults, error) {
|
||||
carp := v1.Carp{}
|
||||
carp.Kind = kind
|
||||
carp.Name = name
|
||||
mnf, err := c.Data.GetResourceYAML(ns, &carp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fname, fclose, err := utils.TempFile(mnf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fclose()
|
||||
|
||||
cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "json", "--file", fname}
|
||||
out, err := utils.RunCommand(cmd, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cr := CheckovReport{}
|
||||
err = json.Unmarshal([]byte(out), &cr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &subproc.ScanResults{
|
||||
PassedCount: cr.Summary.Passed,
|
||||
FailedCount: cr.Summary.Failed,
|
||||
OrigReport: checkovReportTable(&cr),
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func checkovReportTable(c *CheckovReport) string {
|
||||
data := [][]string{}
|
||||
for _, item := range c.Results.FailedChecks {
|
||||
data = append(data, []string{item.Id, item.Name + "\n", item.Guideline})
|
||||
}
|
||||
|
||||
tableString := &strings.Builder{}
|
||||
table := tablewriter.NewWriter(tableString)
|
||||
table.SetHeader([]string{"ID", "Name", "Guideline"})
|
||||
table.SetBorder(false)
|
||||
table.SetColWidth(64)
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
return tableString.String()
|
||||
}
|
||||
|
||||
type CheckovReport struct {
|
||||
Summary CheckovSummary `json:"summary"`
|
||||
Results CheckovResults `json:"results"`
|
||||
}
|
||||
|
||||
type CheckovSummary struct {
|
||||
Failed int `json:"failed"`
|
||||
Passed int `json:"passed"`
|
||||
ResourceCount int `json:"resource_count"`
|
||||
// parsing errors?
|
||||
// skipped ?
|
||||
}
|
||||
|
||||
type CheckovResults struct {
|
||||
FailedChecks []CheckovCheck `json:"failed_checks"`
|
||||
}
|
||||
|
||||
type CheckovCheck struct {
|
||||
Id string `json:"check_id"`
|
||||
BcId string `json:"bc_check_id"`
|
||||
Name string `json:"check_name"`
|
||||
Resource string `json:"resource"`
|
||||
Guideline string `json:"guideline"`
|
||||
FileLineRange []int `json:"file_line_range"`
|
||||
}
|
||||
107
pkg/dashboard/scanners/trivy.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package scanners
|
||||
|
||||
import (
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Trivy struct {
|
||||
Data *subproc.DataLayer
|
||||
}
|
||||
|
||||
func (c *Trivy) ManifestIsScannable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Trivy) SupportedResourceKinds() []string {
|
||||
// from https://github.com/aquasecurity/trivy-kubernetes/blob/main/pkg/k8s/k8s.go#L190
|
||||
return []string{
|
||||
"ReplicaSet",
|
||||
"ReplicationController",
|
||||
"StatefulSet",
|
||||
"Deployment",
|
||||
"CronJob",
|
||||
"DaemonSet",
|
||||
"Job",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Trivy) Name() string {
|
||||
return "Trivy"
|
||||
}
|
||||
|
||||
func (c *Trivy) Test() bool {
|
||||
utils.FailLogLevel = log.DebugLevel
|
||||
defer func() { utils.FailLogLevel = log.WarnLevel }()
|
||||
|
||||
res, err := utils.RunCommand([]string{"trivy", "--version"}, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(res, "\n")
|
||||
log.Infof("Discovered Trivy: %s", strings.TrimSpace(parts[0]))
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Trivy) ScanManifests(_ string) (*subproc.ScanResults, error) {
|
||||
return nil, nil // Trivy is unable to scan manifests
|
||||
}
|
||||
|
||||
func (c *Trivy) scanResource(ns string, kind string, name string) (string, error) {
|
||||
cmd := []string{"trivy", "kubernetes", "--quiet", "--format", "table", "--report", "all", "--no-progress",
|
||||
"--context", c.Data.KubeContext, "--namespace", ns, kind + "/" + name}
|
||||
out, err := utils.RunCommand(cmd, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Trivy) ScanResource(ns string, kind string, name string) (*subproc.ScanResults, error) {
|
||||
res := subproc.ScanResults{}
|
||||
resource, err := c.scanResource(ns, kind, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(resource, "\n") {
|
||||
if strings.HasPrefix(line, "Tests:") {
|
||||
parts := strings.FieldsFunc(line, func(r rune) bool {
|
||||
return r == ':' || r == ',' || r == ')'
|
||||
})
|
||||
|
||||
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[2])); err == nil {
|
||||
res.PassedCount += cnt
|
||||
} else {
|
||||
log.Warnf("Failed to parse Trivy output: %s", err)
|
||||
}
|
||||
|
||||
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[4])); err == nil {
|
||||
res.FailedCount += cnt
|
||||
} else {
|
||||
log.Warnf("Failed to parse Trivy output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "Total:") {
|
||||
parts := strings.FieldsFunc(line, func(r rune) bool {
|
||||
return r == ':' || r == ',' || r == '('
|
||||
})
|
||||
|
||||
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil {
|
||||
res.FailedCount += cnt
|
||||
} else {
|
||||
log.Warnf("Failed to parse Trivy output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
res.OrigReport = resource
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
@@ -2,46 +2,73 @@ package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/scanners"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func StartServer() (string, ControlChan) {
|
||||
data := DataLayer{}
|
||||
type Server struct {
|
||||
Version string
|
||||
Namespace string
|
||||
Address string
|
||||
Debug bool
|
||||
NoTracking bool
|
||||
}
|
||||
|
||||
func (s Server) StartServer() (string, utils.ControlChan) {
|
||||
data := subproc.DataLayer{
|
||||
Namespace: s.Namespace,
|
||||
Cache: subproc.NewCache(),
|
||||
}
|
||||
err := data.CheckConnectivity()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to check that Helm is operational, cannot continue. The error was: %s", err)
|
||||
os.Exit(1) // TODO: propagate error instead?
|
||||
}
|
||||
|
||||
address := os.Getenv("HD_BIND")
|
||||
if address == "" {
|
||||
address = "localhost"
|
||||
isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true"
|
||||
enableAnalytics := (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics
|
||||
data.StatusInfo = &subproc.StatusInfo{
|
||||
CurVer: s.Version,
|
||||
Analytics: enableAnalytics,
|
||||
LimitedToNamespace: s.Namespace,
|
||||
}
|
||||
go checkUpgrade(data.StatusInfo)
|
||||
|
||||
if os.Getenv("HD_PORT") == "" {
|
||||
address += ":8080" // TODO: better default port to clash less?
|
||||
} else {
|
||||
address += ":" + os.Getenv("HD_PORT")
|
||||
}
|
||||
discoverScanners(&data)
|
||||
|
||||
abort := make(ControlChan)
|
||||
api := NewRouter(abort, &data)
|
||||
done := startBackgroundServer(address, api, abort)
|
||||
abort := make(utils.ControlChan)
|
||||
api := NewRouter(abort, &data, s.Debug)
|
||||
done := s.startBackgroundServer(api, abort)
|
||||
|
||||
return "http://" + address, done
|
||||
return "http://" + s.Address, done
|
||||
}
|
||||
|
||||
func startBackgroundServer(addr string, routes *gin.Engine, abort ControlChan) ControlChan {
|
||||
done := make(ControlChan)
|
||||
server := &http.Server{Addr: addr, Handler: routes}
|
||||
func (s Server) startBackgroundServer(routes *gin.Engine, abort utils.ControlChan) utils.ControlChan {
|
||||
done := make(utils.ControlChan)
|
||||
server := &http.Server{
|
||||
Addr: s.Address,
|
||||
Handler: routes,
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := server.ListenAndServe()
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
panic(err) // TODO: in case of "port busy", check that it's another instance of us and just open browser
|
||||
log.Warnf("Looks like port is busy for %s, checking if it's us...", s.Address)
|
||||
if s.itIsUs() {
|
||||
log.Infof("Yes, it's another instance of us. Just reuse it.")
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
@@ -56,3 +83,73 @@ func startBackgroundServer(addr string, routes *gin.Engine, abort ControlChan) C
|
||||
|
||||
return done
|
||||
}
|
||||
|
||||
func (s Server) itIsUs() bool {
|
||||
url := fmt.Sprintf("http://%s/status", s.Address)
|
||||
var myClient = &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
r, err := myClient.Get(url)
|
||||
if err != nil {
|
||||
log.Debugf("It's not us on %s: %s", s.Address, err)
|
||||
return false
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
return strings.HasPrefix(r.Header.Get("X-Application-Name"), "Helm Dashboard")
|
||||
}
|
||||
|
||||
func discoverScanners(data *subproc.DataLayer) {
|
||||
potential := []subproc.Scanner{
|
||||
&scanners.Checkov{Data: data},
|
||||
&scanners.Trivy{Data: data},
|
||||
}
|
||||
|
||||
data.Scanners = []subproc.Scanner{}
|
||||
for _, scanner := range potential {
|
||||
if scanner.Test() {
|
||||
data.Scanners = append(data.Scanners, scanner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkUpgrade(d *subproc.StatusInfo) {
|
||||
url := "https://api.github.com/repos/komodorio/helm-dashboard/releases/latest"
|
||||
type GHRelease struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
var myClient = &http.Client{Timeout: 5 * time.Second}
|
||||
r, err := myClient.Get(url)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to check for new version: %s", err)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
target := new(GHRelease)
|
||||
err = json.NewDecoder(r.Body).Decode(target)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to decode new release version: %s", err)
|
||||
return
|
||||
}
|
||||
d.LatestVer = target.Name
|
||||
|
||||
v1, err := version.NewVersion(d.CurVer)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to parse CurVer: %s", err)
|
||||
v1 = &version.Version{}
|
||||
}
|
||||
|
||||
v2, err := version.NewVersion(d.LatestVer)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to parse LatestVer: %s", err)
|
||||
} else {
|
||||
if v1.LessThan(v2) {
|
||||
log.Warnf("Newer Helm Dashboard version is available: %s", d.LatestVer)
|
||||
log.Warnf("Upgrade instructions: https://github.com/komodorio/helm-dashboard#installing")
|
||||
} else {
|
||||
log.Debugf("Got latest version from GH: %s", d.LatestVer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ $("#btnUpgradeCheck").click(function () {
|
||||
self.find(".spinner-border").show()
|
||||
const repoName = self.data("repo")
|
||||
$("#btnUpgrade span").text("Checking...")
|
||||
$("#btnUpgrade .icon").removeClass("bi-arrow-up bi-check-circle").addClass("bi-hourglass-split")
|
||||
$("#btnUpgrade .icon").removeClass("bi-arrow-up bi-pencil").addClass("bi-hourglass-split")
|
||||
$.post("/api/helm/repo/update?name=" + repoName).fail(function (xhr) {
|
||||
reportError("Failed to update chart repo", xhr)
|
||||
}).done(function () {
|
||||
@@ -26,72 +26,202 @@ function checkUpgradeable(name) {
|
||||
$("#btnUpgrade .icon").removeClass("bi-hourglass-split").addClass("bi-x-octagon")
|
||||
$("#btnUpgrade").prop("disabled", true)
|
||||
$("#btnUpgradeCheck").prop("disabled", true)
|
||||
$("#btnAddRepository").text("Add repository for it")
|
||||
return
|
||||
}
|
||||
|
||||
$('#upgradeModalLabel select').empty()
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
$('#upgradeModalLabel select').append("<option value='" + data[i].version + "'>" + data[i].version + "</option>")
|
||||
}
|
||||
|
||||
$("#btnUpgradeCheck").text("Check for new version")
|
||||
const verCur = $("#specRev").data("last-chart-ver");
|
||||
const elm = data[0]
|
||||
$("#btnUpgradeCheck").data("repo", elm.name.split('/').shift())
|
||||
$("#btnUpgradeCheck").data("chart", elm.name.split('/').pop())
|
||||
|
||||
const verCur = $("#specRev").data("last-chart-ver");
|
||||
const canUpgrade = isNewerVersion(verCur, elm.version);
|
||||
$("#btnUpgradeCheck").prop("disabled", false)
|
||||
if (canUpgrade) {
|
||||
$("#btnUpgrade span").text("Upgrade to " + elm.version)
|
||||
$("#btnUpgrade .icon").removeClass("bi-hourglass-split").addClass("bi-arrow-up")
|
||||
} else {
|
||||
$("#btnUpgrade span").text("Up-to-date")
|
||||
$("#btnUpgrade .icon").removeClass("bi-hourglass-split").addClass("bi-check-circle")
|
||||
$("#btnUpgrade span").text("Reconfigure")
|
||||
$("#btnUpgrade .icon").removeClass("bi-hourglass-split").addClass("bi-pencil")
|
||||
}
|
||||
|
||||
$("#btnUpgrade").off("click").click(function () {
|
||||
popUpUpgrade($(this), verCur, elm)
|
||||
popUpUpgrade(elm, getHashParam("namespace"), getHashParam("chart"), verCur, $("#specRev").data("last-rev"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function popUpUpgrade(self, verCur, elm) {
|
||||
const name = getHashParam("chart");
|
||||
let url = "/api/helm/charts/install?namespace=" + getHashParam("namespace") + "&name=" + name + "&chart=" + elm.name;
|
||||
$('#upgradeModalLabel select').data("url", url)
|
||||
function popUpUpgrade(elm, ns, name, verCur, lastRev) {
|
||||
$("#upgradeModal .btn-confirm").prop("disabled", true)
|
||||
|
||||
$("#upgradeModalLabel .name").text(name)
|
||||
$("#upgradeModalLabel .ver-old").text(verCur)
|
||||
$('#upgradeModal').data("chart", elm.name).data("initial", !verCur)
|
||||
|
||||
$('#upgradeModalLabel select').val(elm.version).trigger("change")
|
||||
$("#upgradeModalLabel .name").text(elm.name)
|
||||
|
||||
const myModal = new bootstrap.Modal(document.getElementById('upgradeModal'), {});
|
||||
myModal.show()
|
||||
$("#upgradeModal .rel-cluster").text(getHashParam("context"))
|
||||
|
||||
const btnConfirm = $("#upgradeModal .btn-confirm");
|
||||
btnConfirm.prop("disabled", true).off('click').click(function () {
|
||||
btnConfirm.prop("disabled", true).prepend('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
$.ajax({
|
||||
url: url + "&version=" + $('#upgradeModalLabel select').val(),
|
||||
type: 'POST',
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to upgrade the chart", xhr)
|
||||
}).done(function (data) {
|
||||
if (verCur) {
|
||||
$("#upgradeModalLabel .type").text("Upgrade")
|
||||
$("#upgradeModal .ver-old").show().find("span").text(verCur)
|
||||
$("#upgradeModal .rel-name").prop("disabled", true).val(name)
|
||||
$("#upgradeModal .rel-ns").prop("disabled", true).val(ns)
|
||||
} else {
|
||||
$("#upgradeModalLabel .type").text("Install")
|
||||
$("#upgradeModal .ver-old").hide()
|
||||
$("#upgradeModal .rel-name").prop("disabled", false).val(elm.name.split("/").pop())
|
||||
$("#upgradeModal .rel-ns").prop("disabled", false).val(ns)
|
||||
}
|
||||
|
||||
$.getJSON("/api/helm/repo/search?name=" + elm.name).fail(function (xhr) {
|
||||
reportError("Failed to find chart in repo", xhr)
|
||||
}).done(function (vers) {
|
||||
// fill versions
|
||||
$('#upgradeModal select').empty()
|
||||
for (let i = 0; i < vers.length; i++) {
|
||||
const opt = $("<option value='" + vers[i].version + "'></option>");
|
||||
if (vers[i].version === verCur) {
|
||||
opt.html(vers[i].version + " ·")
|
||||
} else {
|
||||
opt.html(vers[i].version)
|
||||
}
|
||||
$('#upgradeModal select').append(opt)
|
||||
}
|
||||
|
||||
$('#upgradeModal select').val(elm.version).trigger("change")
|
||||
|
||||
const myModal = new bootstrap.Modal(document.getElementById('upgradeModal'), {});
|
||||
myModal.show()
|
||||
|
||||
if (verCur) {
|
||||
// fill current values
|
||||
$.get("/api/helm/charts/values?namespace=" + ns + "&revision=" + lastRev + "&name=" + name + "&flag=true").fail(function (xhr) {
|
||||
reportError("Failed to get charts values info", xhr)
|
||||
}).done(function (data) {
|
||||
$("#upgradeModal textarea").val(data).data("dirty", false)
|
||||
})
|
||||
} else {
|
||||
$("#upgradeModal textarea").val("").data("dirty", true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$("#upgradeModal .btn-confirm").click(function () {
|
||||
const btnConfirm = $("#upgradeModal .btn-confirm")
|
||||
btnConfirm.prop("disabled", true).prepend('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "/api/helm/charts/install" + upgradeModalQstr() + "&flag=true",
|
||||
data: $("#upgradeModal textarea").data("dirty") ? $("#upgradeModal form").serialize() : null,
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to upgrade the chart", xhr)
|
||||
}).done(function (data) {
|
||||
if (data.version) {
|
||||
setHashParam("section", null)
|
||||
const ns = $("#upgradeModal .rel-ns").val();
|
||||
setHashParam("namespace", ns ? ns : "default") // TODO: relaets issue #51
|
||||
setHashParam("chart", $("#upgradeModal .rel-name").val())
|
||||
setHashParam("revision", data.version)
|
||||
window.location.reload()
|
||||
})
|
||||
} else {
|
||||
reportError("Failed to get new revision number")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let reconfigTimeout = null;
|
||||
|
||||
function changeTimer() {
|
||||
const self = $(this);
|
||||
self.data("dirty", true)
|
||||
if (reconfigTimeout) {
|
||||
window.clearTimeout(reconfigTimeout)
|
||||
}
|
||||
reconfigTimeout = window.setTimeout(function () {
|
||||
requestChangeDiff()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
$('#upgradeModalLabel select').change(function () {
|
||||
$("#upgradeModal textarea").keyup(changeTimer)
|
||||
$("#upgradeModal .rel-name").keyup(changeTimer)
|
||||
$("#upgradeModal .rel-ns").keyup(changeTimer)
|
||||
|
||||
$('#upgradeModal select').change(function () {
|
||||
const self = $(this)
|
||||
|
||||
$("#upgradeModalBody").empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
$("#upgradeModal .btn-confirm").prop("disabled", true)
|
||||
$.get(self.data("url") + "&version=" + self.val()).fail(function (xhr) {
|
||||
requestChangeDiff()
|
||||
|
||||
// fill reference values
|
||||
$("#upgradeModal .ref-vals").html('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
$.get("/api/helm/repo/values?chart=" + $("#upgradeModal").data("chart") + "&version=" + self.val()).fail(function (xhr) {
|
||||
reportError("Failed to get upgrade info", xhr)
|
||||
}).done(function (data) {
|
||||
$("#upgradeModalBody").empty();
|
||||
data = hljs.highlight(data, {language: 'yaml'}).value
|
||||
$("#upgradeModal .ref-vals").html(data)
|
||||
})
|
||||
})
|
||||
|
||||
$('#upgradeModal .btn-scan').click(function () {
|
||||
const self = $(this)
|
||||
|
||||
self.prop("disabled", true).prepend('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/scanners/manifests" + upgradeModalQstr(),
|
||||
data: $("#upgradeModal form").serialize(),
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to scan the manifest", xhr)
|
||||
}).done(function (data) {
|
||||
self.prop("disabled", false).find(".spinner-border").hide()
|
||||
|
||||
const container = $("<div></div>")
|
||||
for (let name in data) {
|
||||
const res = data[name]
|
||||
|
||||
if (!res) {
|
||||
continue
|
||||
}
|
||||
|
||||
const pre = $("<pre></pre>").text(res.OrigReport)
|
||||
|
||||
container.append("<h2>" + name + " Scan Results</h2>")
|
||||
container.append(pre)
|
||||
}
|
||||
|
||||
const tab = window.open('about:blank', '_blank');
|
||||
tab.document.write(container.prop('outerHTML')); // where 'html' is a variable containing your HTML
|
||||
tab.document.close(); // to finish loading the page
|
||||
})
|
||||
})
|
||||
|
||||
function requestChangeDiff() {
|
||||
const self = $('#upgradeModal select');
|
||||
const diffBody = $("#upgradeModalBody");
|
||||
diffBody.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Calculating diff...')
|
||||
$("#upgradeModal .btn-confirm").prop("disabled", true)
|
||||
|
||||
let values = null;
|
||||
if ($("#upgradeModal textarea").data("dirty")) {
|
||||
$("#upgradeModal .invalid-feedback").hide()
|
||||
values = $("#upgradeModal form").serialize()
|
||||
|
||||
try {
|
||||
jsyaml.load($("#upgradeModal textarea").val())
|
||||
} catch (e) {
|
||||
$("#upgradeModal .invalid-feedback").text("YAML parse error: " + e.message).show()
|
||||
$("#upgradeModalBody").html("Invalid values YAML")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/helm/charts/install" + upgradeModalQstr(),
|
||||
data: values,
|
||||
}).fail(function (xhr) {
|
||||
$("#upgradeModalBody").html("<p class='text-danger'>Failed to get upgrade info: " + xhr.responseText + "</p>")
|
||||
}).done(function (data) {
|
||||
diffBody.empty();
|
||||
$("#upgradeModal .btn-confirm").prop("disabled", false)
|
||||
|
||||
const targetElement = document.getElementById('upgradeModalBody');
|
||||
@@ -101,12 +231,25 @@ $('#upgradeModalLabel select').change(function () {
|
||||
};
|
||||
const diff2htmlUi = new Diff2HtmlUI(targetElement, data, configuration);
|
||||
diff2htmlUi.draw()
|
||||
$("#upgradeModalBody").prepend("<p>Following changes will happen to cluster:</p>")
|
||||
if (!data) {
|
||||
$("#upgradeModalBody").html("No changes will happen to cluster")
|
||||
diffBody.html("No changes will happen to the cluster")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function upgradeModalQstr() {
|
||||
let qstr = "?" +
|
||||
"namespace=" + $("#upgradeModal .rel-ns").val() +
|
||||
"&name=" + $("#upgradeModal .rel-name").val() +
|
||||
"&chart=" + $("#upgradeModal").data("chart") +
|
||||
"&version=" + $('#upgradeModal select').val()
|
||||
|
||||
if ($("#upgradeModal").data("initial")) {
|
||||
qstr += "&initial=true"
|
||||
}
|
||||
|
||||
return qstr
|
||||
}
|
||||
|
||||
const btnConfirm = $("#confirmModal .btn-confirm");
|
||||
$("#btnUninstall").click(function () {
|
||||
@@ -193,3 +336,7 @@ $("#btnRollback").click(function () {
|
||||
})
|
||||
})
|
||||
|
||||
$("#btnAddRepository").click(function () {
|
||||
setHashParam("section", "repository")
|
||||
window.location.reload()
|
||||
})
|
||||
|
||||
66
pkg/dashboard/static/analytics.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onload = function () {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
const status = JSON.parse(xhr.responseText);
|
||||
const version = status.CurVer
|
||||
if (status.Analytics) {
|
||||
enableDD(version)
|
||||
enableHeap(version)
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.open('GET', '/status', true);
|
||||
xhr.send(null);
|
||||
|
||||
|
||||
function enableDD(version) {
|
||||
(function (h, o, u, n, d) {
|
||||
h = h[d] = h[d] || {
|
||||
q: [], onReady: function (c) {
|
||||
h.q.push(c)
|
||||
}
|
||||
}
|
||||
d = o.createElement(u);
|
||||
d.async = true;
|
||||
d.src = n
|
||||
n = o.getElementsByTagName(u)[0];
|
||||
n.parentNode.insertBefore(d, n)
|
||||
})(window, document, 'script', 'https://www.datadoghq-browser-agent.com/datadog-rum-v4.js', 'DD_RUM')
|
||||
DD_RUM.onReady(function () {
|
||||
DD_RUM.init({
|
||||
clientToken: 'pub16d64cd1c00cf073ce85af914333bf72',
|
||||
applicationId: 'e75439e5-e1b3-46ba-a9e9-a2e58579a2e2',
|
||||
site: 'datadoghq.com',
|
||||
service: 'helm-dashboard',
|
||||
version: version,
|
||||
trackInteractions: true,
|
||||
trackResources: true,
|
||||
trackLongTasks: true,
|
||||
defaultPrivacyLevel: 'mask',
|
||||
sessionReplaySampleRate: 0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function enableHeap(version) {
|
||||
window.heap = window.heap || [], heap.load = function (e, t) {
|
||||
window.heap.appid = e, window.heap.config = t = t || {};
|
||||
let r = document.createElement("script");
|
||||
r.type = "text/javascript", r.async = !0, r.src = "https://cdn.heapanalytics.com/js/heap-" + e + ".js";
|
||||
let a = document.getElementsByTagName("script")[0];
|
||||
a.parentNode.insertBefore(r, a);
|
||||
for (let n = function (e) {
|
||||
return function () {
|
||||
heap.push([e].concat(Array.prototype.slice.call(arguments, 0)))
|
||||
}
|
||||
}, p = ["addEventProperties", "addUserProperties", "clearEventProperties", "identify", "resetIdentity", "removeEventProperty", "setEventProperties", "track", "unsetEventProperty"], o = 0; o < p.length; o++) heap[p[o]] = n(p[o])
|
||||
};
|
||||
heap.load("4249623943");
|
||||
window.heap.addEventProperties({'version': version});
|
||||
}
|
||||
|
||||
function sendStats(name, prop){
|
||||
if (window.heap) {
|
||||
window.heap.track(name, prop);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
(function(h,o,u,n,d) {
|
||||
h=h[d]=h[d]||{q:[],onReady:function(c){h.q.push(c)}}
|
||||
d=o.createElement(u);d.async=1;d.src=n
|
||||
n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n)
|
||||
})(window,document,'script','https://www.datadoghq-browser-agent.com/datadog-rum-v4.js','DD_RUM')
|
||||
DD_RUM.onReady(function() {
|
||||
DD_RUM.init({
|
||||
clientToken: 'pub16d64cd1c00cf073ce85af914333bf72',
|
||||
applicationId: 'e75439e5-e1b3-46ba-a9e9-a2e58579a2e2',
|
||||
site: 'datadoghq.com',
|
||||
service: 'helm-dashboard',
|
||||
version: 'v0.0.0',
|
||||
trackInteractions: true,
|
||||
trackResources: true,
|
||||
trackLongTasks: true,
|
||||
defaultPrivacyLevel: 'mask'
|
||||
})
|
||||
})
|
||||
@@ -97,7 +97,7 @@ $('#specRev').keyup(function (event) {
|
||||
}
|
||||
});
|
||||
|
||||
$("form").submit(function(e){
|
||||
$("form").submit(function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
@@ -140,8 +140,10 @@ $("#nav-tab [data-tab]").click(function () {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function showResources(namespace, chart, revision) {
|
||||
const resBody = $("#nav-resources .body");
|
||||
const interestingResources = ["STATEFULSET", "DEAMONSET", "DEPLOYMENT"];
|
||||
resBody.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>');
|
||||
let qstr = "name=" + chart + "&namespace=" + namespace + "&revision=" + revision
|
||||
let url = "/api/helm/charts/resources"
|
||||
@@ -149,16 +151,23 @@ function showResources(namespace, chart, revision) {
|
||||
$.getJSON(url).fail(function (xhr) {
|
||||
reportError("Failed to get list of resources", xhr)
|
||||
}).done(function (data) {
|
||||
const scanners = $("body").data("scanners");
|
||||
const scannableResKinds = new Set();
|
||||
for (let k in scanners) {
|
||||
scanners[k].SupportedResourceKinds.forEach(scannableResKinds.add, scannableResKinds)
|
||||
}
|
||||
|
||||
resBody.empty();
|
||||
data = data.sort(function(a, b){return interestingResources.indexOf(a.kind.toUpperCase()) - interestingResources.indexOf(b.kind.toUpperCase())}).reverse();
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const res = data[i]
|
||||
const resBlock = $(`
|
||||
<div class="row px-3 py-2 mb-3 bg-white rounded">
|
||||
<div class="col-2 res-kind text-break"></div>
|
||||
<div class="col-3 res-name text-break fw-bold"></div>
|
||||
<div class="col-1 res-status"><span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span></div>
|
||||
<div class="col-5 res-statusmsg"><span class="text-muted small">Getting status...</span></div>
|
||||
<div class="col-1 res-actions"></div>
|
||||
<div class="col-1 res-status overflow-hidden"><span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span></div>
|
||||
<div class="col-4 res-statusmsg"><span class="text-muted small">Getting status...</span></div>
|
||||
<div class="col-2 res-actions"><button class='btn btn-sm ms-2 visually-hidden'>Vertical-sizer</button></div>
|
||||
</div>
|
||||
`)
|
||||
|
||||
@@ -171,7 +180,7 @@ function showResources(namespace, chart, revision) {
|
||||
//reportError("Failed to get list of resources")
|
||||
}).done(function (data) {
|
||||
const badge = $("<span class='badge me-2 fw-normal'></span>").text(data.status.phase);
|
||||
if (["Available", "Active", "Established"].includes(data.status.phase)) {
|
||||
if (["Available", "Active", "Established", "Bound", "Ready"].includes(data.status.phase)) {
|
||||
badge.addClass("bg-success text-dark")
|
||||
} else if (["Exists"].includes(data.status.phase)) {
|
||||
badge.addClass("bg-success text-dark bg-opacity-50")
|
||||
@@ -182,16 +191,25 @@ function showResources(namespace, chart, revision) {
|
||||
}
|
||||
|
||||
const statusBlock = resBlock.find(".res-status");
|
||||
statusBlock.empty().append(badge)
|
||||
statusBlock.empty().append(badge).attr("title", data.status.phase)
|
||||
resBlock.find(".res-statusmsg").html("<span class='text-muted small'>" + (data.status.message ? data.status.message : '') + "</span>")
|
||||
|
||||
if (badge.text() !== "NotFound") {
|
||||
if (badge.text() !== "NotFound" && revision == $("#specRev").data("last-rev")) {
|
||||
resBlock.find(".res-actions")
|
||||
|
||||
const btn = $("<button class=\"btn btn-sm btn-white border-secondary\">Describe</button>");
|
||||
resBlock.find(".res-actions").append(btn)
|
||||
btn.click(function () {
|
||||
showDescribe(ns, res.kind, res.metadata.name, badge.clone())
|
||||
})
|
||||
|
||||
if (scannableResKinds.has(res.kind)) {
|
||||
const btn2 = $("<button class='btn btn-sm btn-white border-secondary ms-2'>Scan</button>");
|
||||
resBlock.find(".res-actions").append(btn2)
|
||||
btn2.click(function () {
|
||||
scanResource(ns, res.kind, res.metadata.name, badge.clone())
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -212,3 +230,53 @@ function showDescribe(ns, kind, name, badge) {
|
||||
$("#describeModalBody").empty().append("<pre class='bg-white rounded p-3'></pre>").find("pre").html(data)
|
||||
})
|
||||
}
|
||||
|
||||
function scanResource(ns, kind, name, badge) {
|
||||
$("#describeModal .offcanvas-header p").text(kind)
|
||||
$("#describeModalLabel").text(name).append(badge.addClass("ms-3 small fw-normal"))
|
||||
const body = $("#describeModalBody");
|
||||
body.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Scanning...')
|
||||
|
||||
const myModal = new bootstrap.Offcanvas(document.getElementById('describeModal'));
|
||||
myModal.show()
|
||||
$.get("/api/scanners/resource/" + kind.toLowerCase() + "?name=" + name + "&namespace=" + ns).fail(function (xhr) {
|
||||
reportError("Failed to scan resource", xhr)
|
||||
}).done(function (data) {
|
||||
body.empty()
|
||||
if ($.isEmptyObject(data)) {
|
||||
body.append("No information from scanners. Make sure you have installed some and scanned object is supported.")
|
||||
}
|
||||
|
||||
const tabs = $('<ul class="nav nav-tabs mt-3" role="tablist"></ul>')
|
||||
const content = $('<div class="tab-content"></div>')
|
||||
|
||||
for (let name in data) {
|
||||
const res = data[name]
|
||||
|
||||
if (!res.OrigReport && !res.PassedCount) continue
|
||||
|
||||
const hdr = $(`<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="` + name + `-tab" data-bs-toggle="tab" data-bs-target="#` + name + `-tab-pane" type="button" role="tab">` + name + `</button>
|
||||
</li>`)
|
||||
|
||||
if (res.FailedCount) {
|
||||
hdr.find('button').append("<span class='badge bg-danger ms-2'>" + res.FailedCount + " failed</span>")
|
||||
}
|
||||
|
||||
if (res.PassedCount) {
|
||||
hdr.find('button').append("<span class='badge bg-info ms-2'>" + res.PassedCount + " passed</span>")
|
||||
}
|
||||
|
||||
const hl = hljs.highlight(res.OrigReport, {language: 'yaml'}).value
|
||||
const pre = $("<pre class='bg-white rounded p-3' style='font-size: inherit; overflow: unset'></pre>").html(hl)
|
||||
const div = $('<div class="tab-pane fade" id="' + name + '-tab-pane" role="tabpanel" aria-labelledby="profile-tab" tabindex="0"></div>').append(pre)
|
||||
|
||||
tabs.append(hdr)
|
||||
content.append(div)
|
||||
}
|
||||
|
||||
body.append(tabs)
|
||||
body.append(content)
|
||||
tabs.find('li').first().find('button').click()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,20 +5,16 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Helm Dashboard</title>
|
||||
<script src="static/datadog.js"></script>
|
||||
<script src="static/analytics.js"></script>
|
||||
<link rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto&family=Inter&family=Poppins:wght@600&family=Poppins:wght@500&family=Inter:wght@500&family=Roboto+Slab:wght@400&family=Roboto+Slab:wght@700&family=Roboto:wght@700&family=Roboto:wght@500"/>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/lightfair.min.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"/>
|
||||
<link href="static/styles-base.css" rel="stylesheet">
|
||||
<link href="static/styles.css" rel="stylesheet">
|
||||
<script type="text/javascript">
|
||||
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=document.createElement("script");r.type="text/javascript",r.async=!0,r.src="https://cdn.heapanalytics.com/js/heap-"+e+".js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(r,a);for(var n=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],o=0;o<p.length;o++)heap[p[o]]=n(p[o])};
|
||||
heap.load("3615793373");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -26,35 +22,56 @@
|
||||
<!-- TOP BAR -->
|
||||
<nav class="navbar navbar-expand bg-white mb-0 p-0 b-shadow" id="topNav">
|
||||
<div class="container-fluid m-0 p-0">
|
||||
<div class="navbar-brand">
|
||||
<a href="/"><img src="static/logo.png" alt="Logo"></a>
|
||||
<div>
|
||||
<h1><a href="/">Helm Dashboard</a></h1>
|
||||
</div>
|
||||
<div class="navbar-brand me-0">
|
||||
<a href="/"><img src="static/logo-header.svg" alt="Helm Dashboard"></a>
|
||||
</div>
|
||||
|
||||
<div class="separator-vertical mx-3"><span></span></div>
|
||||
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item mx-2">
|
||||
<a class="nav-link px-3 active" aria-current="page" href="/">Installed</a>
|
||||
<a class="nav-link px-3 section-installed">Installed</a>
|
||||
</li>
|
||||
<!-- TODO
|
||||
<li class="nav-item mx-2">
|
||||
<a href="#" class="nav-link px-3">Repository</a>
|
||||
<a class="nav-link px-3 section-repo">Repository</a>
|
||||
</li>
|
||||
-->
|
||||
<!-- TODO
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled">Provisional Charts</a>
|
||||
<li class="nav-item mx-2 dropdown">
|
||||
<a class="nav-link dropdown-toggle section-help" role="button" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
Help
|
||||
</a>
|
||||
<ul class="dropdown-menu fs-80">
|
||||
<li><a class="dropdown-item"
|
||||
href="https://join.slack.com/t/komodorkommunity/shared_invite/zt-1dm3cnkue-ov1Yh~_95teA35QNx5yuMg"
|
||||
target="_blank"><i class="bi-slack"></i> Support Chat</a></li>
|
||||
<li><a class="dropdown-item" href="https://github.com/komodorio/helm-dashboard" target="_blank"><i
|
||||
class="bi-github"></i> Project Page</a></li>
|
||||
<li>
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li>
|
||||
<!-- TODO: this should go under the "user menu" -->
|
||||
<button class="dropdown-item" id="cacheClear"><i
|
||||
class="bi-arrow-repeat"></i> Reset Cache
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li><a class="dropdown-item disabled" href="#">Version <span id="toolVersion"></span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
-->
|
||||
<li class="nav-item mx-2 display-none upgrade-possible">
|
||||
<a class="nav-link position-relative text-danger"
|
||||
href="https://github.com/komodorio/helm-dashboard#installing" target="_blank">
|
||||
Upgrade to <span id="toolVersionUpgrade"></span>
|
||||
</a></li>
|
||||
|
||||
</ul>
|
||||
<div>
|
||||
<a class="btn" href="https://komodor.com/?utm_campaign=Helm-Dash&utm_source=helm-dash"><img
|
||||
src="https://komodor.com/wp-content/uploads/2021/05/favicon.png" alt="komodor.io" style="height: 1.2rem; vertical-align: text-bottom; filter: grayscale(00%);"></a>
|
||||
|
||||
<a class="btn me-2 text-muted" href="https://github.com/komodorio/helm-dashboard" title="Project page on GitHub"><i class="bi-github"></i></a>
|
||||
src="static/komodor-logo.svg" alt="komodor.io"
|
||||
style="height: 1.2rem; vertical-align: text-bottom; filter: grayscale(00%);"></a>
|
||||
</div>
|
||||
<div class="separator-vertical"><span></span></div>
|
||||
<i class="btn bi-power text-muted p-2 m-1 mx-2" title="Shut down the Helm Dashboard application"></i>
|
||||
@@ -62,7 +79,49 @@
|
||||
</nav>
|
||||
<!-- /TOP BAR -->
|
||||
|
||||
<div class="row mt-3 pt-3 me-5" id="sectionList" style="display: none">
|
||||
<!--REPO SECTION-->
|
||||
<div class="row mt-3 pt-3 me-5 section" id="sectionRepo" style="display: none">
|
||||
<div class="col-3 ps-4 repo-list">
|
||||
<div class="p-2 bg-white rounded-1 b-shadow">
|
||||
<h4 class="fs-6">Repositories</h4>
|
||||
<ul class="list-unstyled p-2">
|
||||
</ul>
|
||||
<button class="btn btn-sm border-secondary text-muted">
|
||||
<i class="bi-plus-lg"></i> Add Repository
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9 repo-details bg-white b-shadow pt-4 px-5 overflow-auto rounded">
|
||||
<div class="float-end">
|
||||
<button class="me-2 btn btn-sm btn-light bg-white border border-secondary btn-update">
|
||||
<i class="bi-arrow-repeat"></i> Update
|
||||
</button>
|
||||
<button class="btn btn-sm btn-light bg-white border border-secondary btn-remove">
|
||||
<i class="bi-trash3"></i> Remove
|
||||
</button>
|
||||
<p class="my-3"><input class="form-control form-control-sm" type="text" placeholder="Filter..."
|
||||
id="inputSearch"></p>
|
||||
</div>
|
||||
<div><span class="text-muted small fw-bold me-3">REPOSITORY</span></div>
|
||||
<h2 class="mb-3">name-of-repo</h2>
|
||||
<div class="mb-5">
|
||||
<span class="rounded rounded-1 me-2 p-1 px-2 bg-tag text-dark">URL: <span class="url fw-bold">http://somerepo/somepath</span></span>
|
||||
</div>
|
||||
<div class="py-2 mb-3 float-end">
|
||||
|
||||
</div>
|
||||
<div class="row bg-secondary rounded px-3 py-2 mb-3 fw-bold small"
|
||||
style="text-transform: uppercase">
|
||||
<div class="col-3">Chart Name</div>
|
||||
<div class="col">Description</div>
|
||||
<div class="col-1">Version</div>
|
||||
<div class="col-1"></div>
|
||||
</div>
|
||||
<ul class="list-unstyled mt-4 charts"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3 pt-3 me-5 section" id="sectionList" style="display: none">
|
||||
<div class="col-2 ms-3">
|
||||
<!-- FILTER BLOCK -->
|
||||
<div class="p-2 ps-2 bg-white rounded-1 b-shadow" id="filters">
|
||||
@@ -71,11 +130,10 @@
|
||||
<ul class="list-unstyled" id="cluster">
|
||||
</ul>
|
||||
|
||||
<!-- TODO
|
||||
<h4 class="mt-4">Namespaces</h4>
|
||||
<ul class="list-unstyled" id="namespaces">
|
||||
<p id="limitNamespace" class="display-none ps-3"><span class="fw-bold"></span> (forced)</p>
|
||||
<ul class="list-unstyled" id="namespace">
|
||||
</ul>
|
||||
-->
|
||||
</form>
|
||||
</div>
|
||||
<!-- /FILTER BLOCK -->
|
||||
@@ -85,9 +143,12 @@
|
||||
<!-- INSTALLED LIST -->
|
||||
<div class="col ms-2" id="installedList">
|
||||
<div class="col rounded rounded-1 b-shadow header">
|
||||
<div class="bg-white rounded-top m-0">
|
||||
<div class="bg-white rounded-top m-0 spaced-out">
|
||||
<h2 class="m-0 p-1"><img class="m-2 mx-3 me-2" src="static/helm-gray.svg" alt="Installed Charts">Installed
|
||||
Charts (<span></span>)</h2>
|
||||
<div class="form-outline w-25">
|
||||
<input type="text" id="installedSearch" class="form-control form-control-sm" placeholder="Filter..." />
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-secondary rounded-bottom m-0 row p-2">
|
||||
<div class="col-4 hdr-name">Name</div>
|
||||
@@ -100,11 +161,16 @@
|
||||
</div>
|
||||
|
||||
<div class="body"></div>
|
||||
<div class="bg-white rounded shadow p-3 display-none no-charts">Looks like you don't have any charts
|
||||
installed. "Repository" section may be a good place to start.
|
||||
</div>
|
||||
<div class="bg-white rounded shadow p-3 display-none all-filtered">There are no releases matching your filter criteria. Reset your filters or install more charts.
|
||||
</div>
|
||||
</div>
|
||||
<!-- /INSTALLED LIST -->
|
||||
</div>
|
||||
|
||||
<div class="row flex-nowrap pt-0 mx-0" id="sectionDetails" style="display: none">
|
||||
<div class="row flex-nowrap pt-0 mx-0 section" id="sectionDetails" style="display: none">
|
||||
<div class="col-2 px-4 py-4 pe-3 rev-list">
|
||||
<h3 class="fw-bold small">Revisions</h3>
|
||||
<ul class="list-unstyled">
|
||||
@@ -128,7 +194,11 @@
|
||||
title="Uninstall the chart"><i class="bi-trash3"></i> Uninstall
|
||||
</button>
|
||||
<br/>
|
||||
<a class="link small" id="btnUpgradeCheck">Check for new version
|
||||
<a class="link small" id="btnUpgradeCheck">
|
||||
<span class="spinner-border spinner-border-sm" style="display: none" role="status"
|
||||
aria-hidden="true"></span>
|
||||
</a>
|
||||
<a class="link small" id="btnAddRepository">
|
||||
<span class="spinner-border spinner-border-sm" style="display: none" role="status"
|
||||
aria-hidden="true"></span>
|
||||
</a>
|
||||
@@ -161,15 +231,15 @@
|
||||
Resources
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="manifests"
|
||||
type="button" role="tab" aria-controls="nav-manifest-diff" aria-selected="false"
|
||||
type="button" role="tab" aria-controls="nav-manifest" aria-selected="false"
|
||||
tabindex="-1">Manifests
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="values"
|
||||
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" tabindex="-1">
|
||||
type="button" role="tab" aria-controls="nav-manifest" aria-selected="false" tabindex="-1">
|
||||
Values
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="notes"
|
||||
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" tabindex="-1">
|
||||
type="button" role="tab" aria-controls="nav-manifest" aria-selected="false" tabindex="-1">
|
||||
Notes
|
||||
</button>
|
||||
</div>
|
||||
@@ -187,7 +257,7 @@
|
||||
<div class="body"></div>
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-manifest" role="tabpanel">
|
||||
<nav class="navbar bg-light">
|
||||
<nav class="navbar bg-white rounded border border-secondary">
|
||||
<form class="container-fluid" id="modePanel">
|
||||
<label class="form-check-label" for="diffModeNone">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModeNone"
|
||||
@@ -212,9 +282,6 @@
|
||||
|
||||
<div id="manifestText" class="mt-2 bg-white"></div>
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-disabled" role="tabpanel" aria-labelledby="nav-disabled-tab"
|
||||
tabindex="0">...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,7 +289,7 @@
|
||||
|
||||
|
||||
<!-- Modals -->
|
||||
<div id="errorAlert"
|
||||
<div id="errorAlert" style="z-index: 2000; max-width: 95%; overflow: auto"
|
||||
class="display-none alert alert-sm alert-danger alert-dismissible position-absolute position-absolute top-0 start-50 translate-middle-x mt-3 border-danger"
|
||||
role="alert">
|
||||
<h4 class="alert-heading"><i class="bi-exclamation-triangle-fill"></i> <span></span></h4>
|
||||
@@ -232,7 +299,7 @@
|
||||
</div>
|
||||
|
||||
<div class="offcanvas offcanvas-end rounded-start" tabindex="-1" id="describeModal"
|
||||
aria-labelledby="describeModalLabel">
|
||||
aria-labelledby="describeModalLabel" style="overflow-x: auto">
|
||||
<div class="offcanvas-header border-bottom p-4">
|
||||
<div>
|
||||
<h5 id="describeModalLabel"></h5>
|
||||
@@ -263,22 +330,84 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal" id="upgradeModal" tabindex="-1" aria-labelledby="describeModalLabel" aria-hidden="true">
|
||||
<div class="modal" id="repoAddModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog modal-dialog-scrollable modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="upgradeModalLabel">
|
||||
Upgrade <b class='text-success name'></b> from version <b class='text-success ver-old'></b> to
|
||||
<select class='fw-bold text-success ver-new'></select>
|
||||
</h5>
|
||||
<h5 class="modal-title">Add Chart Repository</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="upgradeModalBody">
|
||||
|
||||
<div class="modal-body">
|
||||
<form enctype="application/x-www-form-urlencoded">
|
||||
<label class="form-label">Name: <input class="form-control" name="name"></label>
|
||||
<label class="form-label">URL: <input class="form-control" name="url"></label>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary btn-confirm">Confirm Upgrade</button>
|
||||
<button type="button" class="btn btn-primary btn-confirm">Add Repository</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="upgradeModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog modal-dialog-scrollable modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="upgradeModalLabel">
|
||||
<span class="type"></span> <b class='text-success name'></b>
|
||||
</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form class="modal-body border-bottom fs-5" enctype="multipart/form-data">
|
||||
<div class="input-group mb-3 text-muted">
|
||||
<label class="form-label me-4 text-dark">Version to install: <select
|
||||
class='fw-bold text-success ver-new'></select></label> <span class="ver-old">(current version is <span
|
||||
class='text-success ms-1'>0.0.0</span>)</span>
|
||||
</div>
|
||||
<div class="input-group mb-3 text-muted">
|
||||
<label class="form-label me-4 text-dark">
|
||||
Release Name: <input class="form-control rel-name">
|
||||
</label>
|
||||
<label class="form-label me-4 text-dark">
|
||||
Namespace (optional):
|
||||
<input type="text" class="form-control rel-ns" list="ns-datalist"/>
|
||||
<datalist id="ns-datalist"></datalist>
|
||||
</label>
|
||||
<label class="form-label me-4 text-dark">
|
||||
Cluster: <span class="form-label rel-cluster"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 pe-3">
|
||||
<label class="form-label">User-Defined Values:</label>
|
||||
</div>
|
||||
<div class="col-6 ps-3">
|
||||
<label class="form-label">Chart Values Reference:</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 pe-3">
|
||||
<textarea name="values" class="form-control w-100 h-100" rows="5"
|
||||
style="font-family: monospace"></textarea>
|
||||
</div>
|
||||
<div class="col-6 ps-3">
|
||||
<pre class="ref-vals fs-6 w-100 bg-secondary p-2 rounded" style="max-height: 20rem"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6 pe-3">
|
||||
<span class="invalid-feedback small mb-3"> (wrong YAML)</span>
|
||||
</div>
|
||||
</div>
|
||||
<label class="form-label mt-4">Manifest changes:</label>
|
||||
<div id="upgradeModalBody" class="small"></div>
|
||||
</form>
|
||||
<div class="modal-footer d-flex">
|
||||
<button type="button" class="btn btn-scan bg-white border-secondary display-none">Scan for Problems
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-confirm">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -293,6 +422,11 @@
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/luxon@3.0.3/build/global/luxon.min.js"
|
||||
integrity="sha256-RH4TKnKcKyde0s2jc5BW3pXZl/5annY3fcZI9VrV5WQ=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"
|
||||
integrity="sha512-CSBhVREyzHAjAFfBlIBakjoRUKp5h7VSweP0InR/pAJyptH7peuhCsqAI/snV+TwZmXZqoUklpXp6R6wMnYf5Q=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<script src="static/repo.js"></script>
|
||||
<script src="static/list-view.js"></script>
|
||||
<script src="static/revisions-view.js"></script>
|
||||
<script src="static/details-view.js"></script>
|
||||
|
||||
@@ -4,21 +4,39 @@ function loadChartsList() {
|
||||
const chartsCards = $("#installedList .body")
|
||||
chartsCards.empty().append("<div><span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span> Loading...</div>")
|
||||
$.getJSON("/api/helm/charts").fail(function (xhr) {
|
||||
sendStats('Get releases', {'status': 'failed'});
|
||||
reportError("Failed to get list of charts", xhr)
|
||||
}).done(function (data) {
|
||||
chartsCards.empty()
|
||||
chartsCards.empty().hide()
|
||||
$("#installedList .header h2 span").text(data.length)
|
||||
const usedNS = {}
|
||||
data.forEach(function (elm) {
|
||||
let card = buildChartCard(elm);
|
||||
chartsCards.append(card)
|
||||
usedNS[elm.namespace] = usedNS[elm.namespace] ? usedNS[elm.namespace] + 1 : 1
|
||||
})
|
||||
sendStats('Get releases', {'status': 'success', length:data.length});
|
||||
filterInstalledList(chartsCards.find(".row"))
|
||||
$("#namespace li").each(function (ix, obj) {
|
||||
obj = $(obj)
|
||||
const objNS = obj.find("input").val();
|
||||
if (usedNS[objNS]) {
|
||||
obj.find("label .text-muted").text('['+usedNS[objNS]+']')
|
||||
obj.show()
|
||||
} else {
|
||||
obj.hide()
|
||||
}
|
||||
})
|
||||
chartsCards.show()
|
||||
if (!data.length) {
|
||||
$("#installedList .no-charts").show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function buildChartCard(elm) {
|
||||
const card = $(`<div class="row m-0 py-3 bg-white rounded-1 b-shadow border-4 border-start">
|
||||
<div class="col-4 rel-name"><span class="link">release-name</span><div></div></div>
|
||||
const card = $(`<div class="row m-0 py-4 bg-white rounded-1 b-shadow border-4 border-start link">
|
||||
<div class="col-4 rel-name"><span>release-name</span><div></div></div>
|
||||
<div class="col-3 rel-status"><span></span><div></div></div>
|
||||
<div class="col-2 rel-chart text-nowrap"><span></span><div>Chart Version</div></div>
|
||||
<div class="col-1 rel-rev"><span>#0</span><div>Revision</div></div>
|
||||
@@ -26,17 +44,49 @@ function buildChartCard(elm) {
|
||||
<div class="col-1 rel-date text-nowrap"><span>today</span><div>Updated</div></div>
|
||||
</div>`)
|
||||
|
||||
// semver2 regex , add optional v prefix
|
||||
const chartNameRegex = 'v?(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?'
|
||||
const chartName = elm.chart.substring(0, elm.chart.match(chartNameRegex).index - 1)
|
||||
$.getJSON("/api/helm/repo/search?name=" + chartName).fail(function (xhr) {
|
||||
// we're ok if we can't show icon and description
|
||||
console.log("Failed to get repo name for charts", xhr)
|
||||
}).done(function (data) {
|
||||
if (data.length > 0) {
|
||||
$.getJSON("/api/helm/charts/show?name=" + data[0].name).fail(function (xhr) {
|
||||
console.log("Failed to get chart", xhr)
|
||||
}).done(function (data) {
|
||||
if (data) {
|
||||
const res = data[0];
|
||||
if (res.icon) {
|
||||
card.find(".rel-name").attr("style", "background-image: url(" + res.icon + ")")
|
||||
}
|
||||
if (res.description) {
|
||||
card.find(".rel-name div").text(res.description)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
card.find(".rel-name span").text(elm.name)
|
||||
card.find(".rel-rev span").text("#" + elm.revision)
|
||||
card.find(".rel-ns span").text(elm.namespace)
|
||||
card.find(".rel-chart span").text(elm.chart)
|
||||
card.find(".rel-date span").text(getAge(elm))
|
||||
|
||||
card.data("namespace", elm.namespace)
|
||||
card.data("name", elm.name)
|
||||
card.data("chart", elm.chart)
|
||||
|
||||
statusStyle(elm.status, card, card.find(".rel-status span"))
|
||||
|
||||
card.find("a").attr("href", '#context=' + getHashParam('context') + '&namespace=' + elm.namespace + '&name=' + elm.name)
|
||||
|
||||
card.find(".rel-name span").data("chart", elm).click(function () {
|
||||
card.data("chart", elm).click(function () {
|
||||
if (window.getSelection().toString()) {
|
||||
return
|
||||
}
|
||||
const self = $(this)
|
||||
$("#sectionList").hide()
|
||||
|
||||
@@ -49,3 +99,6 @@ function buildChartCard(elm) {
|
||||
return card;
|
||||
}
|
||||
|
||||
$("#installedSearch").keyup(function () {
|
||||
filterInstalledList($("#installedList .body .row"))
|
||||
})
|
||||
|
||||
16
pkg/dashboard/static/logo-header.svg
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="32"
|
||||
height="37"
|
||||
viewBox="0 0 32 37"
|
||||
width="179"
|
||||
height="164"
|
||||
viewBox="0 0 179 164"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
id="svg20"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
@@ -13,9 +13,9 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
id="defs24" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
id="namedview22"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
@@ -23,27 +23,70 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="34.810811"
|
||||
inkscape:cx="13.228649"
|
||||
inkscape:cy="17.552019"
|
||||
inkscape:zoom="3.9268293"
|
||||
inkscape:cx="39.090062"
|
||||
inkscape:cy="109.24845"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2059"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
inkscape:current-layer="svg20" />
|
||||
<rect
|
||||
style="opacity:0.455635;fill:#ffffff;stroke-width:18.0094;fill-opacity:1"
|
||||
id="rect1031"
|
||||
width="17.224331"
|
||||
height="10.929513"
|
||||
x="7.3572245"
|
||||
y="12.500892"
|
||||
ry="0" />
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:22.5327"
|
||||
id="rect967"
|
||||
width="113.34328"
|
||||
height="68.482964"
|
||||
x="32.284447"
|
||||
y="47.989918" />
|
||||
<path
|
||||
d="M106.129 93.4776C112.242 93.4776 117.336 86.7533 117.336 78.1951C117.336 69.637 112.242 62.9127 106.129 62.9127C100.016 62.9127 94.9216 69.637 94.9216 78.1951C94.9216 86.7533 100.016 93.4776 106.129 93.4776Z"
|
||||
fill="#1347FF"
|
||||
id="path2" />
|
||||
<path
|
||||
d="M84.1221 78.1951C84.1221 86.5495 79.0279 93.4776 72.915 93.4776C66.802 93.4776 61.7078 86.7533 61.7078 78.1951C61.7078 69.8408 66.802 62.9127 72.915 62.9127C79.0279 62.9127 84.1221 69.8408 84.1221 78.1951Z"
|
||||
fill="#1347FF"
|
||||
id="path4" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M18 5V2.29761C18 1.02617 17.1002 0 15.9958 0C14.8915 0 14 1.03576 14 2.29761V5H18ZM28.8285 11.364L30.7394 9.45308C31.6384 8.55404 31.7278 7.19222 30.9469 6.4113C30.166 5.63038 28.8032 5.73239 27.9109 6.62466L26.0001 8.53553L28.8285 11.364ZM1.62465 9.45308L3.53553 11.364L6.36396 8.53553L4.45308 6.62466C3.56081 5.73239 2.19804 5.63038 1.41712 6.4113C0.636208 7.19222 0.72561 8.55404 1.62465 9.45308ZM6 12.8164L7.78385 11H24.2163L26 12.8085V23.1915L24.2163 25H7.78372L6 23.1915V12.8164ZM8.94831 12.977L8.25128 13.6683V22.3438L8.94831 23.0351H23.0733L23.7446 22.3426V13.6694L23.0733 12.977H8.94831ZM20.1736 17.4485C20.1736 18.7679 19.4222 19.8375 18.4953 19.8375C17.5684 19.8375 16.817 18.7679 16.817 17.4485C16.817 16.1291 17.5684 15.0595 18.4953 15.0595C19.4222 15.0595 20.1736 16.1291 20.1736 17.4485ZM13.5045 19.8375C14.4314 19.8375 15.1828 18.7679 15.1828 17.4485C15.1828 16.1291 14.4314 15.0595 13.5045 15.0595C12.5776 15.0595 11.8262 16.1291 11.8262 17.4485C11.8262 18.7679 12.5776 19.8375 13.5045 19.8375ZM14.3641 34.0662V31.3638H18.3641V34.0662C18.3641 35.328 17.4726 36.3638 16.3682 36.3638C15.2638 36.3638 14.3641 35.3376 14.3641 34.0662ZM1.62465 26.9107L3.53553 24.9998L6.36396 27.8282L4.45308 29.7391C3.56081 30.6314 2.19804 30.7334 1.41712 29.9525C0.636204 29.1716 0.725608 27.8097 1.62465 26.9107ZM28.8285 24.9998L30.7394 26.9107C31.6384 27.8097 31.7278 29.1716 30.9469 29.9525C30.166 30.7334 28.8032 30.6314 27.9109 29.7391L26.0001 27.8282L28.8285 24.9998Z"
|
||||
d="M22.5848 48.8529L34.607 37.2383H144.437L156.459 48.6492V114.873L144.437 126.488H34.607L22.5848 114.873V48.8529ZM42.3501 49.8717L37.6635 54.3546V109.575L42.3501 113.854H136.897L141.38 109.371V54.1508L136.897 49.668H42.3501V49.8717Z"
|
||||
fill="#1347FF"
|
||||
id="path2" />
|
||||
id="path6" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M175.817 28.68L167.87 36.8306L155.848 25.2159L163.794 17.0653C172.353 8.09963 184.579 19.918 175.817 28.68Z"
|
||||
fill="#1347FF"
|
||||
id="path8" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M97.7744 9.32228V20.5294H81.0656V9.32228C81.0656 -3.10743 97.9781 -3.10743 97.7744 9.32228Z"
|
||||
fill="#1347FF"
|
||||
id="path10" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M81.2693 154.2V142.993H97.9781V154.2C97.9781 166.629 81.0655 166.629 81.2693 154.2Z"
|
||||
fill="#1347FF"
|
||||
id="path12" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M15.2493 17.0653L23.1961 25.2159L11.1739 36.8306L3.22708 28.68C-5.53484 19.918 6.6911 8.09963 15.2493 17.0653Z"
|
||||
fill="#1347FF"
|
||||
id="path14" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.0233 134.842L10.9702 126.691L22.9923 138.306L15.0455 146.457C6.48732 155.422 -5.73862 143.604 3.0233 134.842Z"
|
||||
fill="#1347FF"
|
||||
id="path16" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M163.591 146.457L155.644 138.306L167.666 126.691L175.613 134.842C184.375 143.604 172.149 155.422 163.591 146.457Z"
|
||||
fill="#1347FF"
|
||||
id="path18" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.2 KiB |
138
pkg/dashboard/static/repo.js
Normal file
@@ -0,0 +1,138 @@
|
||||
function loadRepoView() {
|
||||
$("#sectionRepo .repo-details").hide()
|
||||
$("#sectionRepo").show()
|
||||
|
||||
$.getJSON("/api/helm/repo").fail(function (xhr) {
|
||||
reportError("Failed to get list of repositories", xhr)
|
||||
sendStats('Get repo', {'status': 'fail'});
|
||||
}).done(function (data) {
|
||||
const items = $("#sectionRepo .repo-list ul").empty()
|
||||
data.sort((a, b) => (a.name > b.name) - (a.name < b.name))
|
||||
|
||||
data.forEach(function (elm) {
|
||||
let opt = $('<li class="mb-2"><label><input type="radio" name="cluster" class="me-2"/><span></span></label></li>');
|
||||
opt.attr('title', elm.url)
|
||||
opt.find("input").val(elm.name).text(elm.name).data("item", elm)
|
||||
opt.find("span").text(elm.name)
|
||||
items.append(opt)
|
||||
})
|
||||
|
||||
if (!data.length) {
|
||||
items.text("No repositories found, try adding one")
|
||||
}
|
||||
sendStats('Get repo', {'status': 'success', length:data.length});
|
||||
items.find("input").click(function () {
|
||||
$("#inputSearch").val('')
|
||||
const self = $(this)
|
||||
const elm = self.data("item");
|
||||
setHashParam("repo", elm.name)
|
||||
$("#sectionRepo .repo-details").show()
|
||||
$("#sectionRepo .repo-details h2").text(elm.name)
|
||||
$("#sectionRepo .repo-details .url").text(elm.url)
|
||||
|
||||
$("#sectionRepo .repo-details ul").html('<span class="spinner-border spinner-border-sm mx-1" role="status" aria-hidden="true"></span>')
|
||||
$.getJSON("/api/helm/repo/charts?name=" + elm.name).fail(function (xhr) {
|
||||
reportError("Failed to get list of charts in repo", xhr)
|
||||
}).done(function (data) {
|
||||
$("#sectionRepo .repo-details ul").empty()
|
||||
data.forEach(function (elm) {
|
||||
const li = $(`<li class="row p-2 rounded">
|
||||
<h6 class="col-3 py-2">` + elm.name.split('/').pop() + `</h6>
|
||||
<div class="col py-2">` + elm.description + `</div>
|
||||
<div class="col-1 py-2">` + elm.version + `</div>
|
||||
<div class="col-1 action text-nowrap"><button class="btn btn-sm border-secondary bg-white">Install</button></div>
|
||||
</li>`)
|
||||
li.data("item", elm)
|
||||
|
||||
if (elm.installed_namespace) {
|
||||
li.find("button").text("View").addClass("btn-success").removeClass("bg-white")
|
||||
li.find(".action").prepend("<i class='bi-check-circle-fill me-1 text-success' title='Already installed'></i>")
|
||||
}
|
||||
|
||||
li.click(repoChartClicked)
|
||||
|
||||
$("#sectionRepo .repo-details ul").append(li)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
if (getHashParam("repo")) {
|
||||
items.find("input[value='" + getHashParam("repo") + "']").click()
|
||||
} else {
|
||||
items.find("input").first().click()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$("#inputSearch").keyup(function () {
|
||||
let val = $(this).val().toLowerCase();
|
||||
|
||||
$(".charts li").each(function () {
|
||||
let chartName = $(this.firstElementChild).text().toLowerCase()
|
||||
if (chartName.indexOf(val) >= 0) {
|
||||
$(this).show()
|
||||
} else {
|
||||
$(this).hide()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$("#sectionRepo .repo-list .btn").click(function () {
|
||||
const myModal = new bootstrap.Modal(document.getElementById('repoAddModal'), {});
|
||||
myModal.show()
|
||||
})
|
||||
|
||||
$("#repoAddModal .btn-confirm").click(function () {
|
||||
$("#repoAddModal .btn-confirm").prop("disabled", true).prepend('<span class="spinner-border spinner-border-sm mx-1" role="status" aria-hidden="true"></span>')
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "/api/helm/repo",
|
||||
data: $("#repoAddModal form").serialize(),
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to add repo", xhr)
|
||||
}).done(function () {
|
||||
setHashParam("repo", $("#repoAddModal form input[name=name]").val())
|
||||
window.location.reload()
|
||||
})
|
||||
})
|
||||
|
||||
$("#sectionRepo .btn-remove").click(function () {
|
||||
if (confirm("Confirm removing repository?")) {
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: "/api/helm/repo?name=" + $("#sectionRepo .repo-details h2").text(),
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to add repo", xhr)
|
||||
}).done(function () {
|
||||
setHashParam("repo", null)
|
||||
window.location.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
$("#sectionRepo .btn-update").click(function () {
|
||||
$("#sectionRepo .btn-update i").removeClass("bi-arrow-repeat").append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: "/api/helm/repo/update?name=" + $("#sectionRepo .repo-details h2").text(),
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to add repo", xhr)
|
||||
}).done(function () {
|
||||
window.location.reload()
|
||||
})
|
||||
})
|
||||
|
||||
function repoChartClicked() {
|
||||
const self = $(this)
|
||||
const elm = self.data("item")
|
||||
if (elm.installed_namespace) {
|
||||
setHashParam("section", null)
|
||||
setHashParam("namespace", elm.installed_namespace)
|
||||
setHashParam("chart", elm.installed_name)
|
||||
window.location.reload()
|
||||
} else {
|
||||
const contexts = $("body").data("contexts")
|
||||
const contextNamespace = contexts.filter(obj => {return obj.Name === getHashParam("context")})[0].Namespace
|
||||
popUpUpgrade(elm, contextNamespace)
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ function fillChartHistory(data, namespace, name) {
|
||||
$("#specRev").val(elm.revision).data("last-rev", elm.revision).data("last-chart-ver", elm.chart_ver)
|
||||
}
|
||||
|
||||
const rev = $(`<li class="px-2 pt-5 pb-4 mb-2 rounded border border-secondary bg-secondary position-relative">
|
||||
const rev = $(`<li class="px-2 pt-5 pb-4 mb-2 rounded border border-secondary bg-secondary position-relative link">
|
||||
<div class="rev-status position-absolute top-0 m-2 mb-5 start-0 fw-bold"></div>
|
||||
<div class="rev-number position-absolute top-0 m-2 mb-5 end-0 fw-bold fs-6"></div>
|
||||
<div class="rev-changes position-absolute bottom-0 start-0 m-2 text-muted small"></div>
|
||||
@@ -48,22 +48,26 @@ function fillChartHistory(data, namespace, name) {
|
||||
|
||||
if (elm.description.startsWith("Rollback to ")) {
|
||||
//rev.find(".rev-status").append(" <span class='small fw-normal text-lowercase'>(rollback)</span>")
|
||||
rev.find(".rev-status").append(" <i class='bi-arrow-counterclockwise text-muted' title='"+elm.description+"'></i>")
|
||||
rev.find(".rev-status").append(" <i class='bi-arrow-counterclockwise text-muted' title='" + elm.description + "'></i>")
|
||||
}
|
||||
|
||||
const nxt = data[x + 1];
|
||||
if (nxt && isNewerVersion(elm.chart_ver, nxt.chart_ver)) {
|
||||
rev.find(".rev-changes").html("<span class='strike'>" + nxt.chart_ver + "</span> <i class='text-danger bi-arrow-down-right'></i> " + elm.chart_ver)
|
||||
rev.find(".rev-changes").html("<span class='strike'>" + nxt.chart_ver + "</span> <i class='bi-arrow-down-right'></i> " + elm.chart_ver)
|
||||
} else if (nxt && isNewerVersion(nxt.chart_ver, elm.chart_ver)) {
|
||||
rev.find(".rev-changes").html("<span class='strike'>" + nxt.chart_ver + "</span> <i class='text-success bi-arrow-up-right'></i> " + elm.chart_ver)
|
||||
rev.find(".rev-changes").html("<span class='strike'>" + nxt.chart_ver + "</span> <i class='bi-arrow-up-right'></i> " + elm.chart_ver)
|
||||
}
|
||||
|
||||
rev.data("elm", elm)
|
||||
rev.addClass("rev-" + elm.revision)
|
||||
rev.click(function () {
|
||||
if (window.getSelection().toString()) {
|
||||
return
|
||||
}
|
||||
revisionClicked(namespace, name, $(this))
|
||||
})
|
||||
|
||||
// revRow.attr("class", "link")
|
||||
revRow.append(rev)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,81 @@
|
||||
$(function () {
|
||||
let limNS = null
|
||||
$.getJSON("/status").fail(function (xhr) { // maybe /options call in the future
|
||||
reportError("Failed to get tool version", xhr)
|
||||
}).done(function (data) {
|
||||
fillToolVersion(data)
|
||||
limNS = data.LimitedToNamespace
|
||||
if (limNS) {
|
||||
$("#limitNamespace").show().find("span").text(limNS)
|
||||
}
|
||||
fillClusters(limNS)
|
||||
})
|
||||
|
||||
$.getJSON("/api/scanners").fail(function (xhr) {
|
||||
reportError("Failed to get list of scanners", xhr)
|
||||
}).done(function (data) {
|
||||
$("body").data("scanners", data)
|
||||
for (let k in data) {
|
||||
if (data[k].ManifestScannable) {
|
||||
$("#upgradeModal .btn-scan").show() // TODO: move this to install flow
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function fillClusters(limNS) {
|
||||
const clusterSelect = $("#cluster");
|
||||
clusterSelect.change(function () {
|
||||
window.location.href = "/#context=" + clusterSelect.find("input:radio:checked").val()
|
||||
window.location.reload()
|
||||
})
|
||||
const namespaceSelect = $("#namespace");
|
||||
namespaceSelect.change(function () {
|
||||
let filteredNamespaces = []
|
||||
namespaceSelect.find("input:checkbox:checked").each(function () {
|
||||
filteredNamespaces.push($(this).val());
|
||||
})
|
||||
setFilteredNamespaces(filteredNamespaces)
|
||||
filterInstalledList($("#installedList .body .row"))
|
||||
})
|
||||
|
||||
$.getJSON("/api/kube/contexts").fail(function (xhr) {
|
||||
sendStats('contexts', {'status': 'fail'});
|
||||
reportError("Failed to get list of clusters", xhr)
|
||||
}).done(function (data) {
|
||||
$("body").data("contexts", data)
|
||||
const context = getHashParam("context")
|
||||
data.sort((a, b) => (getCleanClusterName(a.Name) > getCleanClusterName(b.Name)) - (getCleanClusterName(a.Name) < getCleanClusterName(b.Name)))
|
||||
fillClusterList(data, context);
|
||||
sendStats('contexts', {'status': 'success', length:data.length});
|
||||
$.getJSON("/api/kube/namespaces").fail(function (xhr) {
|
||||
reportError("Failed to get namespaces", xhr)
|
||||
}).done(function (res) {
|
||||
const ns = res.items.map(i => i.metadata.name)
|
||||
$.each(ns, function (i, item) {
|
||||
$("#upgradeModal #ns-datalist").append($("<option>", {
|
||||
value: item,
|
||||
text: item
|
||||
}))
|
||||
})
|
||||
if (!limNS) {
|
||||
fillNamespaceList(res.items)
|
||||
}
|
||||
}).always(function () {
|
||||
initView(); // can only do it after loading cluster and namespace lists
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$(".section").hide()
|
||||
|
||||
const section = getHashParam("section")
|
||||
if (section === "repository") {
|
||||
$("#topNav ul a.section-repo").addClass("active")
|
||||
loadRepoView()
|
||||
} else {
|
||||
$("#topNav ul a.section-installed").addClass("active")
|
||||
const namespace = getHashParam("namespace")
|
||||
const chart = getHashParam("chart")
|
||||
if (!chart) {
|
||||
@@ -18,9 +83,39 @@ $(function () {
|
||||
} else {
|
||||
loadChartHistory(namespace, chart)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$("#topNav ul a").click(function () {
|
||||
const self = $(this)
|
||||
if (self.hasClass("section-help")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#topNav ul a").removeClass("active")
|
||||
|
||||
const ctx = getHashParam("context")
|
||||
const filteredNamespace = getHashParam("filteredNamespace")
|
||||
setHashParam(null, null)
|
||||
setHashParam("context", ctx)
|
||||
setHashParam("filteredNamespace", filteredNamespace)
|
||||
|
||||
if (self.hasClass("section-repo")) {
|
||||
setHashParam("section", "repository")
|
||||
} else if (self.hasClass("section-installed")) {
|
||||
setHashParam("section", null)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
initView()
|
||||
})
|
||||
|
||||
const myAlert = document.getElementById('errorAlert')
|
||||
myAlert.addEventListener('close.bs.alert', event => {
|
||||
event.preventDefault()
|
||||
$("#errorAlert").hide()
|
||||
})
|
||||
|
||||
function reportError(err, xhr) {
|
||||
$("#errorAlert h4 span").text(err)
|
||||
@@ -37,8 +132,14 @@ function getHashParam(name) {
|
||||
}
|
||||
|
||||
function setHashParam(name, val) {
|
||||
const params = new URLSearchParams(window.location.hash.substring(1))
|
||||
params.set(name, val)
|
||||
let params = new URLSearchParams(window.location.hash.substring(1))
|
||||
if (!name) {
|
||||
params = new URLSearchParams()
|
||||
} else if (!val) {
|
||||
params.delete(name)
|
||||
} else {
|
||||
params.set(name, val)
|
||||
}
|
||||
window.location.hash = new URLSearchParams(params).toString()
|
||||
}
|
||||
|
||||
@@ -63,33 +164,36 @@ function statusStyle(status, card, txt) {
|
||||
}
|
||||
|
||||
function getCleanClusterName(rawClusterName) {
|
||||
if (rawClusterName.indexOf('arn')==0) {
|
||||
if (rawClusterName.indexOf('arn') === 0) {
|
||||
// AWS cluster
|
||||
clusterSplit = rawClusterName.split(':')
|
||||
clusterName = clusterSplit.at(-1).split("/").at(-1)
|
||||
region = clusterSplit.at(-3)
|
||||
const clusterSplit = rawClusterName.split(':')
|
||||
const clusterName = clusterSplit.slice(-1)[0].replace('cluster/', '')
|
||||
const region = clusterSplit.at(-3)
|
||||
return region + "/" + clusterName + ' [AWS]'
|
||||
}
|
||||
if (rawClusterName.indexOf('gke')==0) {
|
||||
|
||||
if (rawClusterName.indexOf('gke') === 0) {
|
||||
// GKE cluster
|
||||
return rawClusterName.split('_').at(-2) + '/' + rawClusterName.split('_').at(-1) + ' [GKE]'
|
||||
}
|
||||
|
||||
return rawClusterName
|
||||
}
|
||||
|
||||
function fillClusterList(data, context) {
|
||||
if (!data || !data.length) {
|
||||
$("#cluster").append("No clusters listed in kubectl config, please configure some")
|
||||
return
|
||||
}
|
||||
data.forEach(function (elm) {
|
||||
// aws CLI uses complicated context names, the suffix does not work well
|
||||
// maybe we should have an `if` statement here
|
||||
let label = elm.Name //+ " (" + elm.Cluster + "/" + elm.AuthInfo + "/" + elm.Namespace + ")"
|
||||
let label = getCleanClusterName(elm.Name)
|
||||
let opt = $('<li><label><input type="radio" name="cluster" class="me-2"/><span></span></label></li>');
|
||||
opt.attr('title', label)
|
||||
opt.attr('title', elm.Name)
|
||||
opt.find("input").val(elm.Name).text(label)
|
||||
opt.find("span").text(getCleanClusterName(label))
|
||||
if (elm.IsCurrent && !context) {
|
||||
opt.find("input").prop("checked", true)
|
||||
setCurrentContext(elm.Name)
|
||||
} else if (context && elm.Name === context) {
|
||||
opt.find("span").text(label)
|
||||
const isCurrent = elm.IsCurrent && !context;
|
||||
const isSelected = context && elm.Name === context
|
||||
if (isCurrent || isSelected) {
|
||||
opt.find("input").prop("checked", true)
|
||||
setCurrentContext(elm.Name)
|
||||
}
|
||||
@@ -97,6 +201,33 @@ function fillClusterList(data, context) {
|
||||
})
|
||||
}
|
||||
|
||||
function fillNamespaceList(data) {
|
||||
const curContextNamespaces = $("body").data("contexts").filter(obj => {
|
||||
return obj.IsCurrent
|
||||
})
|
||||
|
||||
if (!data || !data.length) {
|
||||
$("#namespace").append("default")
|
||||
return
|
||||
}
|
||||
Array.from(data).forEach(function (elm) {
|
||||
const filteredNamespace = getHashParam("filteredNamespace")
|
||||
let opt = $('<li class="display-none"><label><input type="checkbox" name="namespace" class="me-2"/><span></span><span class="text-muted ms-2"></span></label></li>');
|
||||
opt.attr('title', elm.metadata.name)
|
||||
opt.find("input").val(elm.metadata.name).text(elm.metadata.name)
|
||||
opt.find("span").text(elm.metadata.name)
|
||||
if (filteredNamespace) {
|
||||
if (filteredNamespace.split('+').includes(elm.metadata.name)) {
|
||||
opt.find("input").prop("checked", true)
|
||||
}
|
||||
} else if (curContextNamespaces && curContextNamespaces[0].Namespace === elm.metadata.name) {
|
||||
opt.find("input").prop("checked", true)
|
||||
setFilteredNamespaces([elm.metadata.name])
|
||||
}
|
||||
$("#namespace").append(opt)
|
||||
})
|
||||
}
|
||||
|
||||
function setCurrentContext(ctx) {
|
||||
setHashParam("context", ctx)
|
||||
$.ajaxSetup({
|
||||
@@ -133,11 +264,20 @@ $(".bi-power").click(function () {
|
||||
url: "/",
|
||||
type: 'DELETE',
|
||||
}).done(function () {
|
||||
// TODO: display explanation overlay here
|
||||
window.close();
|
||||
})
|
||||
})
|
||||
|
||||
function isNewerVersion(oldVer, newVer) {
|
||||
if (oldVer && oldVer[0] === 'v') {
|
||||
oldVer = oldVer.substring(1)
|
||||
}
|
||||
|
||||
if (newVer && newVer[0] === 'v') {
|
||||
newVer = newVer.substring(1)
|
||||
}
|
||||
|
||||
const oldParts = oldVer.split('.')
|
||||
const newParts = newVer.split('.')
|
||||
for (let i = 0; i < newParts.length; i++) {
|
||||
@@ -147,4 +287,60 @@ function isNewerVersion(oldVer, newVer) {
|
||||
if (a < b) return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function fillToolVersion(data) {
|
||||
$("#toolVersion").text(data.CurVer)
|
||||
if (isNewerVersion(data.CurVer, data.LatestVer)) {
|
||||
$("#toolVersionUpgrade").text(data.LatestVer)
|
||||
$(".upgrade-possible").show()
|
||||
}
|
||||
}
|
||||
|
||||
$("#cacheClear").click(function () {
|
||||
$.ajax({
|
||||
url: "/api/cache",
|
||||
type: 'DELETE',
|
||||
}).done(function () {
|
||||
window.location.reload()
|
||||
})
|
||||
})
|
||||
|
||||
function showHideInstalledRelease(card, filteredNamespaces, filterStr) {
|
||||
let releaseNamespace = card.data("namespace")
|
||||
let releaseName = card.data("name")
|
||||
let chartName = card.data("chart").chart
|
||||
const shownByNS = !filteredNamespaces || filteredNamespaces.split('+').includes(releaseNamespace);
|
||||
const shownByStr = releaseName.indexOf(filterStr) >= 0 || chartName.indexOf(filterStr) >= 0
|
||||
if (shownByNS && shownByStr) {
|
||||
card.show()
|
||||
return true
|
||||
} else {
|
||||
card.hide()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function filterInstalledList(list) {
|
||||
const warnMsg = $("#installedList .all-filtered").hide();
|
||||
|
||||
let filterStr = $("#installedSearch").val().toLowerCase();
|
||||
let filteredNamespaces = getHashParam("filteredNamespace")
|
||||
let anyShown = false;
|
||||
list.each(function (ix, card) {
|
||||
anyShown |= showHideInstalledRelease($(card), filteredNamespaces, filterStr)
|
||||
})
|
||||
|
||||
if (list.length && !anyShown) {
|
||||
warnMsg.show()
|
||||
}
|
||||
}
|
||||
|
||||
function setFilteredNamespaces(filteredNamespaces) {
|
||||
if (filteredNamespaces.length === 0 && getHashParam("filteredNamespace")) {
|
||||
setHashParam("filteredNamespace")
|
||||
} else if (filteredNamespaces.length !== 0) {
|
||||
setHashParam("filteredNamespace", filteredNamespaces.join('+'))
|
||||
}
|
||||
}
|
||||
81
pkg/dashboard/static/styles-base.css
Normal file
@@ -0,0 +1,81 @@
|
||||
.link, .nav-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.strike {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #707583 !important;
|
||||
}
|
||||
|
||||
.border-other {
|
||||
border-color: #9195A1 !important;
|
||||
}
|
||||
|
||||
.text-other {
|
||||
color: #9195A1 !important;
|
||||
}
|
||||
|
||||
.border-failed {
|
||||
border-color: #FC1683 !important;
|
||||
}
|
||||
|
||||
.text-failed {
|
||||
color: #FC1683 !important;
|
||||
}
|
||||
|
||||
.border-deployed {
|
||||
border-color: #1BE99A !important;
|
||||
}
|
||||
|
||||
.text-deployed {
|
||||
color: #1FA470 !important;
|
||||
}
|
||||
|
||||
.border-pending {
|
||||
border-color: #5AB0FF !important;
|
||||
}
|
||||
|
||||
.text-pending {
|
||||
color: #5AB0FF !important;
|
||||
}
|
||||
|
||||
.bg-tag {
|
||||
background-color: #D6EFFE;
|
||||
}
|
||||
|
||||
.bg-tag.text-dark {
|
||||
color: #333333 !important;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background-color: #FC1683 !important;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background-color: #A4F8D7 !important;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #1347FF;
|
||||
}
|
||||
|
||||
.text-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.offcanvas {
|
||||
width: auto !important;
|
||||
max-width: 90%;
|
||||
min-width: 60%;
|
||||
}
|
||||
|
||||
.fs-80 {
|
||||
font-size: 0.8rem!important;
|
||||
}
|
||||
@@ -1,77 +1,3 @@
|
||||
.strike {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #707583 !important;
|
||||
}
|
||||
|
||||
.border-other {
|
||||
border-color: #9195A1 !important;
|
||||
}
|
||||
|
||||
.text-other {
|
||||
color: #9195A1 !important;
|
||||
}
|
||||
|
||||
.border-failed {
|
||||
border-color: #FC1683 !important;
|
||||
}
|
||||
|
||||
.text-failed {
|
||||
color: #FC1683 !important;
|
||||
}
|
||||
|
||||
.border-deployed {
|
||||
border-color: #1BE99A !important;
|
||||
}
|
||||
|
||||
.text-deployed {
|
||||
color: #1FA470 !important;
|
||||
}
|
||||
|
||||
.border-pending {
|
||||
border-color: #5AB0FF !important;
|
||||
}
|
||||
|
||||
.text-pending {
|
||||
color: #5AB0FF !important;
|
||||
}
|
||||
|
||||
.bg-tag {
|
||||
background-color: #D6EFFE;
|
||||
}
|
||||
|
||||
.bg-tag.text-dark {
|
||||
color: #333333 !important;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background-color: #FC1683 !important;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background-color: #A4F8D7 !important;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #1347FF;
|
||||
}
|
||||
|
||||
.text-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.offcanvas {
|
||||
width: auto !important;
|
||||
max-width: 90%;
|
||||
min-width: 60%;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
}
|
||||
@@ -100,22 +26,11 @@ body > .container-fluid {
|
||||
min-height: 100% !important;
|
||||
}
|
||||
|
||||
#topNav.navbar {
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-family: Poppins, serif;
|
||||
font-size: 0.6rem !important;
|
||||
color: #707583;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-brand > a > img {
|
||||
vertical-align: middle;
|
||||
height: 2.5rem;
|
||||
height: 3rem;
|
||||
display: inline-block;
|
||||
margin: 0.5rem 0.8rem
|
||||
margin: 0.25rem 0.75rem
|
||||
}
|
||||
|
||||
.navbar-brand > div {
|
||||
@@ -152,6 +67,10 @@ body > .container-fluid {
|
||||
color: #3B3D45 !important;
|
||||
}
|
||||
|
||||
#topNav .nav-link.text-danger {
|
||||
color: #FC1683 !important;
|
||||
}
|
||||
|
||||
#topNav .nav-link.active {
|
||||
background: #EBEFFF;
|
||||
border-radius: 2px;
|
||||
@@ -184,6 +103,13 @@ body > .container-fluid {
|
||||
margin-bottom: 0.75rem !important;
|
||||
}
|
||||
|
||||
#installedList .header .spaced-out {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
#installedList h2 {
|
||||
font-family: Inter, serif;
|
||||
font-weight: 700;
|
||||
@@ -230,6 +156,10 @@ span.link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#installedList .body .row div {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#installedList .rel-name {
|
||||
padding-left: 5.5rem;
|
||||
background-image: url("helm-gray-50.svg");
|
||||
@@ -239,6 +169,10 @@ span.link {
|
||||
background-size: 3rem;
|
||||
}
|
||||
|
||||
#installedList .rel-name div {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#installedList .rel-name span {
|
||||
font-family: Roboto Slab, sans-serif;
|
||||
font-weight: 700;
|
||||
@@ -284,23 +218,23 @@ span.link {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
nav .nav-tabs {
|
||||
border: none;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
nav .nav-tabs .nav-link {
|
||||
padding-bottom: 0.25rem;
|
||||
color: #3B3D45;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
nav .nav-tabs .nav-link.active {
|
||||
border: none;
|
||||
border-bottom: 3px solid #3B3D45;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#installedList .b-shadow:hover {
|
||||
#installedList .body .b-shadow:hover {
|
||||
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
@@ -325,7 +259,7 @@ span.link {
|
||||
}
|
||||
|
||||
#nav-resources .bg-secondary {
|
||||
background-color: #E6E7EB!important;
|
||||
background-color: #E6E7EB !important;
|
||||
}
|
||||
|
||||
.res-actions .btn-sm {
|
||||
@@ -349,4 +283,16 @@ span.link {
|
||||
|
||||
#describeModalBody pre {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#sectionRepo .repo-details ul .row .btn {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#sectionRepo .repo-details ul .row:hover {
|
||||
background-color: #F4F7FA !important;
|
||||
}
|
||||
|
||||
#sectionRepo .repo-details ul .row:hover .btn {
|
||||
visibility: visible;
|
||||
}
|
||||
100
pkg/dashboard/subproc/cache.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package subproc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/eko/gocache/v3/marshaler"
|
||||
"github.com/eko/gocache/v3/store"
|
||||
gocache "github.com/patrickmn/go-cache"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CacheKey = string
|
||||
|
||||
const CacheKeyRelList CacheKey = "installed-releases-list"
|
||||
const CacheKeyShowChart CacheKey = "show-chart"
|
||||
const CacheKeyRelHistory CacheKey = "release-history"
|
||||
const CacheKeyRevManifests CacheKey = "rev-manifests"
|
||||
const CacheKeyRevNotes CacheKey = "rev-notes"
|
||||
const CacheKeyRevValues CacheKey = "rev-values"
|
||||
const CacheKeyRepoChartValues CacheKey = "chart-values"
|
||||
const CacheKeyAllRepos CacheKey = "all-repos"
|
||||
|
||||
type Cache struct {
|
||||
Marshaler *marshaler.Marshaler `json:"-"`
|
||||
HitCount int
|
||||
MissCount int
|
||||
}
|
||||
|
||||
func NewCache() *Cache {
|
||||
gocacheClient := gocache.New(60*time.Minute, 10*time.Minute)
|
||||
gocacheStore := store.NewGoCache(gocacheClient)
|
||||
|
||||
// TODO: use tiered cache with some disk backend, allow configuring that static cache folder
|
||||
|
||||
// Initializes marshaler
|
||||
marshal := marshaler.New(gocacheStore)
|
||||
return &Cache{
|
||||
Marshaler: marshal,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) String(key CacheKey, tags []string, callback func() (string, error)) (string, error) {
|
||||
if tags == nil {
|
||||
tags = make([]string, 0)
|
||||
}
|
||||
tags = append(tags, key)
|
||||
|
||||
ctx := context.Background()
|
||||
out := ""
|
||||
_, err := c.Marshaler.Get(ctx, key, &out)
|
||||
if err == nil {
|
||||
log.Debugf("Using cached value for %s", key)
|
||||
c.HitCount++
|
||||
return out, nil
|
||||
} else if !errors.Is(err, store.NotFound{}) {
|
||||
return "", err
|
||||
}
|
||||
c.MissCount++
|
||||
|
||||
out, err = callback()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = c.Marshaler.Set(ctx, key, out, store.WithTags(tags))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Cache) Invalidate(tags ...CacheKey) {
|
||||
log.Debugf("Invalidating tags %v", tags)
|
||||
err := c.Marshaler.Invalidate(context.Background(), store.WithInvalidateTags(tags))
|
||||
if err != nil {
|
||||
log.Warnf("Failed to invalidate tags %v: %s", tags, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Clear() error {
|
||||
c.HitCount = 0
|
||||
c.MissCount = 0
|
||||
return c.Marshaler.Clear(context.Background())
|
||||
}
|
||||
|
||||
func cacheTagRelease(namespace string, name string) CacheKey {
|
||||
return "release" + "\v" + namespace + "\v" + name
|
||||
}
|
||||
func cacheTagRepoVers(chartName string) CacheKey {
|
||||
return "repo-versions" + "\v" + chartName
|
||||
}
|
||||
|
||||
func cacheTagRepoCharts(name string) CacheKey {
|
||||
return "repo-charts" + "\v" + name
|
||||
}
|
||||
|
||||
func cacheTagRepoName(name string) CacheKey {
|
||||
return "repo-name" + "\v" + name
|
||||
}
|
||||
299
pkg/dashboard/subproc/data.go
Normal file
@@ -0,0 +1,299 @@
|
||||
package subproc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hexops/gotextdiff"
|
||||
"github.com/hexops/gotextdiff/myers"
|
||||
"github.com/hexops/gotextdiff/span"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
)
|
||||
|
||||
type DataLayer struct {
|
||||
KubeContext string
|
||||
Helm string
|
||||
Kubectl string
|
||||
Scanners []Scanner
|
||||
StatusInfo *StatusInfo
|
||||
Namespace string
|
||||
Cache *Cache
|
||||
}
|
||||
|
||||
type StatusInfo struct {
|
||||
CurVer string
|
||||
LatestVer string
|
||||
Analytics bool
|
||||
LimitedToNamespace string
|
||||
CacheHitRatio float64
|
||||
}
|
||||
|
||||
func (d *DataLayer) runCommand(cmd ...string) (string, error) {
|
||||
for i, c := range cmd {
|
||||
// TODO: remove namespace parameter if it's empty
|
||||
if c == "--namespace" && i < len(cmd) { // TODO: in case it's not found - add it?
|
||||
d.forceNamespace(&cmd[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
return utils.RunCommand(cmd, map[string]string{"HELM_KUBECONTEXT": d.KubeContext})
|
||||
}
|
||||
|
||||
func (d *DataLayer) runCommandHelm(cmd ...string) (string, error) {
|
||||
if d.Helm == "" {
|
||||
d.Helm = "helm"
|
||||
}
|
||||
|
||||
cmd = append([]string{d.Helm}, cmd...)
|
||||
if d.KubeContext != "" {
|
||||
cmd = append(cmd, "--kube-context", d.KubeContext)
|
||||
}
|
||||
|
||||
return d.runCommand(cmd...)
|
||||
}
|
||||
|
||||
func (d *DataLayer) forceNamespace(s *string) {
|
||||
if d.Namespace != "" {
|
||||
*s = d.Namespace
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataLayer) CheckConnectivity() error {
|
||||
contexts, err := d.ListContexts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(contexts) < 1 {
|
||||
return errors.New("did not find any kubectl contexts configured")
|
||||
}
|
||||
|
||||
_, err = d.runCommandHelm("--help") // no point in doing is, since the default context may be invalid
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) GetStatus() *StatusInfo {
|
||||
sum := float64(d.Cache.HitCount + d.Cache.MissCount)
|
||||
if sum > 0 {
|
||||
d.StatusInfo.CacheHitRatio = float64(d.Cache.HitCount) / sum
|
||||
} else {
|
||||
d.StatusInfo.CacheHitRatio = 0
|
||||
}
|
||||
return d.StatusInfo
|
||||
}
|
||||
|
||||
func (d *DataLayer) ListInstalled() (res []ReleaseElement, err error) {
|
||||
cmd := []string{"ls", "--all", "--output", "json", "--time-format", time.RFC3339}
|
||||
|
||||
// TODO: filter by namespace
|
||||
if d.Namespace == "" {
|
||||
cmd = append(cmd, "--all-namespaces")
|
||||
} else {
|
||||
cmd = append(cmd, "--namespace", d.Namespace)
|
||||
}
|
||||
|
||||
out, err := d.Cache.String(CacheKeyRelList, nil, func() (string, error) {
|
||||
return d.runCommandHelm(cmd...)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ReleaseHistory(namespace string, releaseName string) (res []*HistoryElement, err error) {
|
||||
// TODO: there is `max` but there is no `offset`
|
||||
ct := cacheTagRelease(namespace, releaseName)
|
||||
out, err := d.Cache.String(CacheKeyRelHistory+ct, []string{ct}, func() (string, error) {
|
||||
return d.runCommandHelm("history", releaseName, "--namespace", namespace, "--output", "json")
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, elm := range res {
|
||||
chartRepoName, curVer, err := utils.ChartAndVersion(elm.Chart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elm.ChartName = chartRepoName // TODO: move it to frontend?
|
||||
elm.ChartVer = curVer
|
||||
elm.Updated.Time = elm.Updated.Time.Round(time.Second)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type SectionFn = func(string, string, int, bool) (string, error) // TODO: rework it into struct-based argument?
|
||||
|
||||
func (d *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
cmd := []string{"get", "manifest", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision)}
|
||||
|
||||
key := CacheKeyRevManifests + "\v" + namespace + "\v" + chartName + "\v" + strconv.Itoa(revision)
|
||||
return d.Cache.String(key, nil, func() (string, error) {
|
||||
return d.runCommandHelm(cmd...)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string, revision int) ([]*v1.Carp, error) {
|
||||
out, err := d.RevisionManifests(namespace, chartName, revision, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec := yaml.NewDecoder(bytes.NewReader([]byte(out)))
|
||||
|
||||
res := make([]*v1.Carp, 0)
|
||||
var tmp interface{}
|
||||
for dec.Decode(&tmp) == nil {
|
||||
// k8s libs uses only JSON tags defined, say hello to https://github.com/go-yaml/yaml/issues/424
|
||||
// we can juggle it
|
||||
jsoned, err := json.Marshal(tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var doc v1.Carp
|
||||
err = json.Unmarshal(jsoned, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if doc.Kind == "" {
|
||||
log.Warnf("Manifest piece is not k8s resource: %s", jsoned)
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, &doc)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionNotes(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
cmd := []string{"get", "notes", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision)}
|
||||
key := CacheKeyRevNotes + "\v" + namespace + "\v" + chartName + "\v" + strconv.Itoa(revision)
|
||||
return d.Cache.String(key, nil, func() (string, error) {
|
||||
return d.runCommandHelm(cmd...)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionValues(namespace string, chartName string, revision int, onlyUserDefined bool) (res string, err error) {
|
||||
cmd := []string{"get", "values", chartName, "--namespace", namespace, "--output", "yaml", "--revision", strconv.Itoa(revision)}
|
||||
|
||||
if !onlyUserDefined {
|
||||
cmd = append(cmd, "--all")
|
||||
}
|
||||
|
||||
key := CacheKeyRevValues + "\v" + namespace + "\v" + chartName + "\v" + strconv.Itoa(revision) + "\v" + fmt.Sprintf("%v", onlyUserDefined)
|
||||
return d.Cache.String(key, nil, func() (string, error) {
|
||||
return d.runCommandHelm(cmd...)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DataLayer) ReleaseUninstall(namespace string, name string) error {
|
||||
d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
|
||||
|
||||
_, err := d.runCommandHelm("uninstall", name, "--namespace", namespace)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DataLayer) Rollback(namespace string, name string, rev int) error {
|
||||
d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
|
||||
|
||||
_, err := d.runCommandHelm("rollback", name, strconv.Itoa(rev), "--namespace", namespace)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string, version string, justTemplate bool, values string, reuseVals bool) (string, error) {
|
||||
if values == "" && reuseVals {
|
||||
oldVals, err := d.RevisionValues(namespace, name, 0, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
values = oldVals
|
||||
}
|
||||
|
||||
valsFile, close1, err := utils.TempFile(values)
|
||||
defer close1()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd := []string{"upgrade", "--install", "--create-namespace", name, repoChart, "--version", version, "--namespace", namespace, "--values", valsFile, "--output", "json"}
|
||||
if justTemplate {
|
||||
cmd = append(cmd, "--dry-run")
|
||||
}
|
||||
|
||||
out, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !justTemplate {
|
||||
d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
|
||||
}
|
||||
|
||||
res := release.Release{}
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if justTemplate {
|
||||
out = strings.TrimSpace(res.Manifest)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) {
|
||||
if revision1 == 0 || revision2 == 0 {
|
||||
log.Debugf("One of revisions is zero: %d %d", revision1, revision2)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
manifest1, err := functor(namespace, name, revision1, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
manifest2, err := functor(namespace, name, revision2, flag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
diff := GetDiff(manifest1, manifest2, strconv.Itoa(revision1)+ext, strconv.Itoa(revision2)+ext)
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func GetDiff(text1 string, text2 string, name1 string, name2 string) string {
|
||||
edits := myers.ComputeEdits(span.URIFromPath(""), text1, text2)
|
||||
unified := gotextdiff.ToUnified(name1, name2, text1, edits)
|
||||
diff := fmt.Sprint(unified)
|
||||
log.Debugf("The diff is: %s", diff)
|
||||
return diff
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package dashboard
|
||||
package subproc
|
||||
|
||||
import (
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
@@ -11,7 +13,9 @@ func TestFlow(t *testing.T) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
var _ release.Status
|
||||
data := DataLayer{}
|
||||
data := DataLayer{
|
||||
Cache: NewCache(),
|
||||
}
|
||||
err := data.CheckConnectivity()
|
||||
if err != nil {
|
||||
if err.Error() == "did not find any kubectl contexts configured" {
|
||||
@@ -38,13 +42,13 @@ func TestFlow(t *testing.T) {
|
||||
}
|
||||
|
||||
chart := installed[1]
|
||||
history, err := data.ChartHistory(chart.Namespace, chart.Name)
|
||||
history, err := data.ReleaseHistory(chart.Namespace, chart.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = history
|
||||
|
||||
chartRepoName, curVer, err := chartAndVersion(chart.Chart)
|
||||
chartRepoName, curVer, err := utils.ChartAndVersion(chart.Chart)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +67,7 @@ func TestFlow(t *testing.T) {
|
||||
_ = manifests
|
||||
|
||||
var wg sync.WaitGroup
|
||||
res := make([]*GenericResource, 0)
|
||||
res := make([]*v1.Carp, 0)
|
||||
for _, m := range manifests {
|
||||
wg.Add(1)
|
||||
mc := m // fix the clojure
|
||||
@@ -1,4 +1,4 @@
|
||||
package dashboard
|
||||
package subproc
|
||||
|
||||
import (
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
)
|
||||
|
||||
// unpleasant copy from Helm sources, where they have it non-public
|
||||
type releaseElement struct {
|
||||
|
||||
type ReleaseElement struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Revision string `json:"revision"`
|
||||
@@ -16,20 +17,29 @@ type releaseElement struct {
|
||||
AppVersion string `json:"app_version"`
|
||||
}
|
||||
|
||||
type historyElement struct {
|
||||
type HistoryElement struct {
|
||||
Revision int `json:"revision"`
|
||||
Updated helmtime.Time `json:"updated"`
|
||||
Status release.Status `json:"status"`
|
||||
Chart string `json:"chart"`
|
||||
AppVersion string `json:"app_version"`
|
||||
Description string `json:"description"`
|
||||
ChartName string `json:"chart_name"`
|
||||
ChartVer string `json:"chart_ver"`
|
||||
|
||||
ChartName string `json:"chart_name"` // custom addition on top of Helm
|
||||
ChartVer string `json:"chart_ver"` // custom addition on top of Helm
|
||||
}
|
||||
|
||||
type repoChartElement struct {
|
||||
type RepoChartElement struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
AppVersion string `json:"app_version"`
|
||||
Description string `json:"description"`
|
||||
|
||||
InstalledNamespace string `json:"installed_namespace"` // custom addition on top of Helm
|
||||
InstalledName string `json:"installed_name"` // custom addition on top of Helm
|
||||
}
|
||||
|
||||
type RepositoryElement struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
144
pkg/dashboard/subproc/kubectl.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package subproc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (d *DataLayer) runCommandKubectl(cmd ...string) (string, error) {
|
||||
if d.Kubectl == "" {
|
||||
d.Kubectl = "kubectl"
|
||||
}
|
||||
|
||||
cmd = append([]string{d.Kubectl}, cmd...)
|
||||
|
||||
if d.KubeContext != "" {
|
||||
cmd = append(cmd, "--context", d.KubeContext)
|
||||
}
|
||||
|
||||
return d.runCommand(cmd...)
|
||||
}
|
||||
|
||||
type KubeContext struct {
|
||||
IsCurrent bool
|
||||
Name string
|
||||
Cluster string
|
||||
AuthInfo string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
|
||||
out, err := d.runCommandKubectl("config", "get-contexts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// kubectl has no JSON output for it, we'll have to do custom text parsing
|
||||
lines := strings.Split(out, "\n")
|
||||
|
||||
// find field positions
|
||||
fields := regexp.MustCompile(`(\w+\s+)`).FindAllString(lines[0], -1)
|
||||
cur := len(fields[0])
|
||||
name := cur + len(fields[1])
|
||||
cluster := name + len(fields[2])
|
||||
auth := cluster + len(fields[3])
|
||||
|
||||
// read items
|
||||
for _, line := range lines[1:] {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
res = append(res, KubeContext{
|
||||
IsCurrent: strings.TrimSpace(line[0:cur]) == "*",
|
||||
Name: strings.TrimSpace(line[cur:name]),
|
||||
Cluster: strings.TrimSpace(line[name:cluster]),
|
||||
AuthInfo: strings.TrimSpace(line[cluster:auth]),
|
||||
Namespace: strings.TrimSpace(line[auth:]),
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type NamespaceElement struct {
|
||||
Items []struct {
|
||||
Metadata struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"metadata"`
|
||||
} `json:"items"`
|
||||
}
|
||||
|
||||
func (d *DataLayer) GetNameSpaces() (res *NamespaceElement, err error) {
|
||||
out, err := d.runCommandKubectl("get", "namespaces", "-o", "json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) GetResource(namespace string, def *v1.Carp) (*v1.Carp, error) {
|
||||
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "json")
|
||||
if err != nil {
|
||||
if strings.HasSuffix(strings.TrimSpace(err.Error()), " not found") {
|
||||
return &v1.Carp{
|
||||
Status: v1.CarpStatus{
|
||||
Phase: "NotFound",
|
||||
Message: err.Error(),
|
||||
Reason: "not found",
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var res v1.Carp
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(res.Status.Conditions, func(i, j int) bool {
|
||||
// some condition types always bubble up
|
||||
if res.Status.Conditions[i].Type == "Available" {
|
||||
return false
|
||||
}
|
||||
|
||||
if res.Status.Conditions[j].Type == "Available" {
|
||||
return true
|
||||
}
|
||||
|
||||
t1 := res.Status.Conditions[i].LastTransitionTime
|
||||
t2 := res.Status.Conditions[j].LastTransitionTime
|
||||
return t1.Time.Before(t2.Time)
|
||||
})
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) GetResourceYAML(namespace string, def *v1.Carp) (string, error) {
|
||||
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "yaml")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) DescribeResource(namespace string, kind string, name string) (string, error) {
|
||||
out, err := d.runCommandKubectl("describe", strings.ToLower(kind), name, "--namespace", namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
159
pkg/dashboard/subproc/repos.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package subproc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) {
|
||||
out, err := d.Cache.String(CacheKeyAllRepos, nil, func() (string, error) {
|
||||
return d.runCommandHelm("repo", "list", "--output", "json")
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoAdd(name string, url string) (string, error) {
|
||||
d.Cache.Invalidate(CacheKeyAllRepos)
|
||||
out, err := d.runCommandHelm("repo", "add", "--force-update", name, url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoDelete(name string) (string, error) {
|
||||
d.Cache.Invalidate(CacheKeyAllRepos)
|
||||
out, err := d.runCommandHelm("repo", "remove", name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoUpdate(name string) error {
|
||||
d.Cache.Invalidate(cacheTagRepoName(name), CacheKeyAllRepos)
|
||||
|
||||
cmd := []string{"repo", "update"}
|
||||
if name != "" {
|
||||
cmd = append(cmd, name)
|
||||
}
|
||||
|
||||
_, err := d.runCommandHelm(cmd...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoVersions(chartName string) (res []*RepoChartElement, err error) {
|
||||
search := "/" + chartName + "\v"
|
||||
if strings.Contains(chartName, "/") {
|
||||
search = "\v" + chartName + "\v"
|
||||
}
|
||||
|
||||
cmd := []string{"search", "repo", "--regexp", search, "--versions", "--output", "json"}
|
||||
out, err := d.Cache.String(cacheTagRepoVers(chartName), []string{CacheKeyAllRepos}, func() (string, error) {
|
||||
return d.runCommandHelm(cmd...)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) ChartRepoCharts(repoName string) (res []*RepoChartElement, err error) {
|
||||
cmd := []string{"search", "repo", "--regexp", "\v" + repoName + "/", "--output", "json"}
|
||||
out, err := d.Cache.String(cacheTagRepoCharts(repoName), []string{CacheKeyAllRepos}, func() (string, error) {
|
||||
return d.runCommandHelm(cmd...)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ins, err := d.ListInstalled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enrichRepoChartsWithInstalled(res, ins)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func enrichRepoChartsWithInstalled(charts []*RepoChartElement, installed []ReleaseElement) {
|
||||
for _, rchart := range charts {
|
||||
for _, rel := range installed {
|
||||
c, _, err := utils.ChartAndVersion(rel.Chart)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to parse chart: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pieces := strings.Split(rchart.Name, "/")
|
||||
if pieces[1] == c {
|
||||
// TODO: there can be more than one
|
||||
rchart.InstalledNamespace = rel.Namespace
|
||||
rchart.InstalledName = rel.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ShowValues get values from repo chart, not from installed release
|
||||
func (d *DataLayer) ShowValues(chart string, ver string) (string, error) {
|
||||
return d.Cache.String(CacheKeyRepoChartValues+"\v"+chart+"\v"+ver, nil, func() (string, error) {
|
||||
return d.runCommandHelm("show", "values", chart, "--version", ver)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DataLayer) ShowChart(chartName string) ([]*chart.Metadata, error) { // TODO: add version parameter to method
|
||||
out, err := d.Cache.String(CacheKeyShowChart+"\v"+chartName, []string{"chart\v" + chartName}, func() (string, error) {
|
||||
return d.runCommandHelm("show", "chart", chartName)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deccoder := yaml.NewDecoder(bytes.NewReader([]byte(out)))
|
||||
res := make([]*chart.Metadata, 0)
|
||||
var tmp interface{}
|
||||
|
||||
for deccoder.Decode(&tmp) == nil {
|
||||
jsoned, err := json.Marshal(tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resjson chart.Metadata
|
||||
err = json.Unmarshal(jsoned, &resjson)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, &resjson)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
17
pkg/dashboard/subproc/scan.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package subproc
|
||||
|
||||
type Scanner interface {
|
||||
Name() string // returns string label for the scanner
|
||||
Test() bool // test if the scanner is available
|
||||
ScanManifests(mnf string) (*ScanResults, error) // run the scanner on manifests
|
||||
ScanResource(ns string, kind string, name string) (*ScanResults, error) // run the scanner on k8s resource
|
||||
SupportedResourceKinds() []string
|
||||
ManifestIsScannable() bool
|
||||
}
|
||||
|
||||
type ScanResults struct {
|
||||
PassedCount int
|
||||
FailedCount int
|
||||
OrigReport interface{}
|
||||
Error error
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ControlChan = chan struct{}
|
||||
|
||||
func chartAndVersion(x string) (string, string, error) {
|
||||
lastInd := strings.LastIndex(x, "-")
|
||||
if lastInd < 0 {
|
||||
return "", "", errors.New("can't parse chart version string")
|
||||
}
|
||||
|
||||
return x[:lastInd], x[lastInd+1:], nil
|
||||
}
|
||||
127
pkg/dashboard/utils/utils.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var FailLogLevel = log.WarnLevel // allows to suppress error logging in some situations
|
||||
|
||||
type ControlChan = chan struct{}
|
||||
|
||||
func ChartAndVersion(x string) (string, string, error) {
|
||||
strs := strings.Split(x, "-")
|
||||
lens := len(strs)
|
||||
if lens < 2 {
|
||||
return "", "", errors.New("can't parse chart version string")
|
||||
} else if lens == 2 {
|
||||
return strs[0], strs[1], nil
|
||||
} else {
|
||||
// semver2 regex , add optional v prefix
|
||||
re := regexp.MustCompile(`v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`)
|
||||
match := re.FindString(x)
|
||||
lastInd := strings.LastIndex(x, match)
|
||||
return x[:lastInd-1], match, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TempFile(txt string) (string, func(), error) {
|
||||
file, err := ioutil.TempFile("", "helm_dahsboard_*.yaml")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(file.Name(), []byte(txt), 0600)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return file.Name(), func() { _ = os.Remove(file.Name()) }, nil
|
||||
}
|
||||
|
||||
type CmdError struct {
|
||||
Command []string
|
||||
OrigError error
|
||||
StdErr string
|
||||
}
|
||||
|
||||
func (e CmdError) Error() string {
|
||||
//return fmt.Sprintf("failed to run command %s:\nError: %s\nSTDERR:%s", e.Command, e.OrigError, e.StdErr)
|
||||
return string(e.StdErr)
|
||||
}
|
||||
|
||||
func RunCommand(cmd []string, env map[string]string) (string, error) {
|
||||
log.Debugf("Starting command: %s", cmd)
|
||||
prog := exec.Command(cmd[0], cmd[1:]...)
|
||||
prog.Env = os.Environ()
|
||||
|
||||
for k, v := range env {
|
||||
prog.Env = append(prog.Env, k+"="+v)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
prog.Stdout = &stdout
|
||||
|
||||
var stderr bytes.Buffer
|
||||
prog.Stderr = &stderr
|
||||
|
||||
if err := prog.Run(); err != nil {
|
||||
log.StandardLogger().Logf(FailLogLevel, "Failed command: %s", cmd)
|
||||
serr := stderr.Bytes()
|
||||
if serr != nil {
|
||||
log.StandardLogger().Logf(FailLogLevel, "STDERR:\n%s", serr)
|
||||
}
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
return "", CmdError{
|
||||
Command: cmd,
|
||||
StdErr: string(serr),
|
||||
OrigError: eerr,
|
||||
}
|
||||
}
|
||||
|
||||
return "", CmdError{
|
||||
Command: cmd,
|
||||
StdErr: string(serr),
|
||||
OrigError: err,
|
||||
}
|
||||
}
|
||||
|
||||
sout := stdout.Bytes()
|
||||
serr := stderr.Bytes()
|
||||
log.Debugf("Command STDOUT:\n%s", sout)
|
||||
log.Debugf("Command STDERR:\n%s", serr)
|
||||
return string(sout), nil
|
||||
}
|
||||
|
||||
type QueryProps struct {
|
||||
Namespace string
|
||||
Name string
|
||||
Revision int
|
||||
}
|
||||
|
||||
func GetQueryProps(c *gin.Context, revRequired bool) (*QueryProps, error) {
|
||||
qp := QueryProps{}
|
||||
|
||||
qp.Namespace = c.Query("namespace")
|
||||
qp.Name = c.Query("name")
|
||||
if qp.Name == "" {
|
||||
return nil, errors.New("missing required query string parameter: name")
|
||||
}
|
||||
|
||||
cRev, err := strconv.Atoi(c.Query("revision"))
|
||||
if err != nil && revRequired {
|
||||
return nil, err
|
||||
}
|
||||
qp.Revision = cRev
|
||||
|
||||
return &qp, nil
|
||||
}
|
||||
118
pkg/dashboard/utils/utils_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestGetQueryProps(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
tests := []struct {
|
||||
name string
|
||||
endpoint string
|
||||
revRequired bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Get query props - all set with revRequired true",
|
||||
wantErr: false,
|
||||
revRequired: true,
|
||||
endpoint: "/api/v1/namespaces/komodorio/charts?name=testing&namespace=testing&revision=1",
|
||||
},
|
||||
{
|
||||
name: "Get query props - no revision with revRequired true",
|
||||
wantErr: true,
|
||||
revRequired: true,
|
||||
endpoint: "/api/v1/namespaces/komodorio/charts?name=testing&namespace=testing",
|
||||
},
|
||||
{
|
||||
name: "Get query props - no namespace with revRequired true",
|
||||
wantErr: false,
|
||||
revRequired: true,
|
||||
endpoint: "/api/v1/namespaces/komodorio/charts?name=testing&revision=1",
|
||||
},
|
||||
{
|
||||
name: "Get query props - no name with revRequired true",
|
||||
wantErr: true,
|
||||
revRequired: true,
|
||||
endpoint: "/api/v1/namespaces/komodorio/charts?namespace=testing&revision=1",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest("GET", tt.endpoint, nil)
|
||||
_, err := GetQueryProps(c, tt.revRequired)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetQueryProps() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChartAndVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params string
|
||||
wantChart string
|
||||
wantVer string
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "Chart and version - successfully parsing chart and version",
|
||||
params: "chart-1.0.0",
|
||||
wantChart: "chart",
|
||||
wantVer: "1.0.0",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Chart and version - successfully parsing chart and version",
|
||||
params: "chart-v1.0.0",
|
||||
wantChart: "chart",
|
||||
wantVer: "v1.0.0",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Chart and version - successfully parsing chart and version",
|
||||
params: "chart-v1.0.0-alpha",
|
||||
wantChart: "chart",
|
||||
wantVer: "v1.0.0-alpha",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Chart and version - successfully parsing chart and version",
|
||||
params: "chart-1.0.0-alpha",
|
||||
wantChart: "chart",
|
||||
wantVer: "1.0.0-alpha",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "Chart and version - parsing chart without version",
|
||||
params: "chart",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a, b, err := ChartAndVersion(tt.params)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ChartAndVersion() error = %v, wantErr %v", err, tt.wantError)
|
||||
return
|
||||
}
|
||||
|
||||
if a != tt.wantChart {
|
||||
t.Errorf("ChartAndVersion() got = %v, want %v", a, tt.wantChart)
|
||||
}
|
||||
|
||||
if b != tt.wantVer {
|
||||
t.Errorf("ChartAndVersion() got1 = %v, want %v", b, tt.wantVer)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
name: "dashboard"
|
||||
version: "0.1.0"
|
||||
version: "0.2.8"
|
||||
usage: "A simplified way of working with Helm"
|
||||
description: "View HELM situation in nice web UI"
|
||||
command: "$HELM_PLUGIN_DIR/bin/helm-dashboard"
|
||||
ignoreFlags: false
|
||||
hooks:
|
||||
install: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh"
|
||||
update: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh"
|
||||
|
||||
BIN
screenshot_scan_manifest.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
screenshot_scan_resource.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
@@ -62,3 +62,8 @@ fi
|
||||
tar xzf "releases/v${version}.tar.gz" -C "releases/v${version}"
|
||||
mv "releases/v${version}/${name}" "bin/${name}" || \
|
||||
mv "releases/v${version}/${name}.exe" "bin/${name}"
|
||||
|
||||
echo
|
||||
echo "Helm Dashboard is installed, to start it, run in your terminal:"
|
||||
echo " helm dashboard"
|
||||
echo
|
||||