36 Commits

Author SHA1 Message Date
Andrei Pohilko
7de7c85426 Release 0.2.8 2022-11-24 16:01:36 +00:00
Andrei Pohilko
8e65c555e0 Fix analytics is enabled while in dev 2022-11-24 15:22:50 +00:00
Andrei Pohilko
2557e6b73d Don't complain if repo is not found for chart
Fixes #116
2022-11-24 15:15:20 +00:00
Andrei Pohilko
4f75ee06a0 Release 0.2.7 2022-11-24 12:07:29 +00:00
ronahk
717adc9e9c fix filter bug (#111) 2022-11-24 11:40:30 +00:00
Andrey Pokhilko
15adeb7cfa Only display NS that has charts (#113) 2022-11-24 13:21:20 +02:00
Itiel shwartz
0b06036a39 add stats and change heap (#108)
* add stats and change heap

* improve analytics

* revert main changes

* add repo
2022-11-24 10:35:45 +00:00
Andrei Pohilko
f7d4dcbff4 If cluster access is failed, still display UI
Fixes #109
2022-11-24 09:59:44 +00:00
Harshit Mehta
8334f2b0b2 Make the entire card clickable on Installed Charts view (#87)
Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
2022-11-24 09:15:33 +00:00
Harshit Mehta
5cccb1caa0 Fixes JS part of #95 (#110)
Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
2022-11-24 08:24:57 +00:00
Andrei Pohilko
db9cdeb1c9 Split data layer into separate files 2022-11-23 12:38:03 +00:00
ronahk
3abae8e49e Filter installed charts by namespace (#101)
* filter by namespace

* exists

* Some improvements, one thing resolved

* cleanup

* merge

* allow filtering by name

* filter by namespace

* changes

* change url parameter name

* keep filtered namespaces when refreshing and combine inpt and namespace filtering

* Refactoring

* Cleanup

* Forced NS handle

* remove else

Co-authored-by: Andrei Pohilko <andrei.pokhilko@gmail.com>
2022-11-23 13:38:09 +02:00
Andrey Pokhilko
bedb356b02 In-memory cache for speed-up (#88)
* Experiment with local cache

* Commit

* Cache all we can, invalidate later

* Commit

* separate cache class

* More cached

* Proper invalidate

* Complete the repos

* Fix the build

* Fix build

* Status reporting
2022-11-22 17:17:32 +02:00
ronahk
34a7dc57b2 Use namespace defined in kubeconfig as default in install view (#99)
* respect defualt namespace from kubeconfig

* respect defualt namespace from kubeconfig

* Replace exists with available - avoid confusion in status

* use data attribute

* remove commented line

* fix
2022-11-16 11:50:32 +00:00
ronahk
d0dbb42492 fix namespaces not updating when switching cluster (#102) 2022-11-16 09:59:38 +00:00
denganliang
1393b117cf fixbug: ChartAndVersion did not handle chart version correct in some case (#98)
* fixbug: ChartAndVersion did not handle chart version correct in some case

https://github.com/komodorio/helm-dashboard/issues/95

* use regexp to get version

* add test case
2022-11-15 15:12:07 +00:00
Harshit Mehta
cf407c63a2 Fixes #91 (#93)
Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
2022-11-11 08:43:55 +00:00
ronahk
3f00e8ef6d Sort resources by interesting items (#89)
* sort resources by intersting items

* add body class

* reuse the name data
2022-11-10 16:51:14 +00:00
ronahk
758b03de36 Validate tag name matches plugin version in gh actions (#80)
* validate that tag name matches plugin version in gh actions

* fail workflow if tag name doesn't match plugin version
2022-11-09 13:35:28 +00:00
Harshit Mehta
76d55f8e44 Enhancement/show chart icon and description (#70)
* New API to fetch chart.yaml for an installec chart

* Show icon and description for installed chart list

Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
2022-11-09 08:23:44 +00:00
Andrei Pohilko
b9392ab4c9 Merge branch 'main' of github.com:komodorio/helm-dashboard 2022-11-08 14:38:01 +00:00
Andrei Pohilko
dadf2d1bde Cosmetics 2022-11-08 14:37:48 +00:00
Harshit Mehta
74c2a3d6e7 Few UI Enhancements (#79)
- Add Install/Upgrade keywork dynamically on popup
- Sort Repo list alphabetically
- Sort CLuster list alphabetically

Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
2022-11-08 14:28:13 +00:00
Andrei Pohilko
2454fcf47c Show better message for empty contexts 2022-11-08 10:10:19 +00:00
siddhikhapare
96a7a429e1 getCleanClusterName fixed (#78) 2022-11-08 10:07:30 +00:00
Bhargav Ravuri
f29800ed5b CLI Flag --bind (#77)
* CLI Flag for Bind Address

Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io>

* Update Documentation for --bind

Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io>

Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io>
2022-11-07 16:05:01 +00:00
Andrei Pohilko
15ce9170f3 Don't take Helm's default namespace into account 2022-11-06 16:40:53 +00:00
Andrei Pohilko
9a144c1c6f Release 0.2.5 2022-11-06 16:22:30 +00:00
Andrei Pohilko
f897c0f197 Display forced namespace in UI 2022-11-06 16:16:36 +00:00
Andrey Pokhilko
671fa949df Improve resource scanning flow (#68)
* List supported resources for scanners

* Don't warn on scanner discovery commands

* Use scanner-to-resource map

* Save changes

* Scan result tabs

* Own table render for Checkov

* Scannable manifest flag for scanners
2022-11-06 15:56:34 +00:00
Duy Nguyen
612352d69f Add namespace dropdown (#67)
* add get namespace endpoint

* add namespace dropdown

* misc fix
2022-11-06 13:19:32 +00:00
Dimas Yudha P
ef31263797 refactoring makefile, also adding test (#71) 2022-11-03 16:47:28 +00:00
Andrei Pohilko
f64fbd4a2e Fix analytics version 2022-11-01 21:59:16 +00:00
Harshit Mehta
dffb8a726b Allow filtering on repository charts list (#61)
Fixes #46

Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
2022-11-01 21:58:34 +00:00
Andrey Pokhilko
14d4886e61 Pass the NS param from Helm correctly (#64) 2022-10-31 18:07:28 +00:00
Andrei Pohilko
0012b0a797 Explicit flag for parameters 2022-10-31 17:04:09 +00:00
32 changed files with 1700 additions and 455 deletions

View File

@@ -6,9 +6,33 @@ on:
- "*" - "*"
jobs: jobs:
release: pre_release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: 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 - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:

View File

@@ -1,9 +1,24 @@
pull: DATE ?= $(shell date +%FT%T%z)
git pull VERSION ?= $(git describe --tags --always --dirty --match=v* 2> /dev/null || \
cat $(CURDIR)/.version 2> /dev/null || echo "v0")
build: .PHONY: test
go build -o bin/dashboard . 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: .PHONY: build
DEBUG=1 ./bin/dashboard 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

View File

@@ -61,6 +61,8 @@ You can see the list of available command-line flags by running `helm dashboard
By default, the web server is only available locally. You can change that by specifying `HD_BIND` 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. 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 your port 8080 is busy, you can specify a different port to use via `--port <number>` command-line flag. If your port 8080 is busy, you can specify a different port to use via `--port <number>` command-line flag.

25
go.mod
View File

@@ -3,10 +3,13 @@ module github.com/komodorio/helm-dashboard
go 1.18 go 1.18
require ( require (
github.com/eko/gocache/v3 v3.1.1
github.com/gin-gonic/gin v1.8.1 github.com/gin-gonic/gin v1.8.1
github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/go-version v1.6.0
github.com/hexops/gotextdiff v1.0.3 github.com/hexops/gotextdiff v1.0.3
github.com/jessevdk/go-flags v1.5.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/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@@ -16,28 +19,50 @@ require (
require ( require (
github.com/Masterminds/semver/v3 v3.1.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/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/logr v1.2.3 // indirect
github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.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-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/goccy/go-json v0.9.11 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-isatty v0.0.16 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pegasus-kv/thrift v0.13.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.3 // indirect github.com/pelletier/go-toml/v2 v2.0.3 // 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/ugorji/go/codec v1.2.7 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // 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/sys v0.0.0-20220818161305-2296e01440c6 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/inf.v0 v0.9.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.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.70.0 // indirect k8s.io/klog/v2 v2.70.0 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect

533
go.sum
View File

@@ -1,17 +1,119 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
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/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
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/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/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= 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/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/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/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/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/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/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/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.9/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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/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 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 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 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
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.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 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-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 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 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= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
@@ -20,28 +122,108 @@ 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/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 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= 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 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
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 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/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.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=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.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.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.5/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 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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.0.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 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/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.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 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= 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/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/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= 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/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.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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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 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/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/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 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -52,31 +234,100 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-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.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/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/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
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.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.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 h1:h9JoA60e1dVEOpp0PFwJSmt1Htu057NUq9/bUwaO61s=
github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= 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/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/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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/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.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 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
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.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
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.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -85,64 +336,329 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= 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 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
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.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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-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-20191011191535-87dc89f01550/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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/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 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/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=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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-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=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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-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 h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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-20210514164344-f6687ab2804c/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=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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-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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-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-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=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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-20200930185726-fdedc70b468f/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210615035016-665e8c7367d1/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-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-20210806184541-e5e7981a1069/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-20220715151400-c0bba94af5f8/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-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 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-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-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-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-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-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=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
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-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
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-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/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=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
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/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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 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-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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 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/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/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 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -151,15 +667,32 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY= helm.sh/helm/v3 v3.9.4 h1:TCI1QhJUeLVOdccfdw+vnSEO3Td6gNqibptB04QtExY=
helm.sh/helm/v3 v3.9.4/go.mod h1:3eaWAIqzvlRSD06gR9MMwmp2KBKwlu9av1/1BZpjeWY= 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=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
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 h1:y6uTWaiqsPTPRewnXJ15IFyGmBo2qPt6enm4zszG8Z0=
k8s.io/apimachinery v0.25.0-alpha.2/go.mod h1:h34FtK3eCxige6ZIACdBSYExtDaKAUxoc7hVe2LOxzw= 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.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.70.0 h1:GMmmjoFOrNepPN0ZeGCzvD2Gh5IKRwdFx8W5PBxVTQU= k8s.io/klog/v2 v2.70.0 h1:GMmmjoFOrNepPN0ZeGCzvD2Gh5IKRwdFx8W5PBxVTQU=
k8s.io/klog/v2 v2.70.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 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 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 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=
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 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
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 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
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 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=

35
main.go
View File

@@ -2,16 +2,17 @@ package main
import ( import (
"fmt" "fmt"
"os"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/jessevdk/go-flags" "github.com/jessevdk/go-flags"
"github.com/komodorio/helm-dashboard/pkg/dashboard" "github.com/komodorio/helm-dashboard/pkg/dashboard"
"github.com/pkg/browser" "github.com/pkg/browser"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"os"
) )
var ( var (
version = "dev" version = "0.0.0"
commit = "none" commit = "none"
date = "unknown" date = "unknown"
) )
@@ -21,21 +22,34 @@ type options struct {
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"` 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"` 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.)"` 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? 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"` Namespace string `short:"n" long:"namespace" description:"Limit operations to a specific namespace"`
} }
func main() { func main() {
opts := parseFlags() opts := parseFlags()
if opts.BindHost == "" {
host := os.Getenv("HD_BIND")
if host == "" {
host = "localhost"
}
opts.BindHost = host
}
setupLogging(opts.Verbose) setupLogging(opts.Verbose)
address, webServerDone := dashboard.StartServer(version, int(opts.Port), opts.Namespace, opts.Verbose, opts.NoTracking) 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 { if !opts.NoTracking {
log.Infof("User analytics collected to improve the quality, disable it with --no-analytics") log.Infof("User analytics is collected to improve the quality, disable it with --no-analytics")
} }
if opts.NoBrowser { if opts.NoBrowser {
@@ -53,7 +67,12 @@ func main() {
} }
func parseFlags() options { func parseFlags() options {
opts := options{} ns := os.Getenv("HELM_NAMESPACE")
if ns == "default" {
ns = ""
}
opts := options{Namespace: ns}
args, err := flags.Parse(&opts) args, err := flags.Parse(&opts)
if err != nil { if err != nil {
if e, ok := err.(*flags.Error); ok { if e, ok := err.(*flags.Error); ok {
@@ -67,7 +86,7 @@ func parseFlags() options {
} }
if opts.Version { if opts.Version {
fmt.Print(version) fmt.Println(version)
os.Exit(0) os.Exit(0)
} }

View File

@@ -2,14 +2,15 @@ package dashboard
import ( import (
"embed" "embed"
"net/http"
"os"
"path"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/komodorio/helm-dashboard/pkg/dashboard/handlers" "github.com/komodorio/helm-dashboard/pkg/dashboard/handlers"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc" "github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils" "github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http"
"os"
"path"
) )
//go:embed static/* //go:embed static/*
@@ -36,9 +37,16 @@ func errorHandler(c *gin.Context) {
func contextSetter(data *subproc.DataLayer) gin.HandlerFunc { func contextSetter(data *subproc.DataLayer) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
if context, ok := c.Request.Header["X-Kubecontext"]; ok { if ctx, ok := c.Request.Header["X-Kubecontext"]; ok {
log.Debugf("Setting current context to: %s", context) log.Debugf("Setting current context to: %s", ctx)
data.KubeContext = context[0] if data.KubeContext != ctx[0] {
err := data.Cache.Clear()
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}
}
data.KubeContext = ctx[0]
} }
c.Next() c.Next()
} }
@@ -72,7 +80,20 @@ func configureRoutes(abortWeb utils.ControlChan, data *subproc.DataLayer, api *g
api.GET("/status", func(c *gin.Context) { api.GET("/status", func(c *gin.Context) {
c.Header("X-Application-Name", "Helm Dashboard by Komodor.io") // to identify ourselves by ourselves c.Header("X-Application-Name", "Helm Dashboard by Komodor.io") // to identify ourselves by ourselves
c.IndentedJSON(http.StatusOK, data.VersionInfo) 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) configureHelms(api.Group("/api/helm"), data)
@@ -89,6 +110,7 @@ func configureHelms(api *gin.RouterGroup, data *subproc.DataLayer) {
api.GET("/charts/history", h.History) api.GET("/charts/history", h.History)
api.GET("/charts/resources", h.Resources) api.GET("/charts/resources", h.Resources)
api.GET("/charts/:section", h.GetInfoSection) api.GET("/charts/:section", h.GetInfoSection)
api.GET("/charts/show", h.Show)
api.POST("/charts/install", h.Install) api.POST("/charts/install", h.Install)
api.POST("/charts/rollback", h.Rollback) api.POST("/charts/rollback", h.Rollback)
@@ -106,6 +128,7 @@ func configureKubectls(api *gin.RouterGroup, data *subproc.DataLayer) {
api.GET("/contexts", h.GetContexts) api.GET("/contexts", h.GetContexts)
api.GET("/resources/:kind", h.GetResourceInfo) api.GET("/resources/:kind", h.GetResourceInfo)
api.GET("/describe/:kind", h.Describe) api.GET("/describe/:kind", h.Describe)
api.GET("/namespaces", h.GetNameSpaces)
} }
func configureStatic(api *gin.Engine) { func configureStatic(api *gin.Engine) {

View File

@@ -2,12 +2,13 @@ package handlers
import ( import (
"errors" "errors"
"github.com/gin-gonic/gin"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
"net/http" "net/http"
"strconv" "strconv"
"strings" "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 { type HelmHandler struct {
@@ -29,7 +30,7 @@ func (h *HelmHandler) Uninstall(c *gin.Context) {
_ = c.AbortWithError(http.StatusBadRequest, err) _ = c.AbortWithError(http.StatusBadRequest, err)
return return
} }
err = h.Data.ChartUninstall(qp.Namespace, qp.Name) err = h.Data.ReleaseUninstall(qp.Namespace, qp.Name)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -44,7 +45,7 @@ func (h *HelmHandler) Rollback(c *gin.Context) {
return return
} }
err = h.Data.Revert(qp.Namespace, qp.Name, qp.Revision) err = h.Data.Rollback(qp.Namespace, qp.Name, qp.Revision)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -59,7 +60,7 @@ func (h *HelmHandler) History(c *gin.Context) {
return return
} }
res, err := h.Data.ChartHistory(qp.Namespace, qp.Name) res, err := h.Data.ReleaseHistory(qp.Namespace, qp.Name)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -127,6 +128,22 @@ func (h *HelmHandler) RepoUpdate(c *gin.Context) {
c.Status(http.StatusNoContent) c.Status(http.StatusNoContent)
} }
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.IndentedJSON(http.StatusOK, res)
}
func (h *HelmHandler) Install(c *gin.Context) { func (h *HelmHandler) Install(c *gin.Context) {
qp, err := utils.GetQueryProps(c, false) qp, err := utils.GetQueryProps(c, false)
if err != nil { if err != nil {

View File

@@ -1,12 +1,13 @@
package handlers package handlers
import ( import (
"net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc" "github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils" "github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"net/http"
) )
type KubeHandler struct { type KubeHandler struct {
@@ -38,6 +39,7 @@ func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
return return
} }
// custom logic to provide most meaningful status for the resource
if res.Status.Phase == "Active" || res.Status.Phase == "Error" { if res.Status.Phase == "Active" || res.Status.Phase == "Error" {
_ = res.Name + "" _ = res.Name + ""
} else if res.Status.Phase == "" && len(res.Status.Conditions) > 0 { } else if res.Status.Phase == "" && len(res.Status.Conditions) > 0 {
@@ -69,3 +71,13 @@ func (h *KubeHandler) Describe(c *gin.Context) {
c.String(http.StatusOK, res) 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)
}

View File

@@ -12,11 +12,18 @@ type ScannersHandler struct {
} }
func (h *ScannersHandler) List(c *gin.Context) { func (h *ScannersHandler) List(c *gin.Context) {
res := make([]string, 0) type ScannerInfo struct {
for _, scanner := range h.Data.Scanners { SupportedResourceKinds []string
res = append(res, scanner.Name()) ManifestScannable bool
} }
c.JSON(http.StatusOK, res) 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) { func (h *ScannersHandler) ScanDraftManifest(c *gin.Context) {

View File

@@ -1,11 +1,12 @@
package scanners package scanners
import ( import (
"encoding/json"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc" "github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils" "github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
"github.com/olekukonko/tablewriter"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"strconv"
"strings" "strings"
) )
@@ -13,11 +14,46 @@ type Checkov struct {
Data *subproc.DataLayer 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 { func (c *Checkov) Name() string {
return "Checkov" return "Checkov"
} }
func (c *Checkov) Test() bool { func (c *Checkov) Test() bool {
utils.FailLogLevel = log.DebugLevel
defer func() { utils.FailLogLevel = log.WarnLevel }()
res, err := utils.RunCommand([]string{"checkov", "--version"}, nil) res, err := utils.RunCommand([]string{"checkov", "--version"}, nil)
if err != nil { if err != nil {
return false return false
@@ -33,7 +69,7 @@ func (c *Checkov) ScanManifests(mnf string) (*subproc.ScanResults, error) {
} }
defer fclose() defer fclose()
cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "cli", "--file", fname} cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "json", "--file", fname}
out, err := utils.RunCommand(cmd, nil) out, err := utils.RunCommand(cmd, nil)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -41,7 +77,10 @@ func (c *Checkov) ScanManifests(mnf string) (*subproc.ScanResults, error) {
res := &subproc.ScanResults{} res := &subproc.ScanResults{}
res.OrigReport = out err = json.Unmarshal([]byte(out), res.OrigReport)
if err != nil {
return nil, err
}
return res, nil return res, nil
} }
@@ -61,41 +100,46 @@ func (c *Checkov) ScanResource(ns string, kind string, name string) (*subproc.Sc
} }
defer fclose() defer fclose()
cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "cli", "--file", fname} cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "json", "--file", fname}
out, err := utils.RunCommand(cmd, nil) out, err := utils.RunCommand(cmd, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
res := subproc.ScanResults{} cr := CheckovReport{}
_, out, _ = strings.Cut(out, "\n") // kubernetes scan results: err = json.Unmarshal([]byte(out), &cr)
_, out, _ = strings.Cut(out, "\n") // empty line if err != nil {
line, out, found := strings.Cut(out, "\n") // status line return nil, err
if found {
parts := strings.FieldsFunc(line, func(r rune) bool {
return r == ':' || r == ','
})
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil {
res.PassedCount = cnt
} else {
log.Warnf("Failed to parse Checkov output: %s", err)
}
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[3])); err == nil {
res.FailedCount = cnt
} else {
log.Warnf("Failed to parse Checkov output: %s", err)
}
} else {
log.Warnf("Failed to parse Checkov output")
} }
res.OrigReport = strings.TrimSpace(out) res := &subproc.ScanResults{
PassedCount: cr.Summary.Passed,
FailedCount: cr.Summary.Failed,
OrigReport: checkovReportTable(&cr),
}
return &res, nil return res, nil
} }
type CheckovResults struct { func checkovReportTable(c *CheckovReport) string {
Summary CheckovSummary 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 { type CheckovSummary struct {
@@ -105,3 +149,16 @@ type CheckovSummary struct {
// parsing errors? // parsing errors?
// skipped ? // 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"`
}

View File

@@ -12,11 +12,31 @@ type Trivy struct {
Data *subproc.DataLayer 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 { func (c *Trivy) Name() string {
return "Trivy" return "Trivy"
} }
func (c *Trivy) Test() bool { func (c *Trivy) Test() bool {
utils.FailLogLevel = log.DebugLevel
defer func() { utils.FailLogLevel = log.WarnLevel }()
res, err := utils.RunCommand([]string{"trivy", "--version"}, nil) res, err := utils.RunCommand([]string{"trivy", "--version"}, nil)
if err != nil { if err != nil {
return false return false

View File

@@ -3,60 +3,68 @@ package dashboard
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
"github.com/komodorio/helm-dashboard/pkg/dashboard/scanners" "github.com/komodorio/helm-dashboard/pkg/dashboard/scanners"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc" "github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils" "github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http"
"os"
"strconv"
"strings"
"time"
) )
func StartServer(version string, port int, ns string, debug bool, noTracking bool) (string, utils.ControlChan) { type Server struct {
Version string
Namespace string
Address string
Debug bool
NoTracking bool
}
func (s Server) StartServer() (string, utils.ControlChan) {
data := subproc.DataLayer{ data := subproc.DataLayer{
Namespace: ns, Namespace: s.Namespace,
Cache: subproc.NewCache(),
} }
err := data.CheckConnectivity() err := data.CheckConnectivity()
if err != nil { if err != nil {
log.Errorf("Failed to check that Helm is operational, cannot continue. The error was: %s", err) log.Errorf("Failed to check that Helm is operational, cannot continue. The error was: %s", err)
os.Exit(1) // TODO: propagate error instead? os.Exit(1) // TODO: propagate error instead?
} }
isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true"
data.VersionInfo = &subproc.VersionInfo{ enableAnalytics := (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics
CurVer: version, data.StatusInfo = &subproc.StatusInfo{
Analytics: !noTracking, CurVer: s.Version,
Analytics: enableAnalytics,
LimitedToNamespace: s.Namespace,
} }
go checkUpgrade(data.VersionInfo) go checkUpgrade(data.StatusInfo)
discoverScanners(&data) discoverScanners(&data)
address := os.Getenv("HD_BIND")
if address == "" {
address = "localhost"
}
address += ":" + strconv.Itoa(port)
abort := make(utils.ControlChan) abort := make(utils.ControlChan)
api := NewRouter(abort, &data, debug) api := NewRouter(abort, &data, s.Debug)
done := startBackgroundServer(address, api, abort) done := s.startBackgroundServer(api, abort)
return "http://" + address, done return "http://" + s.Address, done
} }
func startBackgroundServer(addr string, routes *gin.Engine, abort utils.ControlChan) utils.ControlChan { func (s Server) startBackgroundServer(routes *gin.Engine, abort utils.ControlChan) utils.ControlChan {
done := make(utils.ControlChan) done := make(utils.ControlChan)
server := &http.Server{Addr: addr, Handler: routes} server := &http.Server{
Addr: s.Address,
Handler: routes,
}
go func() { go func() {
err := server.ListenAndServe() err := server.ListenAndServe()
if err != nil && err != http.ErrServerClosed { if err != nil && err != http.ErrServerClosed {
log.Warnf("Looks like port is busy for %s, checking if it's us...", addr) log.Warnf("Looks like port is busy for %s, checking if it's us...", s.Address)
if itIsUs(addr) { if s.itIsUs() {
log.Infof("Yes, it's another instance of us. Just reuse it.") log.Infof("Yes, it's another instance of us. Just reuse it.")
} else { } else {
panic(err) panic(err)
@@ -76,11 +84,14 @@ func startBackgroundServer(addr string, routes *gin.Engine, abort utils.ControlC
return done return done
} }
func itIsUs(addr string) bool { func (s Server) itIsUs() bool {
var myClient = &http.Client{Timeout: 5 * time.Second} url := fmt.Sprintf("http://%s/status", s.Address)
r, err := myClient.Get("http://" + addr + "/status") var myClient = &http.Client{
Timeout: 5 * time.Second,
}
r, err := myClient.Get(url)
if err != nil { if err != nil {
log.Debugf("It's not us on %s: %s", addr, err) log.Debugf("It's not us on %s: %s", s.Address, err)
return false return false
} }
defer r.Body.Close() defer r.Body.Close()
@@ -102,7 +113,7 @@ func discoverScanners(data *subproc.DataLayer) {
} }
} }
func checkUpgrade(d *subproc.VersionInfo) { func checkUpgrade(d *subproc.StatusInfo) {
url := "https://api.github.com/repos/komodorio/helm-dashboard/releases/latest" url := "https://api.github.com/repos/komodorio/helm-dashboard/releases/latest"
type GHRelease struct { type GHRelease struct {
Name string `json:"name"` Name string `json:"name"`
@@ -126,13 +137,13 @@ func checkUpgrade(d *subproc.VersionInfo) {
v1, err := version.NewVersion(d.CurVer) v1, err := version.NewVersion(d.CurVer)
if err != nil { if err != nil {
log.Warnf("Failed to parse version: %s", err) log.Warnf("Failed to parse CurVer: %s", err)
v1 = &version.Version{} v1 = &version.Version{}
} }
v2, err := version.NewVersion(d.LatestVer) v2, err := version.NewVersion(d.LatestVer)
if err != nil { if err != nil {
log.Warnf("Failed to parse version: %s", err) log.Warnf("Failed to parse LatestVer: %s", err)
} else { } else {
if v1.LessThan(v2) { if v1.LessThan(v2) {
log.Warnf("Newer Helm Dashboard version is available: %s", d.LatestVer) log.Warnf("Newer Helm Dashboard version is available: %s", d.LatestVer)

View File

@@ -62,13 +62,15 @@ function popUpUpgrade(elm, ns, name, verCur, lastRev) {
$("#upgradeModal .rel-cluster").text(getHashParam("context")) $("#upgradeModal .rel-cluster").text(getHashParam("context"))
if (verCur) { if (verCur) {
$("#upgradeModalLabel .type").text("Upgrade")
$("#upgradeModal .ver-old").show().find("span").text(verCur) $("#upgradeModal .ver-old").show().find("span").text(verCur)
$("#upgradeModal .rel-name").prop("disabled", true).val(name) $("#upgradeModal .rel-name").prop("disabled", true).val(name)
$("#upgradeModal .rel-ns").prop("disabled", true).val(ns) $("#upgradeModal .rel-ns").prop("disabled", true).val(ns)
} else { } else {
$("#upgradeModalLabel .type").text("Install")
$("#upgradeModal .ver-old").hide() $("#upgradeModal .ver-old").hide()
$("#upgradeModal .rel-name").prop("disabled", false).val(elm.name.split("/").pop()) $("#upgradeModal .rel-name").prop("disabled", false).val(elm.name.split("/").pop())
$("#upgradeModal .rel-ns").prop("disabled", false).val("") $("#upgradeModal .rel-ns").prop("disabled", false).val(ns)
} }
$.getJSON("/api/helm/repo/search?name=" + elm.name).fail(function (xhr) { $.getJSON("/api/helm/repo/search?name=" + elm.name).fail(function (xhr) {
@@ -117,7 +119,7 @@ $("#upgradeModal .btn-confirm").click(function () {
if (data.version) { if (data.version) {
setHashParam("section", null) setHashParam("section", null)
const ns = $("#upgradeModal .rel-ns").val(); const ns = $("#upgradeModal .rel-ns").val();
setHashParam("namespace", ns ? ns : "default") setHashParam("namespace", ns ? ns : "default") // TODO: relaets issue #51
setHashParam("chart", $("#upgradeModal .rel-name").val()) setHashParam("chart", $("#upgradeModal .rel-name").val())
setHashParam("revision", data.version) setHashParam("revision", data.version)
window.location.reload() window.location.reload()

View File

@@ -2,9 +2,10 @@ const xhr = new XMLHttpRequest();
xhr.onload = function () { xhr.onload = function () {
if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.readyState === XMLHttpRequest.DONE) {
const status = JSON.parse(xhr.responseText); const status = JSON.parse(xhr.responseText);
if (status.Analytics && status.version !== "dev") { const version = status.CurVer
enableDD(status.version) if (status.Analytics) {
enableHeap() enableDD(version)
enableHeap(version)
} }
} }
} }
@@ -41,7 +42,7 @@ function enableDD(version) {
}) })
} }
function enableHeap() { function enableHeap(version) {
window.heap = window.heap || [], heap.load = function (e, t) { window.heap = window.heap || [], heap.load = function (e, t) {
window.heap.appid = e, window.heap.config = t = t || {}; window.heap.appid = e, window.heap.config = t = t || {};
let r = document.createElement("script"); let r = document.createElement("script");
@@ -54,5 +55,12 @@ function enableHeap() {
} }
}, p = ["addEventProperties", "addUserProperties", "clearEventProperties", "identify", "resetIdentity", "removeEventProperty", "setEventProperties", "track", "unsetEventProperty"], o = 0; o < p.length; o++) heap[p[o]] = n(p[o]) }, 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"); heap.load("4249623943");
window.heap.addEventProperties({'version': version});
}
function sendStats(name, prop){
if (window.heap) {
window.heap.track(name, prop);
}
} }

View File

@@ -143,6 +143,7 @@ $("#nav-tab [data-tab]").click(function () {
function showResources(namespace, chart, revision) { function showResources(namespace, chart, revision) {
const resBody = $("#nav-resources .body"); 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>'); 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 qstr = "name=" + chart + "&namespace=" + namespace + "&revision=" + revision
let url = "/api/helm/charts/resources" let url = "/api/helm/charts/resources"
@@ -150,7 +151,14 @@ function showResources(namespace, chart, revision) {
$.getJSON(url).fail(function (xhr) { $.getJSON(url).fail(function (xhr) {
reportError("Failed to get list of resources", xhr) reportError("Failed to get list of resources", xhr)
}).done(function (data) { }).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(); 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++) { for (let i = 0; i < data.length; i++) {
const res = data[i] const res = data[i]
const resBlock = $(` const resBlock = $(`
@@ -159,7 +167,7 @@ function showResources(namespace, chart, revision) {
<div class="col-3 res-name text-break fw-bold"></div> <div class="col-3 res-name text-break fw-bold"></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-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-4 res-statusmsg"><span class="text-muted small">Getting status...</span></div>
<div class="col-2 res-actions"></div> <div class="col-2 res-actions"><button class='btn btn-sm ms-2 visually-hidden'>Vertical-sizer</button></div>
</div> </div>
`) `)
@@ -195,12 +203,14 @@ function showResources(namespace, chart, revision) {
showDescribe(ns, res.kind, res.metadata.name, badge.clone()) 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>"); const btn2 = $("<button class='btn btn-sm btn-white border-secondary ms-2'>Scan</button>");
resBlock.find(".res-actions").append(btn2) resBlock.find(".res-actions").append(btn2)
btn2.click(function () { btn2.click(function () {
scanResource(ns, res.kind, res.metadata.name, badge.clone()) scanResource(ns, res.kind, res.metadata.name, badge.clone())
}) })
} }
}
}) })
} }
}) })
@@ -237,25 +247,36 @@ function scanResource(ns, kind, name, badge) {
body.append("No information from scanners. Make sure you have installed some and scanned object is supported.") 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) { for (let name in data) {
const res = data[name] const res = data[name]
if (!res.OrigReport) continue if (!res.OrigReport && !res.PassedCount) continue
const hdr = $("<h3>" + name + " Scan Results</h3>");
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) { if (res.FailedCount) {
hdr.append("<span class='badge bg-danger ms-3'>" + res.FailedCount + " failed</span>") hdr.find('button').append("<span class='badge bg-danger ms-2'>" + res.FailedCount + " failed</span>")
} }
if (res.PassedCount) { if (res.PassedCount) {
hdr.append("<span class='badge bg-info ms-3'>" + res.PassedCount + " passed</span>") hdr.find('button').append("<span class='badge bg-info ms-2'>" + res.PassedCount + " passed</span>")
} }
body.append(hdr)
const hl = hljs.highlight(res.OrigReport, {language: 'yaml'}).value 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 pre = $("<pre class='bg-white rounded p-3' style='font-size: inherit; overflow: unset'></pre>").html(hl)
body.append(pre) 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()
}) })
} }

View File

@@ -49,6 +49,15 @@
<li> <li>
<hr class="dropdown-divider"> <hr class="dropdown-divider">
</li> </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> <li><a class="dropdown-item disabled" href="#">Version <span id="toolVersion"></span></a></li>
</ul> </ul>
</li> </li>
@@ -90,18 +99,17 @@
<button class="btn btn-sm btn-light bg-white border border-secondary btn-remove"> <button class="btn btn-sm btn-light bg-white border border-secondary btn-remove">
<i class="bi-trash3"></i> Remove <i class="bi-trash3"></i> Remove
</button> </button>
<p class="my-3"><input class="form-control form-control-sm" type="text" placeholder="Filter..."
id="inputSearch"></p>
</div> </div>
<div><span class="text-muted small fw-bold me-3">REPOSITORY</span></div> <div><span class="text-muted small fw-bold me-3">REPOSITORY</span></div>
<h2 class="mb-3">name-of-repo</h2> <h2 class="mb-3">name-of-repo</h2>
<div class="mb-5"> <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> <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>
<div class="py-2 mb-3 float-end">
<!-- TODO
<div class="float-end">
<input class="form-control form-control-sm" type="text" placeholder="Filter...">
</div> </div>
-->
<div class="row bg-secondary rounded px-3 py-2 mb-3 fw-bold small" <div class="row bg-secondary rounded px-3 py-2 mb-3 fw-bold small"
style="text-transform: uppercase"> style="text-transform: uppercase">
<div class="col-3">Chart Name</div> <div class="col-3">Chart Name</div>
@@ -109,7 +117,7 @@
<div class="col-1">Version</div> <div class="col-1">Version</div>
<div class="col-1"></div> <div class="col-1"></div>
</div> </div>
<ul class="list-unstyled mt-4"></ul> <ul class="list-unstyled mt-4 charts"></ul>
</div> </div>
</div> </div>
@@ -122,11 +130,10 @@
<ul class="list-unstyled" id="cluster"> <ul class="list-unstyled" id="cluster">
</ul> </ul>
<!-- TODO
<h4 class="mt-4">Namespaces</h4> <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> </ul>
-->
</form> </form>
</div> </div>
<!-- /FILTER BLOCK --> <!-- /FILTER BLOCK -->
@@ -136,9 +143,12 @@
<!-- INSTALLED LIST --> <!-- INSTALLED LIST -->
<div class="col ms-2" id="installedList"> <div class="col ms-2" id="installedList">
<div class="col rounded rounded-1 b-shadow header"> <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 <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> 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>
<div class="bg-secondary rounded-bottom m-0 row p-2"> <div class="bg-secondary rounded-bottom m-0 row p-2">
<div class="col-4 hdr-name">Name</div> <div class="col-4 hdr-name">Name</div>
@@ -154,6 +164,8 @@
<div class="bg-white rounded shadow p-3 display-none no-charts">Looks like you don't have any charts <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. installed. "Repository" section may be a good place to start.
</div> </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> </div>
<!-- /INSTALLED LIST --> <!-- /INSTALLED LIST -->
</div> </div>
@@ -343,7 +355,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title" id="upgradeModalLabel"> <h4 class="modal-title" id="upgradeModalLabel">
Install <b class='text-success name'></b> <span class="type"></span> <b class='text-success name'></b>
</h4> </h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
@@ -358,7 +370,9 @@
Release Name: <input class="form-control rel-name"> Release Name: <input class="form-control rel-name">
</label> </label>
<label class="form-label me-4 text-dark"> <label class="form-label me-4 text-dark">
Namespace (optional): <input class="form-control rel-ns"> Namespace (optional):
<input type="text" class="form-control rel-ns" list="ns-datalist"/>
<datalist id="ns-datalist"></datalist>
</label> </label>
<label class="form-label me-4 text-dark"> <label class="form-label me-4 text-dark">
Cluster: <span class="form-label rel-cluster"></span> Cluster: <span class="form-label rel-cluster"></span>
@@ -391,7 +405,8 @@
<div id="upgradeModalBody" class="small"></div> <div id="upgradeModalBody" class="small"></div>
</form> </form>
<div class="modal-footer d-flex"> <div class="modal-footer d-flex">
<button type="button" class="btn btn-scan bg-white border-secondary">Scan for Problems</button> <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> <button type="button" class="btn btn-primary btn-confirm">Confirm</button>
</div> </div>
</div> </div>

View File

@@ -4,15 +4,30 @@ function loadChartsList() {
const chartsCards = $("#installedList .body") const chartsCards = $("#installedList .body")
chartsCards.empty().append("<div><span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span> Loading...</div>") 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) { $.getJSON("/api/helm/charts").fail(function (xhr) {
sendStats('Get releases', {'status': 'failed'});
reportError("Failed to get list of charts", xhr) reportError("Failed to get list of charts", xhr)
}).done(function (data) { }).done(function (data) {
chartsCards.empty() chartsCards.empty().hide()
$("#installedList .header h2 span").text(data.length) $("#installedList .header h2 span").text(data.length)
const usedNS = {}
data.forEach(function (elm) { data.forEach(function (elm) {
let card = buildChartCard(elm); let card = buildChartCard(elm);
chartsCards.append(card) 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) { if (!data.length) {
$("#installedList .no-charts").show() $("#installedList .no-charts").show()
} }
@@ -20,8 +35,8 @@ function loadChartsList() {
} }
function buildChartCard(elm) { function buildChartCard(elm) {
const card = $(`<div class="row m-0 py-3 bg-white rounded-1 b-shadow border-4 border-start"> 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 class="link">release-name</span><div></div></div> <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-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-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> <div class="col-1 rel-rev"><span>#0</span><div>Revision</div></div>
@@ -29,17 +44,49 @@ function buildChartCard(elm) {
<div class="col-1 rel-date text-nowrap"><span>today</span><div>Updated</div></div> <div class="col-1 rel-date text-nowrap"><span>today</span><div>Updated</div></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-name span").text(elm.name)
card.find(".rel-rev span").text("#" + elm.revision) card.find(".rel-rev span").text("#" + elm.revision)
card.find(".rel-ns span").text(elm.namespace) card.find(".rel-ns span").text(elm.namespace)
card.find(".rel-chart span").text(elm.chart) card.find(".rel-chart span").text(elm.chart)
card.find(".rel-date span").text(getAge(elm)) 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")) 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("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) const self = $(this)
$("#sectionList").hide() $("#sectionList").hide()
@@ -52,3 +99,6 @@ function buildChartCard(elm) {
return card; return card;
} }
$("#installedSearch").keyup(function () {
filterInstalledList($("#installedList .body .row"))
})

View File

@@ -4,8 +4,10 @@ function loadRepoView() {
$.getJSON("/api/helm/repo").fail(function (xhr) { $.getJSON("/api/helm/repo").fail(function (xhr) {
reportError("Failed to get list of repositories", xhr) reportError("Failed to get list of repositories", xhr)
sendStats('Get repo', {'status': 'fail'});
}).done(function (data) { }).done(function (data) {
const items = $("#sectionRepo .repo-list ul").empty() const items = $("#sectionRepo .repo-list ul").empty()
data.sort((a, b) => (a.name > b.name) - (a.name < b.name))
data.forEach(function (elm) { data.forEach(function (elm) {
let opt = $('<li class="mb-2"><label><input type="radio" name="cluster" class="me-2"/><span></span></label></li>'); let opt = $('<li class="mb-2"><label><input type="radio" name="cluster" class="me-2"/><span></span></label></li>');
@@ -18,8 +20,9 @@ function loadRepoView() {
if (!data.length) { if (!data.length) {
items.text("No repositories found, try adding one") items.text("No repositories found, try adding one")
} }
sendStats('Get repo', {'status': 'success', length:data.length});
items.find("input").click(function () { items.find("input").click(function () {
$("#inputSearch").val('')
const self = $(this) const self = $(this)
const elm = self.data("item"); const elm = self.data("item");
setHashParam("repo", elm.name) setHashParam("repo", elm.name)
@@ -61,6 +64,19 @@ function loadRepoView() {
}) })
} }
$("#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 () { $("#sectionRepo .repo-list .btn").click(function () {
const myModal = new bootstrap.Modal(document.getElementById('repoAddModal'), {}); const myModal = new bootstrap.Modal(document.getElementById('repoAddModal'), {});
myModal.show() myModal.show()
@@ -115,6 +131,8 @@ function repoChartClicked() {
setHashParam("chart", elm.installed_name) setHashParam("chart", elm.installed_name)
window.location.reload() window.location.reload()
} else { } else {
popUpUpgrade(elm) const contexts = $("body").data("contexts")
const contextNamespace = contexts.filter(obj => {return obj.Name === getHashParam("context")})[0].Namespace
popUpUpgrade(elm, contextNamespace)
} }
} }

View File

@@ -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) $("#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-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-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> <div class="rev-changes position-absolute bottom-0 start-0 m-2 text-muted small"></div>
@@ -61,9 +61,13 @@ function fillChartHistory(data, namespace, name) {
rev.data("elm", elm) rev.data("elm", elm)
rev.addClass("rev-" + elm.revision) rev.addClass("rev-" + elm.revision)
rev.click(function () { rev.click(function () {
if (window.getSelection().toString()) {
return
}
revisionClicked(namespace, name, $(this)) revisionClicked(namespace, name, $(this))
}) })
// revRow.attr("class", "link")
revRow.append(rev) revRow.append(rev)
} }
} }

View File

@@ -1,34 +1,72 @@
$(function () { $(function () {
const clusterSelect = $("#cluster"); let limNS = null
clusterSelect.change(function () { $.getJSON("/status").fail(function (xhr) { // maybe /options call in the future
window.location.href = "/#context=" + clusterSelect.find("input:radio:checked").val() reportError("Failed to get tool version", xhr)
window.location.reload()
})
$.getJSON("/api/kube/contexts").fail(function (xhr) {
reportError("Failed to get list of clusters", xhr)
}).done(function (data) { }).done(function (data) {
const context = getHashParam("context") fillToolVersion(data)
fillClusterList(data, context); limNS = data.LimitedToNamespace
if (limNS) {
initView(); // can only do it after loading cluster list $("#limitNamespace").show().find("span").text(limNS)
}
fillClusters(limNS)
}) })
$.getJSON("/api/scanners").fail(function (xhr) { $.getJSON("/api/scanners").fail(function (xhr) {
reportError("Failed to get list of scanners", xhr) reportError("Failed to get list of scanners", xhr)
}).done(function (data) { }).done(function (data) {
if (!data || !data.length) { $("body").data("scanners", data)
$("#upgradeModal .btn-scan").hide() for (let k in data) {
if (data[k].ManifestScannable) {
$("#upgradeModal .btn-scan").show() // TODO: move this to install flow
}
} }
}) })
$.getJSON("/status").fail(function (xhr) {
reportError("Failed to get tool version", xhr)
}).done(function (data) {
fillToolVersion(data)
})
}) })
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() { function initView() {
$(".section").hide() $(".section").hide()
@@ -57,8 +95,10 @@ $("#topNav ul a").click(function () {
$("#topNav ul a").removeClass("active") $("#topNav ul a").removeClass("active")
const ctx = getHashParam("context") const ctx = getHashParam("context")
const filteredNamespace = getHashParam("filteredNamespace")
setHashParam(null, null) setHashParam(null, null)
setHashParam("context", ctx) setHashParam("context", ctx)
setHashParam("filteredNamespace", filteredNamespace)
if (self.hasClass("section-repo")) { if (self.hasClass("section-repo")) {
setHashParam("section", "repository") setHashParam("section", "repository")
@@ -127,28 +167,33 @@ function getCleanClusterName(rawClusterName) {
if (rawClusterName.indexOf('arn') === 0) { if (rawClusterName.indexOf('arn') === 0) {
// AWS cluster // AWS cluster
const clusterSplit = rawClusterName.split(':') const clusterSplit = rawClusterName.split(':')
const clusterName = clusterSplit.at(-1).split("/").at(-1) const clusterName = clusterSplit.slice(-1)[0].replace('cluster/', '')
const region = clusterSplit.at(-3) const region = clusterSplit.at(-3)
return region + "/" + clusterName + ' [AWS]' return region + "/" + clusterName + ' [AWS]'
} }
if (rawClusterName.indexOf('gke') === 0) { if (rawClusterName.indexOf('gke') === 0) {
// GKE cluster // GKE cluster
return rawClusterName.split('_').at(-2) + '/' + rawClusterName.split('_').at(-1) + ' [GKE]' return rawClusterName.split('_').at(-2) + '/' + rawClusterName.split('_').at(-1) + ' [GKE]'
} }
return rawClusterName return rawClusterName
} }
function fillClusterList(data, context) { function fillClusterList(data, context) {
if (!data || !data.length) {
$("#cluster").append("No clusters listed in kubectl config, please configure some")
return
}
data.forEach(function (elm) { data.forEach(function (elm) {
let label = getCleanClusterName(elm.Name) let label = getCleanClusterName(elm.Name)
let opt = $('<li><label><input type="radio" name="cluster" class="me-2"/><span></span></label></li>'); let opt = $('<li><label><input type="radio" name="cluster" class="me-2"/><span></span></label></li>');
opt.attr('title', elm.Name) opt.attr('title', elm.Name)
opt.find("input").val(elm.Name).text(label) opt.find("input").val(elm.Name).text(label)
opt.find("span").text(label) opt.find("span").text(label)
if (elm.IsCurrent && !context) { const isCurrent = elm.IsCurrent && !context;
opt.find("input").prop("checked", true) const isSelected = context && elm.Name === context
setCurrentContext(elm.Name) if (isCurrent || isSelected) {
} else if (context && elm.Name === context) {
opt.find("input").prop("checked", true) opt.find("input").prop("checked", true)
setCurrentContext(elm.Name) setCurrentContext(elm.Name)
} }
@@ -156,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) { function setCurrentContext(ctx) {
setHashParam("context", ctx) setHashParam("context", ctx)
$.ajaxSetup({ $.ajaxSetup({
@@ -192,6 +264,7 @@ $(".bi-power").click(function () {
url: "/", url: "/",
type: 'DELETE', type: 'DELETE',
}).done(function () { }).done(function () {
// TODO: display explanation overlay here
window.close(); window.close();
}) })
}) })
@@ -223,3 +296,51 @@ function fillToolVersion(data) {
$(".upgrade-possible").show() $(".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('+'))
}
}

View File

@@ -26,10 +26,6 @@ body > .container-fluid {
min-height: 100% !important; min-height: 100% !important;
} }
#topNav.navbar {
}
.navbar-brand > a > img { .navbar-brand > a > img {
vertical-align: middle; vertical-align: middle;
height: 3rem; height: 3rem;
@@ -107,6 +103,13 @@ body > .container-fluid {
margin-bottom: 0.75rem !important; margin-bottom: 0.75rem !important;
} }
#installedList .header .spaced-out {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 0.5rem;
}
#installedList h2 { #installedList h2 {
font-family: Inter, serif; font-family: Inter, serif;
font-weight: 700; font-weight: 700;
@@ -166,6 +169,10 @@ span.link {
background-size: 3rem; background-size: 3rem;
} }
#installedList .rel-name div {
overflow: hidden;
}
#installedList .rel-name span { #installedList .rel-name span {
font-family: Roboto Slab, sans-serif; font-family: Roboto Slab, sans-serif;
font-weight: 700; font-weight: 700;
@@ -211,23 +218,23 @@ span.link {
position: static; position: static;
} }
.nav-tabs { nav .nav-tabs {
border: none; border: none;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.nav-tabs .nav-link { nav .nav-tabs .nav-link {
padding-bottom: 0.25rem; padding-bottom: 0.25rem;
color: #3B3D45; color: #3B3D45;
} }
.nav-tabs .nav-link.active { nav .nav-tabs .nav-link.active {
border: none; border: none;
border-bottom: 3px solid #3B3D45; border-bottom: 3px solid #3B3D45;
background-color: transparent; background-color: transparent;
} }
#installedList .b-shadow:hover { #installedList .body .b-shadow:hover {
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.18); box-shadow: 0 3px 15px rgba(0, 0, 0, 0.18);
} }

View 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
}

View File

@@ -5,6 +5,10 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"strconv"
"strings"
"time"
"github.com/hexops/gotextdiff" "github.com/hexops/gotextdiff"
"github.com/hexops/gotextdiff/myers" "github.com/hexops/gotextdiff/myers"
"github.com/hexops/gotextdiff/span" "github.com/hexops/gotextdiff/span"
@@ -13,11 +17,6 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"regexp"
"sort"
"strconv"
"strings"
"time"
) )
type DataLayer struct { type DataLayer struct {
@@ -25,18 +24,22 @@ type DataLayer struct {
Helm string Helm string
Kubectl string Kubectl string
Scanners []Scanner Scanners []Scanner
VersionInfo *VersionInfo StatusInfo *StatusInfo
Namespace string Namespace string
Cache *Cache
} }
type VersionInfo struct { type StatusInfo struct {
CurVer string CurVer string
LatestVer string LatestVer string
Analytics bool Analytics bool
LimitedToNamespace string
CacheHitRatio float64
} }
func (d *DataLayer) runCommand(cmd ...string) (string, error) { func (d *DataLayer) runCommand(cmd ...string) (string, error) {
for i, c := range cmd { 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? if c == "--namespace" && i < len(cmd) { // TODO: in case it's not found - add it?
d.forceNamespace(&cmd[i+1]) d.forceNamespace(&cmd[i+1])
} }
@@ -58,20 +61,6 @@ func (d *DataLayer) runCommandHelm(cmd ...string) (string, error) {
return d.runCommand(cmd...) return d.runCommand(cmd...)
} }
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...)
}
func (d *DataLayer) forceNamespace(s *string) { func (d *DataLayer) forceNamespace(s *string) {
if d.Namespace != "" { if d.Namespace != "" {
*s = d.Namespace *s = d.Namespace
@@ -96,46 +85,14 @@ func (d *DataLayer) CheckConnectivity() error {
return nil return nil
} }
type KubeContext struct { func (d *DataLayer) GetStatus() *StatusInfo {
IsCurrent bool sum := float64(d.Cache.HitCount + d.Cache.MissCount)
Name string if sum > 0 {
Cluster string d.StatusInfo.CacheHitRatio = float64(d.Cache.HitCount) / sum
AuthInfo string } else {
Namespace string d.StatusInfo.CacheHitRatio = 0
}
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
out, err := d.runCommandKubectl("config", "get-contexts")
if err != nil {
return nil, err
} }
return d.StatusInfo
// 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) { func (d *DataLayer) ListInstalled() (res []ReleaseElement, err error) {
@@ -148,7 +105,9 @@ func (d *DataLayer) ListInstalled() (res []ReleaseElement, err error) {
cmd = append(cmd, "--namespace", d.Namespace) cmd = append(cmd, "--namespace", d.Namespace)
} }
out, err := d.runCommandHelm(cmd...) out, err := d.Cache.String(CacheKeyRelList, nil, func() (string, error) {
return d.runCommandHelm(cmd...)
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -160,9 +119,13 @@ func (d *DataLayer) ListInstalled() (res []ReleaseElement, err error) {
return res, nil return res, nil
} }
func (d *DataLayer) ChartHistory(namespace string, chartName string) (res []*HistoryElement, err error) { func (d *DataLayer) ReleaseHistory(namespace string, releaseName string) (res []*HistoryElement, err error) {
// TODO: there is `max` but there is no `offset` // TODO: there is `max` but there is no `offset`
out, err := d.runCommandHelm("history", chartName, "--namespace", namespace, "--output", "json", "--max", "18") 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 { if err != nil {
return nil, err return nil, err
} }
@@ -177,7 +140,7 @@ func (d *DataLayer) ChartHistory(namespace string, chartName string) (res []*His
if err != nil { if err != nil {
return nil, err return nil, err
} }
elm.ChartName = chartRepoName elm.ChartName = chartRepoName // TODO: move it to frontend?
elm.ChartVer = curVer elm.ChartVer = curVer
elm.Updated.Time = elm.Updated.Time.Round(time.Second) elm.Updated.Time = elm.Updated.Time.Round(time.Second)
} }
@@ -185,79 +148,15 @@ func (d *DataLayer) ChartHistory(namespace string, chartName string) (res []*His
return res, nil return res, nil
} }
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.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.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 _, chart := 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(chart.Name, "/")
if pieces[1] == c {
// TODO: there can be more than one
chart.InstalledNamespace = rel.Namespace
chart.InstalledName = rel.Name
}
}
}
}
type SectionFn = func(string, string, int, bool) (string, error) // TODO: rework it into struct-based argument? 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) { func (d *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
cmd := []string{"get", "manifest", chartName, "--namespace", namespace} cmd := []string{"get", "manifest", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision)}
if revision > 0 {
cmd = append(cmd, "--revision", strconv.Itoa(revision))
}
out, err := d.runCommandHelm(cmd...) key := CacheKeyRevManifests + "\v" + namespace + "\v" + chartName + "\v" + strconv.Itoa(revision)
if err != nil { return d.Cache.String(key, nil, func() (string, error) {
return "", err return d.runCommandHelm(cmd...)
} })
return out, nil
} }
func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string, revision int) ([]*v1.Carp, error) { func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string, revision int) ([]*v1.Carp, error) {
@@ -272,7 +171,7 @@ func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string,
var tmp interface{} var tmp interface{}
for dec.Decode(&tmp) == nil { for dec.Decode(&tmp) == nil {
// k8s libs uses only JSON tags defined, say hello to https://github.com/go-yaml/yaml/issues/424 // k8s libs uses only JSON tags defined, say hello to https://github.com/go-yaml/yaml/issues/424
// bug we can juggle it // we can juggle it
jsoned, err := json.Marshal(tmp) jsoned, err := json.Marshal(tmp)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -296,115 +195,38 @@ func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string,
} }
func (d *DataLayer) RevisionNotes(namespace string, chartName string, revision int, _ bool) (res string, err error) { 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)) cmd := []string{"get", "notes", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision)}
if err != nil { key := CacheKeyRevNotes + "\v" + namespace + "\v" + chartName + "\v" + strconv.Itoa(revision)
return "", err return d.Cache.String(key, nil, func() (string, error) {
} return d.runCommandHelm(cmd...)
return out, nil })
} }
func (d *DataLayer) RevisionValues(namespace string, chartName string, revision int, onlyUserDefined bool) (res string, err error) { 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"} cmd := []string{"get", "values", chartName, "--namespace", namespace, "--output", "yaml", "--revision", strconv.Itoa(revision)}
if revision > 0 {
cmd = append(cmd, "--revision", strconv.Itoa(revision))
}
if !onlyUserDefined { if !onlyUserDefined {
cmd = append(cmd, "--all") cmd = append(cmd, "--all")
} }
out, err := d.runCommandHelm(cmd...)
if err != nil {
return "", err
}
return out, nil
}
func (d *DataLayer) GetResource(namespace string, def *v1.Carp) (*v1.Carp, error) { key := CacheKeyRevValues + "\v" + namespace + "\v" + chartName + "\v" + strconv.Itoa(revision) + "\v" + fmt.Sprintf("%v", onlyUserDefined)
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "json") return d.Cache.String(key, nil, func() (string, error) {
if err != nil { return d.runCommandHelm(cmd...)
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) { func (d *DataLayer) ReleaseUninstall(namespace string, name string) error {
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "yaml") d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
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
}
func (d *DataLayer) ChartUninstall(namespace string, name string) error {
_, err := d.runCommandHelm("uninstall", name, "--namespace", namespace) _, err := d.runCommandHelm("uninstall", name, "--namespace", namespace)
if err != nil {
return err return err
}
return nil
} }
func (d *DataLayer) Revert(namespace string, name string, rev int) error { 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) _, err := d.runCommandHelm("rollback", name, strconv.Itoa(rev), "--namespace", namespace)
if err != nil {
return err 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) ChartInstall(namespace string, name string, repoChart string, version string, justTemplate bool, values string, reuseVals bool) (string, error) { func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string, version string, justTemplate bool, values string, reuseVals bool) (string, error) {
@@ -431,6 +253,11 @@ func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string
if err != nil { if err != nil {
return "", err return "", err
} }
if !justTemplate {
d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
}
res := release.Release{} res := release.Release{}
err = json.Unmarshal([]byte(out), &res) err = json.Unmarshal([]byte(out), &res)
if err != nil { if err != nil {
@@ -443,41 +270,6 @@ func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string
return out, nil return out, nil
} }
func (d *DataLayer) ShowValues(chart string, ver string) (string, error) {
return d.runCommandHelm("show", "values", chart, "--version", ver)
}
func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) {
out, err := 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) {
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) {
out, err := d.runCommandHelm("repo", "remove", name)
if err != nil {
return "", err
}
return out, nil
}
func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) { func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) {
if revision1 == 0 || revision2 == 0 { if revision1 == 0 || revision2 == 0 {
log.Debugf("One of revisions is zero: %d %d", revision1, revision2) log.Debugf("One of revisions is zero: %d %d", revision1, revision2)

View File

@@ -13,7 +13,9 @@ func TestFlow(t *testing.T) {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
var _ release.Status var _ release.Status
data := DataLayer{} data := DataLayer{
Cache: NewCache(),
}
err := data.CheckConnectivity() err := data.CheckConnectivity()
if err != nil { if err != nil {
if err.Error() == "did not find any kubectl contexts configured" { if err.Error() == "did not find any kubectl contexts configured" {
@@ -40,7 +42,7 @@ func TestFlow(t *testing.T) {
} }
chart := installed[1] chart := installed[1]
history, err := data.ChartHistory(chart.Namespace, chart.Name) history, err := data.ReleaseHistory(chart.Namespace, chart.Name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -24,8 +24,9 @@ type HistoryElement struct {
Chart string `json:"chart"` Chart string `json:"chart"`
AppVersion string `json:"app_version"` AppVersion string `json:"app_version"`
Description string `json:"description"` 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 {

View 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
}

View 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
}

View File

@@ -5,6 +5,8 @@ type Scanner interface {
Test() bool // test if the scanner is available Test() bool // test if the scanner is available
ScanManifests(mnf string) (*ScanResults, error) // run the scanner on manifests 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 ScanResource(ns string, kind string, name string) (*ScanResults, error) // run the scanner on k8s resource
SupportedResourceKinds() []string
ManifestIsScannable() bool
} }
type ScanResults struct { type ScanResults struct {

View File

@@ -3,24 +3,35 @@ package utils
import ( import (
"bytes" "bytes"
"errors" "errors"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"regexp"
"strconv" "strconv"
"strings" "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{} type ControlChan = chan struct{}
func ChartAndVersion(x string) (string, string, error) { func ChartAndVersion(x string) (string, string, error) {
lastInd := strings.LastIndex(x, "-") strs := strings.Split(x, "-")
if lastInd < 0 { lens := len(strs)
if lens < 2 {
return "", "", errors.New("can't parse chart version string") 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
} }
return x[:lastInd], x[lastInd+1:], nil
} }
func TempFile(txt string) (string, func(), error) { func TempFile(txt string) (string, func(), error) {
@@ -64,10 +75,10 @@ func RunCommand(cmd []string, env map[string]string) (string, error) {
prog.Stderr = &stderr prog.Stderr = &stderr
if err := prog.Run(); err != nil { if err := prog.Run(); err != nil {
log.Warnf("Failed command: %s", cmd) log.StandardLogger().Logf(FailLogLevel, "Failed command: %s", cmd)
serr := stderr.Bytes() serr := stderr.Bytes()
if serr != nil { if serr != nil {
log.Warnf("STDERR:\n%s", serr) log.StandardLogger().Logf(FailLogLevel, "STDERR:\n%s", serr)
} }
if eerr, ok := err.(*exec.ExitError); ok { if eerr, ok := err.(*exec.ExitError); ok {
return "", CmdError{ return "", CmdError{

View File

@@ -1,9 +1,10 @@
package utils package utils
import ( import (
"github.com/gin-gonic/gin"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/gin-gonic/gin"
) )
func TestGetQueryProps(t *testing.T) { func TestGetQueryProps(t *testing.T) {
@@ -69,6 +70,27 @@ func TestChartAndVersion(t *testing.T) {
wantVer: "1.0.0", wantVer: "1.0.0",
wantError: false, 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", name: "Chart and version - parsing chart without version",
params: "chart", params: "chart",

View File

@@ -1,8 +1,9 @@
name: "dashboard" name: "dashboard"
version: "0.2.4" version: "0.2.8"
usage: "A simplified way of working with Helm" usage: "A simplified way of working with Helm"
description: "View HELM situation in nice web UI" description: "View HELM situation in nice web UI"
command: "$HELM_PLUGIN_DIR/bin/helm-dashboard" command: "$HELM_PLUGIN_DIR/bin/helm-dashboard"
ignoreFlags: false
hooks: hooks:
install: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh" install: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh"
update: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh" update: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh"