47 Commits

Author SHA1 Message Date
Andrei Pohilko
a2ddb94c16 Merge branch 'main' of github.com:komodorio/helm-dashboard 2023-04-11 12:15:27 +01:00
Andrei Pohilko
861de33bfe Record event when error is shown 2023-04-11 12:15:09 +01:00
Andrei Pohilko
26d82dd5ab Omit storageClassName if it is null
Fixes #226
2023-04-11 12:10:30 +01:00
Andrei Pohilko
b1294cbe1a Make failed manifest parse be shown less breaking
Relates to #328
2023-04-11 12:09:12 +01:00
dependabot[bot]
d4583a222e Bump github.com/docker/docker (#317)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.21+incompatible to 20.10.24+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v20.10.21...v20.10.24)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-11 09:37:34 +01:00
Satyam Singh
a0bf59edc6 Added Link to the v1.3.0 badge (#319)
* Added Link to the v1.3.0 badge

Added link of Release v1.3.0 to the badge of v1.3.0 and it now redirects to the v1.3.0

* Added link to the release badge 

Added link to the release badge which will now redirect to the latest release
2023-04-09 16:30:07 +01:00
komodor-bot
79a79979e2 Increment chart versions [skip ci] 2023-03-30 12:38:29 +00:00
ElisarEisenbach
76e4fe51b5 Refactoring analytics (#311)
* exampple

* send call type in function

* remove heap check

---------

Co-authored-by: itielshwartz <itielshw@gmail.com>
2023-03-30 14:42:35 +03:00
ElisarEisenbach
95ea5e4d6d Send analytics to server (#310)
* start adding functions

* refactoring analytics functions

* formatting analytics
2023-03-30 09:29:01 +01:00
Andrei Pohilko
c139f3941d Merge branch 'main' of github.com:komodorio/helm-dashboard 2023-03-24 17:39:49 +00:00
Andrei Pohilko
80022c3ef8 Don't fail on single resource status error
Relates to #301
2023-03-24 17:39:29 +00:00
Viet Nguyen
a07cfcdbb4 Fix typo on readme doc (#265) 2023-03-23 10:00:06 +00:00
Andrei Pohilko
8826124f70 Merge branch 'main' of github.com:komodorio/helm-dashboard 2023-03-23 09:55:29 +00:00
Andrei Pohilko
703b4029de Cosmetic cleanup 2023-03-23 09:55:11 +00:00
Harshit Mehta
a2dc1ed96b update README (#270) 2023-03-17 14:06:14 +00:00
Harshit Mehta
29c1682bbb Removing redundant table header (#268) 2023-03-16 12:31:43 +00:00
Andrei Pohilko
c7d18a7fb7 Count API docs in user analytics 2023-03-13 17:11:20 +00:00
Andrei Pohilko
e9ee10287b Improve console message in case no k8s connection possible 2023-03-13 15:26:32 +00:00
Andrey Pokhilko
57d4d073e9 Display resource health aggregate icons on list of releases (#235)
* Display aggregate resource health status

* Reuse old API request, show icons

* Take progress indication from deployment conditions

* Improve status

* Cleanup

* Fixups

* Squares approach
2023-03-13 12:56:31 +00:00
Harshit Mehta
47dae4d35a Add username and password support to Repo add feature (#228)
* Add username and password support to Repo add in UI

* Add support for Username and Passowrd in Add Repo API
2023-03-09 13:34:05 +00:00
Harshit Mehta
0ac8eec368 Minor UI fixes (#234) 2023-03-09 08:45:41 +00:00
komodor-bot
aec46d43f7 Increment chart versions [skip ci] 2023-03-08 09:27:49 +00:00
Andrei Pohilko
37e1d44bf1 Remove survey banner code 2023-03-07 15:07:59 +00:00
Andrei Pohilko
362cb09e6d Improve logo source 2023-03-07 15:00:49 +00:00
Andrei Pohilko
209f5b5e44 Improve upgradable status display 2023-03-07 14:51:46 +00:00
Andrei Pohilko
a0680a4820 Add links to Komodor 2023-03-07 12:51:39 +00:00
Andrei Pohilko
d95cac94d5 Auto-update repositories each 10 minutes, unless HD_NO_AUTOUPDATE is set 2023-03-06 11:38:33 +00:00
Andrey Pokhilko
bbb425bfea Query ArtifactHub for repo suggestion (#225)
* Query ArtifactHub for repo suggestion

* Refactor & improve

* Add notice on local chart support
2023-03-02 10:22:32 +00:00
komodor-bot
679d31e4ab Increment chart versions [skip ci] 2023-02-22 12:45:52 +00:00
Andrei Pohilko
3119d17738 Ignore test file 2023-02-22 11:35:02 +00:00
Andrei Pohilko
778e58360c Fix the values not considered correctly when loading the upgrade preview 2023-02-22 11:24:03 +00:00
Andrei Pohilko
a7c7ba80fe Overwrite old values correctly, don't cache current resources list 2023-02-21 15:22:49 +00:00
dependabot[bot]
d86c46aabf Bump golang.org/x/net from 0.5.0 to 0.7.0 (#220)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-18 09:16:04 +00:00
Harshit Mehta
c79259275a Execute install script in debug mode only when HELM_DEBUG flag is set (#219) 2023-02-17 12:52:50 +00:00
Andrey Pokhilko
4a4760d5b8 Update README.md 2023-02-16 16:36:13 +00:00
dependabot[bot]
244e35bb6b Bump github.com/containerd/containerd from 1.6.15 to 1.6.18 (#216)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.15 to 1.6.18.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.15...v1.6.18)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-16 15:08:46 +00:00
Andrei Pohilko
709c3c600b Fix inability to reconfigure charts without corresponding repository 2023-02-16 12:32:48 +00:00
komodor-bot
3060b92f8e Increment chart versions [skip ci] 2023-02-15 18:00:36 +00:00
Andrey Pokhilko
f49f52efe4 Support working with local charts (#215)
* Basic functioning

* Support reconfiguring

* Improve tests coverage

* Always update local repo, don't offer to delete it

* Handle multi-repo correctly

* Document local charts usage

* Screenshot for docs
2023-02-15 16:45:28 +00:00
Bhargav Ravuri
6a4ca793c9 New CLI Flag --devel To Include Development/Prerelease Versions of Charts (#139)
* Include devel Flag for Toggling Dev Chart Versions

The flag `--devel` for enabling/disabling dev versions
of charts in following endpoints:
1. /api/helm/repositories/kafka-operator
2. /api/helm/repositories/versions
3. /api/helm/repositories/latestver

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

* Run Tests on Devel Flag Related Changes

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

---------

Signed-off-by: Bhargav Ravuri <bhargav.ravuri@infracloud.io>
2023-02-11 16:02:01 +00:00
Andrey Pokhilko
61b67f8bed Display UI indication if chart is upgradable (#211) 2023-02-10 12:36:40 +00:00
dependabot[bot]
ac690b6332 Bump helm.sh/helm/v3 from 3.10.3 to 3.11.1 (#212)
Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.10.3 to 3.11.1.
- [Release notes](https://github.com/helm/helm/releases)
- [Commits](https://github.com/helm/helm/compare/v3.10.3...v3.11.1)

---
updated-dependencies:
- dependency-name: helm.sh/helm/v3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 11:25:58 +00:00
Andrei Pohilko
b613e4e9dc Fix list of releases not displaying pending 2023-02-08 12:07:49 +00:00
Andrei Pohilko
a9939d5067 Improve tests for repositories
Relates to #210
2023-02-08 10:11:18 +00:00
Andrei Pohilko
7a25335028 Fix PVC+PV definition in chart 2023-02-05 15:56:45 +00:00
Itiel shwartz
8befc1d017 Update README.md
Update survey in the readme
2023-02-02 11:46:04 +02:00
komodor-bot
aaf6ae80c5 Increment chart versions [skip ci] 2023-02-01 16:20:28 +00:00
45 changed files with 1436 additions and 533 deletions

View File

@@ -1,14 +1,10 @@
<!-- If your PR fixes an open issue, use `Closes #999` to link your PR with the issue. #999 stands for the issue number you are fixing --> ## Changes Proposed
## Fixes Issue <!-- Describe the proposed changes and any additional information -->
<!-- Remove this section if not applicable --> <!-- Add all the screenshots which illustrate your changes -->
<!-- Example: Closes #31 --> ## Check List
## Changes proposed
<!-- List all the proposed changes in your PR -->
<!-- Mark all the applicable boxes. To mark the box as done follow the following conventions --> <!-- Mark all the applicable boxes. To mark the box as done follow the following conventions -->
<!-- <!--
@@ -18,18 +14,8 @@
[ ] - Not correct; marked as **not** done [ ] - Not correct; marked as **not** done
--> -->
## Check List (Check all the applicable boxes) <!-- Follow the above conventions to check the box --> - [ ] The title of my pull request is a short description of the changes
- [ ] This PR relates to some issue: <!-- use "Closes #999" to auto-close related issue -->
- [ ] I have documented the changes made (if applicable)
- [ ] I have covered the changes with unit tests
- [ ] My code follows the code style of this project.
- [ ] My change requires changes to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] All new and existing tests passed.
- [ ] The title of my pull request is a short description of the requested changes.
## Screenshots
<!-- Add all the screenshots which support your changes -->
## Note to reviewers
<!-- Add notes to reviewers if applicable -->

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ go.work
.DS_Store .DS_Store
.vscode/ .vscode/
/pkg/dashboard/objects/testdata/hello-world-0.1.0.tgz

View File

@@ -8,9 +8,11 @@
<p align="center">A simplified way of working with Helm.</p> <p align="center">A simplified way of working with Helm.</p>
![GitHub contributors](https://img.shields.io/github/contributors/komodorio/helm-dashboard) [![GitHub issues](https://img.shields.io/github/issues-raw/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard/issues) ![GitHub stars](https://img.shields.io/github/stars/komodorio/helm-dashboard?style=social) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/komodorio/helm-dashboard) ![GitHub pull requests](https://img.shields.io/github/issues-pr/komodorio/helm-dashboard) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/komodorio/helm-dashboard) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/komodorio/helm-dashboard) [![GitHub license](https://img.shields.io/github/license/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard)
<kbd>[<img src="screenshot.png" style="width: 100%; border: 1px solid silver;" border="1" alt="Screenshot">](screenshot.png)</kbd> ![GitHub contributors](https://img.shields.io/github/contributors/komodorio/helm-dashboard) [![GitHub issues](https://img.shields.io/github/issues-raw/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard/issues) ![GitHub stars](https://img.shields.io/github/stars/komodorio/helm-dashboard?style=social) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/komodorio/helm-dashboard) ![GitHub pull requests](https://img.shields.io/github/issues-pr/komodorio/helm-dashboard) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard/releases) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/komodorio/helm-dashboard) [![GitHub license](https://img.shields.io/github/license/komodorio/helm-dashboard)](https://github.com/komodorio/helm-dashboard)
<kbd>[<img src="images/screenshot.png" style="width: 100%; border: 1px solid silver;" border="1" alt="Screenshot">](images/screenshot.png)</kbd>
## Description ## Description
@@ -92,29 +94,44 @@ If you want to increase the logging verbosity and see all the debug info, use th
The official helm chart is [available here](https://github.com/komodorio/helm-charts/blob/master/charts/helm-dashboard) The official helm chart is [available here](https://github.com/komodorio/helm-charts/blob/master/charts/helm-dashboard)
## Selected Features
## Execute Helm tests ### Support for Local Charts
Local Helm chart is a directory with specially named files and a `Chart.yaml` file, which you can install via Helm, without the need to publish the chart into Helm repository. Chart developers might want to experiment with the chart locally, before uploading into public repository. Also, a proprietary application might only use non-published chart as an approach to deploy the software.
For all the above use-cases, you may use Helm Dashboard UI, spcifying location of your local chart folders via special `--local-chart` command-line parameter. The parameter might be specified multiple times, for example:
```shell
helm-dashboard --local-chart=/opt/charts/my-private-app --local-chart=/home/dev/sources/app/chart
```
When _valid_ local chart sources specified, the repository list would contain a surrogate `[local]` entry, with those charts listed inside. All the chart operations are normal: installing, reconfiguring and upgrading.
![](images/screenshot_local_charts.png)
### Execute Helm tests
For all the release(s) (installed helm charts), you can execute helm tests for that release. For the tests to execute successfully, you need to have existing tests for that helm chart. For all the release(s) (installed helm charts), you can execute helm tests for that release. For the tests to execute successfully, you need to have existing tests for that helm chart.
You can execute `helm test` for the specific release as below: You can execute `helm test` for the specific release as below:
![](screenshot_run_test.png) ![](images/screenshot_run_test.png)
The result of executed `helm test` for the release will be disapled as below: The result of executed `helm test` for the release will be displayed as below:
![](screenshot_run_test_result.png) ![](images/screenshot_run_test_result.png)
## Scanner Integrations ### Scanner Integrations
Upon startup, Helm Dashboard detects the presence of [Trivy](https://github.com/aquasecurity/trivy) Upon startup, Helm Dashboard detects the presence of [Trivy](https://github.com/aquasecurity/trivy)
and [Checkov](https://github.com/bridgecrewio/checkov) scanners. When available, these scanners are offered on k8s and [Checkov](https://github.com/bridgecrewio/checkov) scanners. When available, these scanners are offered on k8s
resources page, as well as install/upgrade preview page. resources page, as well as install/upgrade preview page.
You can request scanning of the specific k8s resource in your cluster: You can request scanning of the specific k8s resource in your cluster:
![](screenshot_scan_resource.png) ![](images/screenshot_scan_resource.png)
If you want to validate the k8s manifest prior to installing/reconfiguring a Helm chart, look for "Scan for Problems" If you want to validate the k8s manifest prior to installing/reconfiguring a Helm chart, look for "Scan for Problems"
button at the bottom of the dialog: button at the bottom of the dialog:
![](screenshot_scan_manifest.png) ![](images/screenshot_scan_manifest.png)
## Support Channels ## Support Channels
@@ -134,15 +151,25 @@ Kindly read our [Contributing Guide](CONTRIBUTING.md) to learn and understand ab
## Local Dev Testing ## Local Dev Testing
Prerequisites: `helm` and `kubectl` binaries installed and operational. Prerequisites, binaries installed and operational:
- [Go](https://go.dev/doc/install)
There is a need to build binary for plugin to function, run: There is a need to build binary for plugin to function, run:
### Linux
```shell ```shell
go build -o bin/dashboard . go build -o bin/dashboard .
``` ```
You can just run the `bin/dashboard` binary directly, it will just work. ### Windows
```bat
go build -o bin\dashboard.exe .
```
You can just run the `dashboard` or `dashboard.exe` binary directly, it will just work.
To install, checkout the source code and run from source dir: To install, checkout the source code and run from source dir:

View File

@@ -5,5 +5,5 @@ name: helm-dashboard
description: A GUI Dashboard for Helm by Komodor description: A GUI Dashboard for Helm by Komodor
icon: "https://raw.githubusercontent.com/komodorio/helm-dashboard/main/pkg/dashboard/static/logo.svg" icon: "https://raw.githubusercontent.com/komodorio/helm-dashboard/main/pkg/dashboard/static/logo.svg"
version: 0.1.2 version: 0.1.7
appVersion: "0.3.1" appVersion: "1.3.0"

View File

@@ -5,7 +5,7 @@
```bash ```bash
helm repo add komodorio https://helm-charts.komodor.io helm repo add komodorio https://helm-charts.komodor.io
helm repo update helm repo update
helm upgrade --install my-release komodorio/helm-dashboard helm upgrade --install helm-dashboard komodorio/helm-dashboard
``` ```
## Introduction ## Introduction
@@ -17,14 +17,13 @@ While installed inside cluster, Helm Dashboard will run some additional backgrou
## Prerequisites ## Prerequisites
- Kubernetes 1.16+ - Kubernetes 1.16+
- Helm 3+
## Installing the Chart ## Installing the Chart
To install the chart with the release name `my-release`: To install the chart with the release name `helm-dashboard`:
```bash ```bash
helm install my-release . helm install helm-dashboard .
``` ```
The command deploys Helm Dashboard on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. The command deploys Helm Dashboard on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation.
@@ -33,10 +32,10 @@ The command deploys Helm Dashboard on the Kubernetes cluster in the default conf
## Uninstalling the Chart ## Uninstalling the Chart
To uninstall/delete the `my-release` deployment: To uninstall/delete the `helm-dashboard` deployment:
```bash ```bash
helm uninstall my-release helm uninstall helm-dashboard
``` ```
The command removes all the Kubernetes components associated with the chart and deletes the release. The command removes all the Kubernetes components associated with the chart and deletes the release.
@@ -80,7 +79,7 @@ The following table lists the configurable parameters of the chart and their def
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
```bash ```bash
helm upgrade --install my-release komodorio/helm-dashboard --set dashboard.allowWriteActions=true --set service.port=9090 helm upgrade --install helm-dashboard komodorio/helm-dashboard --set dashboard.allowWriteActions=true --set service.port=9090
``` ```
> **Tip**: You can use the default [values.yaml](values.yaml) > **Tip**: You can use the default [values.yaml](values.yaml)

View File

@@ -14,8 +14,10 @@ spec:
{{- if .Values.dashboard.persistence.hostPath }} {{- if .Values.dashboard.persistence.hostPath }}
storageClassName: "" storageClassName: ""
{{- else }} {{- else }}
{{- if kindIs "string" .Values.dashboard.persistence.storageClass }}
storageClassName: "{{ .Values.dashboard.persistence.storageClass }}" storageClassName: "{{ .Values.dashboard.persistence.storageClass }}"
{{- end }} {{- end }}
{{- end }}
accessModes: accessModes:
{{- if not (empty .Values.dashboard.persistence.accessModes) }} {{- if not (empty .Values.dashboard.persistence.accessModes) }}
{{- range .Values.dashboard.persistence.accessModes }} {{- range .Values.dashboard.persistence.accessModes }}
@@ -42,7 +44,11 @@ metadata:
{{- end }} {{- end }}
spec: spec:
accessModes: accessModes:
- {{ .Values.dashboard.persistence.accessMode | quote }} {{- if not (empty .Values.dashboard.persistence.accessModes) }}
{{- range .Values.dashboard.persistence.accessModes }}
- {{ . | quote }}
{{- end }}
{{- end }}
capacity: capacity:
storage: {{ .Values.dashboard.persistence.size | quote }} storage: {{ .Values.dashboard.persistence.size | quote }}
hostPath: hostPath:

View File

@@ -43,7 +43,7 @@ dashboard:
## set, choosing the default provisioner. (gp2 on AWS, standard on ## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack) ## GKE, AWS & OpenStack)
## ##
storageClass: "" storageClass: null
## Helm Dashboard Persistent Volume access modes ## Helm Dashboard Persistent Volume access modes
## Must match those of existing PV or dynamic provisioner ## Must match those of existing PV or dynamic provisioner

54
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/komodorio/helm-dashboard
go 1.18 go 1.18
require ( require (
github.com/Masterminds/semver/v3 v3.2.0
github.com/eko/gocache/v3 v3.1.2 github.com/eko/gocache/v3 v3.1.2
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
@@ -17,21 +18,22 @@ require (
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.10.3 gotest.tools/v3 v3.4.0
helm.sh/helm/v3 v3.11.1
k8s.io/api v0.26.0 k8s.io/api v0.26.0
k8s.io/apimachinery v0.26.0 k8s.io/apimachinery v0.26.0
k8s.io/cli-runtime v0.26.0 k8s.io/cli-runtime v0.26.0
k8s.io/client-go v0.26.0 k8s.io/client-go v0.26.0
k8s.io/kubectl v0.26.0 k8s.io/kubectl v0.26.0
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d
) )
require ( require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/BurntSushi/toml v1.1.0 // indirect github.com/BurntSushi/toml v1.2.1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
@@ -40,14 +42,14 @@ require (
github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/containerd v1.6.12 // indirect github.com/containerd/containerd v1.6.18 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/cli v20.10.21+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.17+incompatible // indirect github.com/docker/docker v20.10.24+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect
@@ -56,11 +58,10 @@ require (
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/camelcase v1.0.0 // indirect
github.com/fatih/color v1.13.0 // indirect github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect github.com/go-errors/errors v1.0.1 // indirect
github.com/go-gorp/gorp/v3 v3.0.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect
@@ -82,7 +83,7 @@ require (
github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/mux v1.8.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/huandu/xstrings v1.3.2 // indirect github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.12 // indirect github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect
@@ -92,10 +93,10 @@ require (
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.6 // indirect github.com/lib/pq v1.10.7 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.6 // indirect github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-colorable v0.1.13 // 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/mattn/go-runewidth v0.0.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
@@ -104,14 +105,14 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // 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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
github.com/pegasus-kv/thrift v0.13.0 // 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/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
@@ -120,11 +121,11 @@ require (
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/rubenv/sql-migrate v1.1.2 // indirect github.com/rubenv/sql-migrate v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.6.0 // indirect github.com/spf13/cobra v1.6.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // 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 github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
@@ -132,16 +133,15 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.1.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect golang.org/x/crypto v0.5.0 // indirect
golang.org/x/exp v0.0.0-20221110155412-d0897a79cd37 // indirect golang.org/x/exp v0.0.0-20221110155412-d0897a79cd37 // indirect
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect golang.org/x/net v0.7.0 // indirect
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
golang.org/x/sync v0.1.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.3.0 // indirect golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.3.0 // indirect golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
@@ -151,14 +151,12 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // 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
gotest.tools/v3 v3.4.0 k8s.io/apiextensions-apiserver v0.26.0 // indirect
k8s.io/apiextensions-apiserver v0.25.2 // indirect k8s.io/apiserver v0.26.0 // indirect
k8s.io/apiserver v0.25.2 // indirect
k8s.io/component-base v0.26.0 // indirect k8s.io/component-base v0.26.0 // indirect
k8s.io/klog/v2 v2.80.1 // indirect k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d oras.land/oras-go v1.2.2 // indirect
oras.land/oras-go v1.2.0 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect

140
go.sum
View File

@@ -40,8 +40,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
@@ -49,16 +49,16 @@ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
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/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/hcsshim v0.9.5 h1:AbV+VPfTrIVffukazHcpxmz/sRiE6YaMDzHWR9BXZHo= github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 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/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/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
@@ -110,20 +110,18 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
github.com/containerd/containerd v1.6.12 h1:kJ9b3mOFKf8yqo05Ob+tMoxvt1pbVWhnB0re9Y+k+8c= github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
github.com/containerd/containerd v1.6.12/go.mod h1:K4Bw7gjgh4TnkmQY+py/PYQGp4e7xgnHAeg87VeWb3A= github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
github.com/coocood/freecache v1.2.3 h1:lcBwpZrwBZRZyLk/8EMyQVXRiFl663cCuMOrjCALeto= github.com/coocood/freecache v1.2.3 h1:lcBwpZrwBZRZyLk/8EMyQVXRiFl663cCuMOrjCALeto=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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=
@@ -132,15 +130,15 @@ github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27N
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU=
github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE=
github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
@@ -183,8 +181,7 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 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.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -198,8 +195,9 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 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-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-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4=
github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 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/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.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -368,6 +366,7 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@@ -377,8 +376,9 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -425,8 +425,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
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.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -439,8 +439,8 @@ 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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -457,8 +457,8 @@ github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@@ -468,14 +468,15 @@ github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 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/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -496,8 +497,8 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
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=
@@ -530,8 +531,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= 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/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
@@ -555,8 +556,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg=
github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
@@ -591,8 +592,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
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/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU=
github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -617,11 +618,10 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
@@ -662,14 +662,12 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -697,9 +695,11 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-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.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -737,6 +737,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -784,8 +785,10 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
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-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-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -812,6 +815,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/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-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -878,18 +882,20 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/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.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/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/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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=
@@ -900,8 +906,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -921,7 +928,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/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-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-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-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-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-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -962,6 +968,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
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=
@@ -1111,11 +1118,10 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 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=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
helm.sh/helm/v3 v3.10.3 h1:wL7IUZ7Zyukm5Kz0OUmIFZgKHuAgByCrUcJBtY0kDyw= helm.sh/helm/v3 v3.11.1 h1:cmL9fFohOoNQf+wnp2Wa0OhNFH0KFnSzEkVxi3fcc3I=
helm.sh/helm/v3 v3.10.3/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= helm.sh/helm/v3 v3.11.1/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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-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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1125,13 +1131,13 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I=
k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=
k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=
k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw= k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o=
k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I= k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=
k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw=
k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY=
k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8=
@@ -1150,8 +1156,8 @@ k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0=
k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE=
oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc= oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

18
main.go
View File

@@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/joomcode/errorx"
"os" "os"
"os/signal" "os/signal"
"strings" "strings"
@@ -25,13 +26,20 @@ type options struct {
Version bool `long:"version" description:"Show tool version"` Version bool `long:"version" description:"Show tool version"`
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 (Heap, 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 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"` Port uint `short:"p" long:"port" description:"Port to start server on" default:"8080"`
Namespace string `short:"n" long:"namespace" description:"Namespace for HELM operations"` Namespace string `short:"n" long:"namespace" description:"Namespace for HELM operations"`
Devel bool `long:"devel" description:"Include development versions of charts"`
LocalChart []string `long:"local-chart" description:"Specify location of local chart to include into UI"`
} }
func main() { func main() {
err := os.Setenv("HD_VERSION", version) // for anyone willing to access it
if err != nil {
fmt.Println("Failed to remember app version because of error: " + err.Error())
}
opts := parseFlags() opts := parseFlags()
if opts.BindHost == "" { if opts.BindHost == "" {
host := os.Getenv("HD_BIND") host := os.Getenv("HD_BIND")
@@ -50,6 +58,8 @@ func main() {
Address: fmt.Sprintf("%s:%d", opts.BindHost, opts.Port), Address: fmt.Sprintf("%s:%d", opts.BindHost, opts.Port),
Debug: opts.Verbose, Debug: opts.Verbose,
NoTracking: opts.NoTracking, NoTracking: opts.NoTracking,
Devel: opts.Devel,
LocalCharts: opts.LocalChart,
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@@ -64,8 +74,14 @@ func main() {
address, webServerDone, err := server.StartServer(ctx, cancel) address, webServerDone, err := server.StartServer(ctx, cancel)
if err != nil { if err != nil {
if errorx.IsOfType(err, errorx.InitializationFailed) {
log.Debugf("Full error: %+v", err)
log.Errorf("No Kubernetes cluster connection possible. Make sure you have valid kubeconfig file or run dashboard from inside cluster. See https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/")
os.Exit(1)
} else {
log.Fatalf("Failed to start Helm Dashboard: %+v", err) log.Fatalf("Failed to start Helm Dashboard: %+v", err)
} }
}
if !opts.NoTracking { if !opts.NoTracking {
log.Infof("User analytics is 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")

View File

@@ -1,6 +1,15 @@
package dashboard package dashboard
import ( import (
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"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/objects" "github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
@@ -13,14 +22,6 @@ import (
"helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/storage" "helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver" "helm.sh/helm/v3/pkg/storage/driver"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
) )
var inMemStorage *storage.Storage var inMemStorage *storage.Storage
@@ -111,7 +112,7 @@ func TestConfigureRoutes(t *testing.T) {
// Required arguements for route configuration // Required arguements for route configuration
abortWeb := func() {} abortWeb := func() {}
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -131,7 +132,7 @@ func TestContextSetter(t *testing.T) {
con := GetTestGinContext(w) con := GetTestGinContext(w)
// Required arguements // Required arguements
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -161,7 +162,7 @@ func TestNewRouter(t *testing.T) {
// Required arguemnets // Required arguemnets
abortWeb := func() {} abortWeb := func() {}
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -183,7 +184,7 @@ func TestConfigureScanners(t *testing.T) {
} }
// Required arguemnets // Required arguemnets
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -206,7 +207,7 @@ func TestConfigureKubectls(t *testing.T) {
} }
// Required arguemnets // Required arguemnets
data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig) data, err := objects.NewDataLayer([]string{"TestSpace"}, "T-1", NewHelmConfig, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -226,7 +227,7 @@ func TestConfigureKubectls(t *testing.T) {
func TestE2E(t *testing.T) { func TestE2E(t *testing.T) {
// Initialize data layer // Initialize data layer
data, err := objects.NewDataLayer([]string{""}, "0.0.0-test", getFakeHelmConfig) data, err := objects.NewDataLayer([]string{""}, "0.0.0-test", getFakeHelmConfig, false)
assert.NilError(t, err) assert.NilError(t, err)
// Create a new router with the function // Create a new router with the function

View File

@@ -1,13 +1,23 @@
package handlers package handlers
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"net/http"
"sort"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"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"
"github.com/joomcode/errorx" "github.com/joomcode/errorx"
"github.com/komodorio/helm-dashboard/pkg/dashboard/objects" "github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
"github.com/rogpeppe/go-internal/semver" "github.com/rogpeppe/go-internal/semver"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -15,12 +25,7 @@ import (
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/repo"
helmtime "helm.sh/helm/v3/pkg/time" helmtime "helm.sh/helm/v3/pkg/time"
"net/http" "k8s.io/utils/strings/slices"
"sort"
"strconv"
"github.com/gin-gonic/gin"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
) )
type HelmHandler struct { type HelmHandler struct {
@@ -120,7 +125,7 @@ func (h *HelmHandler) History(c *gin.Context) {
} }
func (h *HelmHandler) Resources(c *gin.Context) { func (h *HelmHandler) Resources(c *gin.Context) {
h.EnableClientCache(c) // can't enable the client cache because resource list changes with time
rel := h.getRelease(c) rel := h.getRelease(c)
if rel == nil { if rel == nil {
@@ -129,9 +134,40 @@ func (h *HelmHandler) Resources(c *gin.Context) {
res, err := objects.ParseManifests(rel.Orig.Manifest) res, err := objects.ParseManifests(rel.Orig.Manifest)
if err != nil { if err != nil {
res = append(res, &v1.Carp{
TypeMeta: metav1.TypeMeta{Kind: "ManifestParseError"},
ObjectMeta: metav1.ObjectMeta{
Name: err.Error(),
},
Spec: v1.CarpSpec{},
Status: v1.CarpStatus{
Phase: "BrokenManifest",
Message: err.Error(),
},
})
//_ = c.AbortWithError(http.StatusInternalServerError, err)
//return
}
if c.Query("health") != "" { // we need to query k8s for health status
app := h.GetApp(c)
if app == nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
} }
for _, obj := range res {
ns := obj.Namespace
if ns == "" {
ns = c.Param("ns")
}
info, err := app.K8s.GetResourceInfo(obj.Kind, ns, obj.Name)
if err != nil {
log.Warnf("Failed to get resource info for %s %s/%s: %+v", obj.Name, ns, obj.Name, err)
info = &v1.Carp{}
}
obj.Status = *EnhanceStatus(info, err)
}
}
c.IndentedJSON(http.StatusOK, res) c.IndentedJSON(http.StatusOK, res)
} }
@@ -162,6 +198,7 @@ func (h *HelmHandler) RepoVersions(c *gin.Context) {
AppVersion: r.AppVersion, AppVersion: r.AppVersion,
Description: r.Description, Description: r.Description,
Repository: r.Annotations[objects.AnnRepo], Repository: r.Annotations[objects.AnnRepo],
URLs: r.URLs,
}) })
} }
@@ -194,6 +231,7 @@ func (h *HelmHandler) RepoLatestVer(c *gin.Context) {
AppVersion: r.AppVersion, AppVersion: r.AppVersion,
Description: r.Description, Description: r.Description,
Repository: r.Annotations[objects.AnnRepo], Repository: r.Annotations[objects.AnnRepo],
URLs: r.URLs,
}) })
} }
@@ -204,7 +242,21 @@ func (h *HelmHandler) RepoLatestVer(c *gin.Context) {
if len(res) > 0 { if len(res) > 0 {
c.IndentedJSON(http.StatusOK, res[:1]) c.IndentedJSON(http.StatusOK, res[:1])
} else { } else {
// caching it to avoid too many requests
found, err := h.Data.Cache.String("chart-artifacthub-query/"+qp.Name, nil, func() (string, error) {
return h.repoFromArtifactHub(qp.Name)
})
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
if found == "" {
c.Status(http.StatusNoContent) c.Status(http.StatusNoContent)
} else {
c.Header("Content-Type", "application/json")
c.String(http.StatusOK, found)
}
} }
} }
@@ -232,7 +284,6 @@ func (h *HelmHandler) RepoCharts(c *gin.Context) {
return return
} }
// TODO: enrich with installed
enrichRepoChartsWithInstalled(charts, installed) enrichRepoChartsWithInstalled(charts, installed)
sort.Slice(charts, func(i, j int) bool { sort.Slice(charts, func(i, j int) bool {
@@ -288,12 +339,19 @@ func (h *HelmHandler) Install(c *gin.Context) {
return return
} }
repoChart, err := h.checkLocalRepo(c.PostForm("chart"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
justTemplate := c.PostForm("preview") == "true" justTemplate := c.PostForm("preview") == "true"
ns := c.Param("ns") ns := c.Param("ns")
if ns == "[empty]" { if ns == "[empty]" {
ns = "" ns = ""
} }
rel, err := app.Releases.Install(ns, c.PostForm("name"), c.PostForm("chart"), c.PostForm("version"), justTemplate, values)
rel, err := app.Releases.Install(ns, c.PostForm("name"), repoChart, c.PostForm("version"), justTemplate, values)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -306,6 +364,16 @@ func (h *HelmHandler) Install(c *gin.Context) {
} }
} }
func (h *HelmHandler) checkLocalRepo(repoChart string) (string, error) {
if strings.HasPrefix(repoChart, "file://") {
repoChart = repoChart[len("file://"):]
if !slices.Contains(h.Data.LocalCharts, repoChart) {
return "", fmt.Errorf("chart path is not present in local charts: %s", repoChart)
}
}
return repoChart, nil
}
func (h *HelmHandler) Upgrade(c *gin.Context) { func (h *HelmHandler) Upgrade(c *gin.Context) {
app := h.GetApp(c) app := h.GetApp(c)
if app == nil { if app == nil {
@@ -325,8 +393,14 @@ func (h *HelmHandler) Upgrade(c *gin.Context) {
return return
} }
repoChart, err := h.checkLocalRepo(c.PostForm("chart"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
justTemplate := c.PostForm("preview") == "true" justTemplate := c.PostForm("preview") == "true"
rel, err := existing.Upgrade(c.PostForm("chart"), c.PostForm("version"), justTemplate, values) rel, err := existing.Upgrade(repoChart, c.PostForm("version"), justTemplate, values)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -409,7 +483,13 @@ func (h *HelmHandler) RepoValues(c *gin.Context) {
return // sets error inside return // sets error inside
} }
out, err := app.Repositories.GetChartValues(c.Query("chart"), c.Query("version")) repoChart, err := h.checkLocalRepo(c.Query("chart"))
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
out, err := app.Repositories.GetChartValues(repoChart, c.Query("version"))
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -433,8 +513,8 @@ func (h *HelmHandler) RepoList(c *gin.Context) {
out := []RepositoryElement{} out := []RepositoryElement{}
for _, r := range repos { for _, r := range repos {
out = append(out, RepositoryElement{ out = append(out, RepositoryElement{
Name: r.Orig.Name, Name: r.Name(),
URL: r.Orig.URL, URL: r.URL(),
}) })
} }
@@ -448,7 +528,7 @@ func (h *HelmHandler) RepoAdd(c *gin.Context) {
} }
// TODO: more repo options to accept // TODO: more repo options to accept
err := app.Repositories.Add(c.PostForm("name"), c.PostForm("url")) err := app.Repositories.Add(c.PostForm("name"), c.PostForm("url"), c.PostForm("username"), c.PostForm("password"))
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
@@ -521,15 +601,73 @@ func (h *HelmHandler) handleGetSection(rel *objects.Release, section string, rDi
return res, nil return res, nil
} }
type RepoChartElement struct { func (h *HelmHandler) repoFromArtifactHub(name string) (string, error) {
results, err := objects.QueryArtifactHub(name)
if err != nil {
log.Warnf("Failed to query ArtifactHub: %s", err)
return "", nil // swallowing the error to not annoy users
}
if len(results) == 0 {
return "", nil
}
sort.SliceStable(results, func(i, j int) bool {
ri, rj := results[i], results[j]
// we prefer official repos
if ri.Repository.Official && !rj.Repository.Official {
return true
}
// more popular
if ri.Stars != rj.Stars {
return ri.Stars > rj.Stars
}
// or from verified publishers
if ri.Repository.VerifiedPublisher && !rj.Repository.VerifiedPublisher {
return true
}
// or with more recent app version
c := semver.Compare("v"+ri.AppVersion, "v"+rj.AppVersion)
if c != 0 {
return c > 0
}
// shorter repo name is usually closer to officials
return len(ri.Repository.Name) < len(rj.Repository.Name)
})
r := results[0]
buf, err := json.Marshal([]*RepoChartElement{{
Name: r.Name,
Version: r.Version,
AppVersion: r.AppVersion,
Description: r.Description,
Repository: r.Repository.Name,
URLs: []string{r.Repository.Url},
IsSuggestedRepo: true,
}})
if err != nil {
return "", err
}
return string(buf), nil
}
type RepoChartElement struct { // TODO: do we need it at all? there is existing repo.ChartVersion in Helm
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
AppVersion string `json:"app_version"` AppVersion string `json:"app_version"`
Description string `json:"description"` Description string `json:"description"`
InstalledNamespace string `json:"installed_namespace"` // custom addition on top of Helm InstalledNamespace string `json:"installed_namespace"`
InstalledName string `json:"installed_name"` // custom addition on top of Helm InstalledName string `json:"installed_name"`
Repository string `json:"repository"` Repository string `json:"repository"`
URLs []string `json:"urls"`
IsSuggestedRepo bool `json:"isSuggestedRepo"`
} }
func HReleaseToJSON(o *release.Release) *ReleaseElement { func HReleaseToJSON(o *release.Release) *ReleaseElement {
@@ -540,6 +678,8 @@ func HReleaseToJSON(o *release.Release) *ReleaseElement {
Updated: o.Info.LastDeployed, Updated: o.Info.LastDeployed,
Status: o.Info.Status, Status: o.Info.Status,
Chart: fmt.Sprintf("%s-%s", o.Chart.Name(), o.Chart.Metadata.Version), Chart: fmt.Sprintf("%s-%s", o.Chart.Name(), o.Chart.Metadata.Version),
ChartName: o.Chart.Name(),
ChartVersion: o.Chart.Metadata.Version,
AppVersion: o.Chart.AppVersion(), AppVersion: o.Chart.AppVersion(),
Icon: o.Chart.Metadata.Icon, Icon: o.Chart.Metadata.Icon,
Description: o.Chart.Metadata.Description, Description: o.Chart.Metadata.Description,
@@ -553,6 +693,8 @@ type ReleaseElement struct {
Updated helmtime.Time `json:"updated"` Updated helmtime.Time `json:"updated"`
Status release.Status `json:"status"` Status release.Status `json:"status"`
Chart string `json:"chart"` Chart string `json:"chart"`
ChartName string `json:"chartName"`
ChartVersion string `json:"chartVersion"`
AppVersion string `json:"app_version"` AppVersion string `json:"app_version"`
Icon string `json:"icon"` Icon string `json:"icon"`
Description string `json:"description"` Description string `json:"description"`

View File

@@ -1,15 +1,21 @@
package handlers package handlers
import ( import (
"github.com/joomcode/errorx"
"k8s.io/apimachinery/pkg/api/errors"
"net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/joomcode/errorx"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils" "github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"k8s.io/utils/strings/slices"
"net/http"
) )
const Unknown = "Unknown"
const Healthy = "Healthy"
const Unhealthy = "Unhealthy"
const Progressing = "Progressing"
type KubeHandler struct { type KubeHandler struct {
*Contexted *Contexted
} }
@@ -50,25 +56,67 @@ func (h *KubeHandler) GetResourceInfo(c *gin.Context) {
return return
} }
EnhanceStatus(res) EnhanceStatus(res, nil)
c.IndentedJSON(http.StatusOK, res) c.IndentedJSON(http.StatusOK, res)
} }
func EnhanceStatus(res *v12.Carp) { func EnhanceStatus(res *v12.Carp, err error) *v12.CarpStatus {
s := res.Status
if s.Conditions == nil {
s.Conditions = []v12.CarpCondition{}
}
c := v12.CarpCondition{
Type: "hdHealth",
Status: Unknown,
Reason: s.Reason,
Message: s.Message,
}
// custom logic to provide most meaningful status for the resource // custom logic to provide most meaningful status for the resource
if res.Status.Phase == "Active" || res.Status.Phase == "Error" { if err != nil {
_ = res.Name + "" c.Reason = "ErrorGettingStatus"
} else if res.Status.Phase == "" && len(res.Status.Conditions) > 0 { c.Message = err.Error()
res.Status.Phase = v12.CarpPhase(res.Status.Conditions[len(res.Status.Conditions)-1].Type) } else if s.Phase == "Error" {
res.Status.Message = res.Status.Conditions[len(res.Status.Conditions)-1].Message c.Status = Unhealthy
res.Status.Reason = res.Status.Conditions[len(res.Status.Conditions)-1].Reason } else if slices.Contains([]string{"Available", "Active", "Established", "Bound", "Ready"}, string(s.Phase)) {
if res.Status.Conditions[len(res.Status.Conditions)-1].Status == "False" { c.Status = Healthy
res.Status.Phase = "Not" + res.Status.Phase } else if s.Phase == "" && len(s.Conditions) > 0 {
for _, cond := range s.Conditions {
if cond.Type == "Progressing" { // https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
if cond.Status == "False" {
c.Status = Unhealthy
c.Reason = cond.Reason
c.Message = cond.Message
} else if cond.Reason != "NewReplicaSetAvailable" {
c.Status = Progressing
c.Reason = cond.Reason
c.Message = cond.Message
} }
} else if res.Status.Phase == "" { } else if cond.Type == "Available" && c.Status == Unknown {
res.Status.Phase = "Exists" if cond.Status == "False" {
c.Status = Unhealthy
} else {
c.Status = Healthy
} }
c.Reason = cond.Reason
c.Message = cond.Message
}
}
} else if s.Phase == "Pending" {
c.Status = Progressing
c.Reason = string(s.Phase)
} else if s.Phase == "" {
c.Status = Healthy
c.Reason = "Exists"
} else {
log.Warnf("Unhandled status: %v", s)
c.Reason = string(s.Phase)
}
s.Conditions = append(s.Conditions, c)
return &s
} }
func (h *KubeHandler) Describe(c *gin.Context) { func (h *KubeHandler) Describe(c *gin.Context) {

View File

@@ -4,6 +4,7 @@ import (
"github.com/joomcode/errorx" "github.com/joomcode/errorx"
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
// Import to initialize client auth plugins. // Import to initialize client auth plugins.
// From https://github.com/kubernetes/client-go/issues/242 // From https://github.com/kubernetes/client-go/issues/242
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -22,7 +23,7 @@ type Application struct {
Repositories *Repositories Repositories *Repositories
} }
func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, namespaces []string) (*Application, error) { func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, namespaces []string, devel bool) (*Application, error) {
hc, err := helmConfig(settings.Namespace()) hc, err := helmConfig(settings.Namespace())
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to get helm config for namespace '%s'", "") return nil, errorx.Decorate(err, "failed to get helm config for namespace '%s'", "")
@@ -33,6 +34,11 @@ func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, na
return nil, errorx.Decorate(err, "failed to get k8s client") return nil, errorx.Decorate(err, "failed to get k8s client")
} }
semVerConstraint, err := versionConstaint(devel)
if err != nil {
return nil, errorx.Decorate(err, "failed to create semantic version constraint")
}
return &Application{ return &Application{
HelmConfig: helmConfig, HelmConfig: helmConfig,
K8s: k8s, K8s: k8s,
@@ -44,6 +50,7 @@ func NewApplication(settings *cli.EnvSettings, helmConfig HelmNSConfigGetter, na
Repositories: &Repositories{ Repositories: &Repositories{
Settings: settings, Settings: settings,
HelmConfig: hc, HelmConfig: hc,
versionConstraint: semVerConstraint,
}, },
}, nil }, nil
} }

View File

@@ -0,0 +1,90 @@
package objects
import (
"encoding/json"
"fmt"
log "github.com/sirupsen/logrus"
"net/http"
neturl "net/url"
"os"
"sync"
)
var mxArtifactHub sync.Mutex
func QueryArtifactHub(chartName string) ([]*ArtifactHubResult, error) {
mxArtifactHub.Lock() // to avoid parallel request spike
defer mxArtifactHub.Unlock()
url := os.Getenv("HD_ARTIFACT_HUB_URL")
if url == "" {
url = "https://artifacthub.io/api/v1/packages/search"
}
p, err := neturl.Parse(url)
if err != nil {
return nil, err
}
p.RawQuery = "offset=0&limit=5&facets=false&kind=0&deprecated=false&sort=relevance&ts_query_web=" + neturl.QueryEscape(chartName)
req, err := http.NewRequest("GET", p.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", "Komodor Helm Dashboard/"+os.Getenv("HD_VERSION")) // TODO
log.Debugf("Making HTTP request: %v", req)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, fmt.Errorf("failed to fetch %s : %s", p.String(), res.Status)
}
result := ArtifactHubResults{}
err = json.NewDecoder(res.Body).Decode(&result)
if err != nil {
return nil, err
}
return result.Packages, nil
}
type ArtifactHubResults struct {
Packages []*ArtifactHubResult `json:"packages"`
}
type ArtifactHubResult struct {
PackageId string `json:"package_id"`
Name string `json:"name"`
NormalizedName string `json:"normalized_name"`
LogoImageId string `json:"logo_image_id"`
Stars int `json:"stars"`
Description string `json:"description"`
Version string `json:"version"`
AppVersion string `json:"app_version"`
Deprecated bool `json:"deprecated"`
Signed bool `json:"signed"`
ProductionOrganizationsCount int `json:"production_organizations_count"`
Ts int `json:"ts"`
Repository ArtifactHubRepo `json:"repository"`
}
type ArtifactHubRepo struct {
Url string `json:"url"`
Kind int `json:"kind"`
Name string `json:"name"`
Official bool `json:"official"`
DisplayName string `json:"display_name"`
RepositoryId string `json:"repository_id"`
ScannerDisabled bool `json:"scanner_disabled"`
OrganizationName string `json:"organization_name"`
VerifiedPublisher bool `json:"verified_publisher"`
OrganizationDisplayName string `json:"organization_display_name"`
}

View File

@@ -4,9 +4,12 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"os"
"sync" "sync"
"time" "time"
"io"
"github.com/joomcode/errorx" "github.com/joomcode/errorx"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc" "github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -15,9 +18,9 @@ import (
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
"io"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1" v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
//"sigs.k8s.io/yaml"
) )
type DataLayer struct { type DataLayer struct {
@@ -30,6 +33,8 @@ type DataLayer struct {
ConfGen HelmConfigGetter ConfGen HelmConfigGetter
appPerContext map[string]*Application appPerContext map[string]*Application
appPerContextMx *sync.Mutex appPerContextMx *sync.Mutex
devel bool
LocalCharts []string
} }
type StatusInfo struct { type StatusInfo struct {
@@ -40,7 +45,7 @@ type StatusInfo struct {
ClusterMode bool ClusterMode bool
} }
func NewDataLayer(ns []string, ver string, cg HelmConfigGetter) (*DataLayer, error) { func NewDataLayer(ns []string, ver string, cg HelmConfigGetter, devel bool) (*DataLayer, error) {
if cg == nil { if cg == nil {
return nil, errors.New("HelmConfigGetter can't be nil") return nil, errors.New("HelmConfigGetter can't be nil")
} }
@@ -56,6 +61,7 @@ func NewDataLayer(ns []string, ver string, cg HelmConfigGetter) (*DataLayer, err
ConfGen: cg, ConfGen: cg,
appPerContext: map[string]*Application{}, appPerContext: map[string]*Application{},
appPerContextMx: new(sync.Mutex), appPerContextMx: new(sync.Mutex),
devel: devel,
}, nil }, nil
} }
@@ -108,20 +114,20 @@ func ParseManifests(out string) ([]*v1.Carp, error) {
} }
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to parse manifest document #%d", len(res)+1) return res, errorx.Decorate(err, "failed to parse manifest document #%d", len(res)+1)
} }
// 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
// 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 res, err
} }
var doc v1.Carp var doc v1.Carp
err = json.Unmarshal(jsoned, &doc) err = json.Unmarshal(jsoned, &doc)
if err != nil { if err != nil {
return nil, err return res, err
} }
if doc.Kind == "" { if doc.Kind == "" {
@@ -162,11 +168,13 @@ func (d *DataLayer) AppForCtx(ctx string) (*Application, error) {
return d.ConfGen(settings, ns) return d.ConfGen(settings, ns)
} }
a, err := NewApplication(settings, cfgGetter, d.Namespaces) a, err := NewApplication(settings, cfgGetter, d.Namespaces, d.devel)
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "Failed to create application for context '%s'", ctx) return nil, errorx.Decorate(err, "Failed to create application for context '%s'", ctx)
} }
a.Repositories.LocalCharts = d.LocalCharts
app = a app = a
d.appPerContext[ctx] = app d.appPerContext[ctx] = app
} }
@@ -188,13 +196,12 @@ func (d *DataLayer) nsForCtx(ctx string) string {
} }
func (d *DataLayer) PeriodicTasks(ctx context.Context) { func (d *DataLayer) PeriodicTasks(ctx context.Context) {
if !d.StatusInfo.ClusterMode { // TODO: maybe have a separate flag for that? // TODO: separate scanning setup for in-cluster?
log.Debugf("Not in cluster mode, not starting background tasks")
return
}
if os.Getenv("HD_NO_AUTOUPDATE") == "" {
// auto-update repos // auto-update repos
go d.loopUpdateRepos(ctx, 10*time.Minute) // TODO: parameterize interval? go d.loopUpdateRepos(ctx, 10*time.Minute) // TODO: parameterize interval?
}
// auto-scan // auto-scan
} }
@@ -215,7 +222,7 @@ func (d *DataLayer) loopUpdateRepos(ctx context.Context, interval time.Duration)
for _, repo := range repos { for _, repo := range repos {
err := repo.Update() err := repo.Update()
if err != nil { if err != nil {
log.Warnf("Failed to update repo %s: %v", repo.Orig.Name, err) log.Warnf("Failed to update repo %s: %v", repo.Name(), err)
} }
} }
} }

View File

@@ -15,6 +15,7 @@ func TestNewDataLayer(t *testing.T) {
namespaces []string namespaces []string
version string version string
helmConfig HelmConfigGetter helmConfig HelmConfigGetter
devel bool
errorExpected bool errorExpected bool
}{ }{
{ {
@@ -22,6 +23,7 @@ func TestNewDataLayer(t *testing.T) {
namespaces: []string{"namespace1", "namespace2"}, namespaces: []string{"namespace1", "namespace2"},
version: "1.0.0", version: "1.0.0",
helmConfig: nil, helmConfig: nil,
devel: false,
errorExpected: true, errorExpected: true,
}, },
{ {
@@ -34,12 +36,13 @@ func TestNewDataLayer(t *testing.T) {
helmConfig: func(sett *cli.EnvSettings, ns string) (*action.Configuration, error) { helmConfig: func(sett *cli.EnvSettings, ns string) (*action.Configuration, error) {
return &action.Configuration{}, nil return &action.Configuration{}, nil
}, },
devel: false,
errorExpected: false, errorExpected: false,
}, },
} }
for _, tt := range testCases { for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
dl, err := NewDataLayer(tt.namespaces, tt.version, tt.helmConfig) dl, err := NewDataLayer(tt.namespaces, tt.version, tt.helmConfig, tt.devel)
if tt.errorExpected { if tt.errorExpected {
assert.Error(t, err, "Expected error but got nil") assert.Error(t, err, "Expected error but got nil")
} else { } else {

View File

@@ -126,7 +126,14 @@ func (k *K8s) DescribeResource(kind string, ns string, name string) (string, err
func (k *K8s) GetResource(kind string, namespace string, name string) (*runtime.Object, error) { func (k *K8s) GetResource(kind string, namespace string, name string) (*runtime.Object, error) {
builder := k.Factory.NewBuilder() builder := k.Factory.NewBuilder()
resp := builder.Unstructured().NamespaceParam(namespace).Flatten().ResourceNames(kind, name).Do() builder = builder.Unstructured().SingleResourceType()
if namespace != "" {
builder = builder.NamespaceParam(namespace)
} else {
builder = builder.DefaultNamespace()
}
resp := builder.Flatten().ResourceNames(kind, name).Do()
if resp.Err() != nil { if resp.Err() != nil {
return nil, errorx.Decorate(resp.Err(), "failed to get k8s resource") return nil, errorx.Decorate(resp.Err(), "failed to get k8s resource")
} }
@@ -139,6 +146,7 @@ func (k *K8s) GetResource(kind string, namespace string, name string) (*runtime.
} }
func (k *K8s) GetResourceInfo(kind string, namespace string, name string) (*testapiv1.Carp, error) { func (k *K8s) GetResourceInfo(kind string, namespace string, name string) (*testapiv1.Carp, error) {
// TODO: mutex to avoid a lot of requests?
obj, err := k.GetResource(kind, namespace, name) obj, err := k.GetResource(kind, namespace, name)
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to get k8s object") return nil, errorx.Decorate(err, "failed to get k8s object")

View File

@@ -45,6 +45,7 @@ func (a *Releases) List() ([]*Release, error) {
client.All = true client.All = true
client.AllNamespaces = true client.AllNamespaces = true
client.Limit = 0 client.Limit = 0
client.SetStateMask() // required to apply proper filtering
rels, err := client.Run() rels, err := client.Run()
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to get list of releases") return nil, errorx.Decorate(err, "failed to get list of releases")
@@ -319,6 +320,7 @@ func (r *Release) Upgrade(repoChart string, version string, justTemplate bool, v
cmd.Version = version cmd.Version = version
cmd.DryRun = justTemplate cmd.DryRun = justTemplate
cmd.ResetValues = true
chrt, err := locateChart(cmd.ChartPathOptions, repoChart, r.Settings) chrt, err := locateChart(cmd.ChartPathOptions, repoChart, r.Settings)
if err != nil { if err != nil {

View File

@@ -1,20 +1,21 @@
package objects package objects
import ( import (
"os"
"path/filepath"
"strings"
"sync"
"github.com/Masterminds/semver/v3"
"github.com/joomcode/errorx" "github.com/joomcode/errorx"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/repo"
"os"
"path/filepath"
"strings"
"sync"
) )
const AnnRepo = "helm-dashboard/repository-name" const AnnRepo = "helm-dashboard/repository-name"
@@ -23,9 +24,11 @@ type Repositories struct {
Settings *cli.EnvSettings Settings *cli.EnvSettings
HelmConfig *action.Configuration HelmConfig *action.Configuration
mx sync.Mutex mx sync.Mutex
versionConstraint *semver.Constraints
LocalCharts []string
} }
func (r *Repositories) Load() (*repo.File, error) { func (r *Repositories) load() (*repo.File, error) {
r.mx.Lock() r.mx.Lock()
defer r.mx.Unlock() defer r.mx.Unlock()
@@ -37,28 +40,40 @@ func (r *Repositories) Load() (*repo.File, error) {
return f, nil return f, nil
} }
func (r *Repositories) List() ([]*Repository, error) { func (r *Repositories) List() ([]Repository, error) {
f, err := r.Load() f, err := r.load()
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to load repo information") return nil, errorx.Decorate(err, "failed to load repo information")
} }
res := []*Repository{} res := []Repository{}
for _, item := range f.Repositories { for _, item := range f.Repositories {
res = append(res, &Repository{ res = append(res, &HelmRepo{
Settings: r.Settings, Settings: r.Settings,
Orig: item, Orig: item,
versionConstraint: r.versionConstraint,
}) })
} }
if len(r.LocalCharts) > 0 {
lc := LocalChart{
LocalCharts: r.LocalCharts,
}
res = append(res, &lc)
}
return res, nil return res, nil
} }
func (r *Repositories) Add(name string, url string) error { func (r *Repositories) Add(name string, url string, username string, password string) error {
if name == "" || url == "" { if name == "" || url == "" {
return errors.New("Name and URL are required parameters to add the repository") return errors.New("Name and URL are required parameters to add the repository")
} }
if (username != "" && password == "") || (username == "" && password != "") {
return errors.New("Username and Password, both are required parameters to add the repository with authentication")
}
// copied from cmd/helm/repo_add.go // copied from cmd/helm/repo_add.go
repoFile := r.Settings.RepositoryConfig repoFile := r.Settings.RepositoryConfig
@@ -68,7 +83,7 @@ func (r *Repositories) Add(name string, url string) error {
return err return err
} }
f, err := r.Load() f, err := r.load()
if err != nil { if err != nil {
return errorx.Decorate(err, "Failed to load repo config") return errorx.Decorate(err, "Failed to load repo config")
} }
@@ -79,8 +94,8 @@ func (r *Repositories) Add(name string, url string) error {
c := repo.Entry{ c := repo.Entry{
Name: name, Name: name,
URL: url, URL: url,
//Username: o.username, Username: username,
//Password: o.password, Password: password,
//PassCredentialsAll: o.passCredentialsAll, //PassCredentialsAll: o.passCredentialsAll,
//CertFile: o.certFile, //CertFile: o.certFile,
//KeyFile: o.keyFile, //KeyFile: o.keyFile,
@@ -111,7 +126,7 @@ func (r *Repositories) Add(name string, url string) error {
} }
func (r *Repositories) Delete(name string) error { func (r *Repositories) Delete(name string) error {
f, err := r.Load() f, err := r.load()
if err != nil { if err != nil {
return errorx.Decorate(err, "failed to load repo information") return errorx.Decorate(err, "failed to load repo information")
} }
@@ -133,24 +148,22 @@ func (r *Repositories) Delete(name string) error {
return nil return nil
} }
func (r *Repositories) Get(name string) (*Repository, error) { func (r *Repositories) Get(name string) (Repository, error) {
f, err := r.Load() l, err := r.List()
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to load repo information") return nil, errorx.Decorate(err, "failed to get list of repos")
} }
for _, entry := range f.Repositories { for _, entry := range l {
if entry.Name == name { if entry.Name() == name {
return &Repository{ return entry, nil
Settings: r.Settings,
Orig: entry,
}, nil
} }
} }
return nil, errorx.DataUnavailable.New("Could not find reposiroty '%s'", name) return nil, errorx.DataUnavailable.New("Could not find repository '%s'", name)
} }
// Containing returns list of chart versions for the given chart name, across all repositories
func (r *Repositories) Containing(name string) (repo.ChartVersions, error) { func (r *Repositories) Containing(name string) (repo.ChartVersions, error) {
list, err := r.List() list, err := r.List()
if err != nil { if err != nil {
@@ -161,11 +174,12 @@ func (r *Repositories) Containing(name string) (repo.ChartVersions, error) {
for _, rep := range list { for _, rep := range list {
vers, err := rep.ByName(name) vers, err := rep.ByName(name)
if err != nil { if err != nil {
log.Warnf("Failed to get data from repo '%s', updating it might help", rep.Orig.Name) log.Warnf("Failed to get data from repo '%s', updating it might help", rep.Name())
log.Debugf("The error was: %v", err) log.Debugf("The error was: %v", err)
continue continue
} }
var updatedChartVersions repo.ChartVersions
for _, v := range vers { for _, v := range vers {
// just using annotations here to attach a bit of information to the object // just using annotations here to attach a bit of information to the object
// it has nothing to do with k8s annotations and should not get into manifests // it has nothing to do with k8s annotations and should not get into manifests
@@ -173,32 +187,27 @@ func (r *Repositories) Containing(name string) (repo.ChartVersions, error) {
v.Annotations = map[string]string{} v.Annotations = map[string]string{}
} }
v.Annotations[AnnRepo] = rep.Orig.Name v.Annotations[AnnRepo] = rep.Name()
// Validate the versions against semantic version constraints and filter
version, err := semver.NewVersion(v.Version)
if err != nil {
// Ignored if version string is not parsable
log.Debugf("failed to parse version string %q: %v", v.Version, err)
continue
} }
res = append(res, vers...) // TODO filter dev versions here, relates to #139 if r.versionConstraint.Check(version) {
// Add only versions that satisfy the semantic version constraint
updatedChartVersions = append(updatedChartVersions, v)
}
}
res = append(res, updatedChartVersions...)
} }
return res, nil return res, nil
} }
func (r *Repositories) GetChart(chart string, ver string) (*chart.Chart, error) {
// TODO: unused method?
client := action.NewShowWithConfig(action.ShowAll, r.HelmConfig)
client.Version = ver
cp, err := client.ChartPathOptions.LocateChart(chart, r.Settings)
if err != nil {
return nil, errorx.Decorate(err, "failed to locate chart '%s'", chart)
}
chrt, err := loader.Load(cp)
if err != nil {
return nil, errorx.Decorate(err, "failed to load chart from '%s'", cp)
}
return chrt, nil
}
func (r *Repositories) GetChartValues(chart string, ver string) (string, error) { func (r *Repositories) GetChartValues(chart string, ver string) (string, error) {
// comes from cmd/helm/show.go // comes from cmd/helm/show.go
client := action.NewShowWithConfig(action.ShowValues, r.HelmConfig) client := action.NewShowWithConfig(action.ShowValues, r.HelmConfig)
@@ -216,17 +225,35 @@ func (r *Repositories) GetChartValues(chart string, ver string) (string, error)
return out, nil return out, nil
} }
type Repository struct { type Repository interface {
Name() string
URL() string
Update() error
Charts() (repo.ChartVersions, error)
ByName(name string) (repo.ChartVersions, error)
}
type HelmRepo struct {
Settings *cli.EnvSettings Settings *cli.EnvSettings
Orig *repo.Entry Orig *repo.Entry
mx sync.Mutex mx sync.Mutex
versionConstraint *semver.Constraints
} }
func (r *Repository) indexFileName() string { func (r *HelmRepo) Name() string {
return r.Orig.Name
}
func (r *HelmRepo) URL() string {
return r.Orig.URL
}
func (r *HelmRepo) indexFileName() string {
return filepath.Join(r.Settings.RepositoryCache, helmpath.CacheIndexFile(r.Orig.Name)) return filepath.Join(r.Settings.RepositoryCache, helmpath.CacheIndexFile(r.Orig.Name))
} }
func (r *Repository) getIndex() (*repo.IndexFile, error) { func (r *HelmRepo) getIndex() (*repo.IndexFile, error) {
r.mx.Lock() r.mx.Lock()
defer r.mx.Unlock() defer r.mx.Unlock()
@@ -240,23 +267,39 @@ func (r *Repository) getIndex() (*repo.IndexFile, error) {
return ind, nil return ind, nil
} }
func (r *Repository) Charts() ([]*repo.ChartVersion, error) { func (r *HelmRepo) Charts() (repo.ChartVersions, error) {
ind, err := r.getIndex() ind, err := r.getIndex()
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to get repo index") return nil, errorx.Decorate(err, "failed to get repo index")
} }
res := []*repo.ChartVersion{} res := repo.ChartVersions{}
for _, v := range ind.Entries { for _, cv := range ind.Entries {
if len(v) > 0 { // TODO filter dev versions here, relates to #139 for _, v := range cv {
res = append(res, v[0]) version, err := semver.NewVersion(v.Version)
if err != nil {
// Ignored if version string is not parsable
log.Debugf("failed to parse version string %q: %v", v.Version, err)
continue
}
if r.versionConstraint.Check(version) {
// Add only versions that satisfy the semantic version constraint
res = append(res, v)
// Only the highest version satisfying the constraint is required. Hence, break.
// The constraint here is (only stable versions) vs (stable + dev/prerelease).
// If dev versions are disabled and chart only has dev versions,
// chart is excluded from the result.
break
}
} }
} }
return res, nil return res, nil
} }
func (r *Repository) ByName(name string) (repo.ChartVersions, error) { func (r *HelmRepo) ByName(name string) (repo.ChartVersions, error) {
ind, err := r.getIndex() ind, err := r.getIndex()
if err != nil { if err != nil {
return nil, errorx.Decorate(err, "failed to get repo index") return nil, errorx.Decorate(err, "failed to get repo index")
@@ -269,7 +312,7 @@ func (r *Repository) ByName(name string) (repo.ChartVersions, error) {
return repo.ChartVersions{}, nil return repo.ChartVersions{}, nil
} }
func (r *Repository) Update() error { func (r *HelmRepo) Update() error {
r.mx.Lock() r.mx.Lock()
defer r.mx.Unlock() defer r.mx.Unlock()
log.Infof("Updating repository: %s", r.Orig.Name) log.Infof("Updating repository: %s", r.Orig.Name)
@@ -310,3 +353,79 @@ func removeRepoCache(root, name string) error {
} }
return os.Remove(idx) return os.Remove(idx)
} }
// versionConstaint returns semantic version constraint instance that can be used to
// validate the version of repositories. The flag isDevelEnabled is used to configure
// enabling/disabling of development/prerelease versions of charts.
func versionConstaint(isDevelEnabled bool) (*semver.Constraints, error) {
// When devel flag is disabled. i.e., Only stable releases are included.
version := ">0.0.0"
if isDevelEnabled {
// When devel flag is enabled. i.e., Prereleases (alpha, beta, release candidate, etc.) are included.
version = ">0.0.0-0"
}
constraint, err := semver.NewConstraint(version)
if err != nil {
return nil, errors.Wrapf(err, "invalid version constraint format %q", version)
}
return constraint, nil
}
type LocalChart struct {
LocalCharts []string
charts map[string]repo.ChartVersions
mx sync.Mutex
}
// Update reloads the chart information from disk
func (l *LocalChart) Update() error {
l.mx.Lock()
defer l.mx.Unlock()
l.charts = map[string]repo.ChartVersions{}
for _, lc := range l.LocalCharts {
c, err := loader.Load(lc)
if err != nil {
log.Warnf("Failed to load chart from '%s': %s", lc, err)
continue
}
// we don't filter out dev versions here, because local chart implies user wants to see the chart anyway
l.charts[c.Name()] = repo.ChartVersions{&repo.ChartVersion{
URLs: []string{l.URL() + lc},
Metadata: c.Metadata,
}}
}
return nil
}
func (l *LocalChart) Name() string {
return "[local]"
}
func (l *LocalChart) URL() string {
return "file://"
}
func (l *LocalChart) Charts() (repo.ChartVersions, error) {
_ = l.Update() // always re-read, for chart devs to have quick debug loop
res := repo.ChartVersions{}
for _, c := range l.charts {
res = append(res, c...)
}
return res, nil
}
func (l *LocalChart) ByName(name string) (repo.ChartVersions, error) {
_ = l.Update() // always re-read, for chart devs to have quick debug loop
for n, c := range l.charts {
if n == name {
return c, nil
}
}
return repo.ChartVersions{}, nil
}

View File

@@ -1,149 +1,290 @@
package objects package objects
import ( import (
"helm.sh/helm/v3/pkg/action" "io/ioutil"
"os"
"path"
"testing" "testing"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/repo"
) )
var filePath = "./testdata/repositories.yaml" const (
validRepositoryConfigPath = "./testdata/repositories.yaml"
invalidCacheFileRepositoryConfigPath = "./testdata/repositories-invalid-cache-file.yaml"
invalidMalformedManifestRepositoryConfigPath = "./testdata/repositories-malformed-manifest.yaml"
)
func initRepository(t *testing.T, filePath string) *Repositories { func initRepository(t *testing.T, filePath string, devel bool) *Repositories {
t.Helper() t.Helper()
settings := cli.New() settings := cli.New()
fname, err := ioutil.TempFile("", "repo-*.yaml")
if err != nil {
t.Fatal(err)
}
input, err := ioutil.ReadFile(filePath)
if err != nil {
t.Fatal(err)
}
err = ioutil.WriteFile(fname.Name(), input, 0644)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
err := os.Remove(fname.Name())
if err != nil {
t.Fatal(err)
}
})
vc, err := versionConstaint(devel)
if err != nil {
t.Fatal(err)
}
// Sets the repository file path // Sets the repository file path
settings.RepositoryConfig = filePath settings.RepositoryConfig = fname.Name()
settings.RepositoryCache = path.Dir(filePath)
testRepository := &Repositories{ testRepository := &Repositories{
Settings: settings, Settings: settings,
HelmConfig: &action.Configuration{}, // maybe use copy of getFakeHelmConfig from api_test.go HelmConfig: &action.Configuration{}, // maybe use copy of getFakeHelmConfig from api_test.go
versionConstraint: vc,
LocalCharts: []string{"../../../charts/helm-dashboard"},
} }
return testRepository return testRepository
} }
func TestLoadRepo(t *testing.T) { func TestFlow(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, false)
res, err := repo.LoadFile(filePath)
if err != nil {
t.Fatal(err)
}
testRepository := initRepository(t, filePath)
file, err := testRepository.Load()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, file.Generated, res.Generated)
}
func TestList(t *testing.T) {
res, err := repo.LoadFile(filePath)
if err != nil {
t.Fatal(err)
}
testRepository := initRepository(t, filePath)
// initial list
repos, err := testRepository.List() repos, err := testRepository.List()
assert.NilError(t, err)
assert.Equal(t, len(repos), 5)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, len(repos), len(res.Repositories))
}
func TestAdd(t *testing.T) {
testRepoName := "TEST" testRepoName := "TEST"
testRepoUrl := "https://helm.github.io/examples" testRepoUrl := "https://helm.github.io/examples"
res, err := repo.LoadFile(filePath) // add repo
if err != nil { err = testRepository.Add(testRepoName, testRepoUrl, "", "")
t.Fatal(err) assert.NilError(t, err)
}
// Delete the repository if already exist // get repo
res.Remove(testRepoName) r, err := testRepository.Get(testRepoName)
assert.NilError(t, err)
assert.Equal(t, r.URL(), testRepoUrl)
testRepository := initRepository(t, filePath) // update repo
err = r.Update()
assert.NilError(t, err)
err = testRepository.Add(testRepoName, testRepoUrl) // list charts
c, err := r.Charts()
assert.NilError(t, err)
if err != nil { // contains chart
t.Fatal(err, "Failed to add repo") c, err = testRepository.Containing(c[0].Name)
} assert.NilError(t, err)
// Reload the file // chart by name from repo
res, err = repo.LoadFile(filePath) c, err = r.ByName(c[0].Name)
if err != nil { assert.NilError(t, err)
t.Fatal(err)
}
assert.Equal(t, res.Has(testRepoName), true) // get chart values
v, err := testRepository.GetChartValues(r.Name()+"/"+c[0].Name, c[0].Version)
// Removes test repository which is added for testing assert.NilError(t, err)
t.Cleanup(func() { assert.Assert(t, v != "")
removed := res.Remove(testRepoName)
if removed != true {
t.Log("Failed to clean the test repository file")
}
err = res.WriteFile(filePath, 0644)
if err != nil {
t.Log("Failed to write the file while cleaning test repo")
}
})
}
func TestDelete(t *testing.T) {
testRepoName := "TEST DELETE"
testRepoUrl := "https://helm.github.io/examples"
res, err := repo.LoadFile(filePath)
if err != nil {
t.Fatal(err)
}
// Add a test entry
res.Add(&repo.Entry{Name: testRepoName, URL: testRepoUrl})
err = res.WriteFile(filePath, 0644)
if err != nil {
t.Fatal("Failed to write the file while creating test repo")
}
testRepository := initRepository(t, filePath)
// delete added
err = testRepository.Delete(testRepoName) err = testRepository.Delete(testRepoName)
if err != nil { assert.NilError(t, err)
t.Fatal(err, "Failed to delete the repo")
// final list
repos, err = testRepository.List()
assert.NilError(t, err)
assert.Equal(t, len(repos), 5)
} }
// Reload the file func TestRepository_Charts_DevelDisabled(t *testing.T) {
res, err = repo.LoadFile(filePath) testRepository := initRepository(t, validRepositoryConfigPath, false)
r, err := testRepository.Get("testing")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(t, res.Has(testRepoName), false) charts, err := r.Charts()
}
func TestGet(t *testing.T) {
// Initial repositiry name in test file
repoName := "charts"
testRepository := initRepository(t, filePath)
repo, err := testRepository.Get(repoName)
if err != nil { if err != nil {
t.Fatal(err, "Failed to get th repo") t.Fatal(err)
} }
assert.Equal(t, repo.Orig.Name, repoName) // Total charts in ./testdata/testing-index.yaml = 4
// Excluded charts = 2 (1 has invalid version, 1 has only dev version)
// Included charts = 2 (2 stable versions)
expectedCount := 2
if len(charts) != expectedCount {
t.Fatalf("Wrong charts count: %d, expected: %d", len(charts), expectedCount)
}
}
func TestRepository_Charts_DevelEnabled(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, true)
r, err := testRepository.Get("testing")
if err != nil {
t.Fatal(err)
}
charts, err := r.Charts()
if err != nil {
t.Fatal(err)
}
// Total charts in ./testdata/testing-index.yaml = 4
// Excluded charts = 1 (1 has invalid version)
// Included charts = 3 (2 stable versions, 1 has only dev version)
expectedCount := 3
if len(charts) != expectedCount {
t.Fatalf("Wrong charts count: %d, expected: %d", len(charts), expectedCount)
}
}
func TestRepository_Charts_InvalidCacheFile(t *testing.T) {
testRepository := initRepository(t, invalidCacheFileRepositoryConfigPath, false)
r, err := testRepository.Get("non-existing")
if err != nil {
t.Fatal(err)
}
_, err = r.Charts()
if err == nil {
t.Fatalf("Expected error for invalid cache file path, got nil")
}
}
func TestRepositories_Containing_DevelDisable(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, false)
chartVersions, err := testRepository.Containing("alpine")
if err != nil {
t.Fatal(err)
}
// Total versions of chart alpine in ./testdata/testing-index.yaml = 3
// Excluded charts = 1 (1 dev version)
// Included charts = 2 (2 stable versions)
expectedCount := 2
if len(chartVersions) != expectedCount {
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
}
}
func TestRepositories_Containing_DevelEnabled(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, true)
chartVersions, err := testRepository.Containing("alpine")
if err != nil {
t.Fatal(err)
}
// Total versions of chart alpine in ./testdata/testing-index.yaml = 3
// Excluded charts = 0
// Included charts = 3 (2 stable versions, 1 dev version)
expectedCount := 3
if len(chartVersions) != expectedCount {
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
}
}
func TestRepositories_Containing_DevelDisable_OnlyDevVersionsOfChartAvailable(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, false)
chartVersions, err := testRepository.Containing("traefik")
if err != nil {
t.Fatal(err)
}
// Total versions of chart traefik in ./testdata/testing-index.yaml = 1
// Excluded charts = 1 (1 dev version)
// Included charts = 0
expectedCount := 0
if len(chartVersions) != expectedCount {
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
}
}
func TestRepositories_Containing_DevelEnabled_OnlyDevVersionsOfChartAvailable(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, true)
chartVersions, err := testRepository.Containing("traefik")
if err != nil {
t.Fatal(err)
}
// Total versions of chart traefik in ./testdata/testing-index.yaml = 1
// Excluded charts = 0
// Included charts = 1 (1 dev version)
expectedCount := 1
if len(chartVersions) != expectedCount {
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
}
}
func TestRepositories_Containing_DevelDisable_InvalidChartVersion(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, false)
chartVersions, err := testRepository.Containing("rabbitmq")
if err != nil {
t.Fatal(err)
}
// Total versions of chart rabbitmq in ./testdata/testing-index.yaml = 1
// Excluded charts = 1 (1 invalid version)
// Included charts = 0
expectedCount := 0
if len(chartVersions) != expectedCount {
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
}
}
func TestRepositories_Containing_DevelEnabled_InvalidChartVersion(t *testing.T) {
testRepository := initRepository(t, validRepositoryConfigPath, true)
chartVersions, err := testRepository.Containing("rabbitmq")
if err != nil {
t.Fatal(err)
}
// Total versions of chart rabbitmq in ./testdata/testing-index.yaml = 1
// Excluded charts = 1 (1 invalid version)
// Included charts = 0
expectedCount := 0
if len(chartVersions) != expectedCount {
t.Fatalf("Wrong charts versions count: %d, expected: %d", len(chartVersions), expectedCount)
}
}
func TestRepositories_Containing_MalformedRepositoryConfigFile(t *testing.T) {
testRepository := initRepository(t, invalidMalformedManifestRepositoryConfigPath, false)
_, err := testRepository.Containing("alpine")
if err == nil {
t.Fatalf("Expected error for malformed RepositoryConfig file, got nil")
}
} }

View File

@@ -0,0 +1,6 @@
apiVersion: ""
generated: "0001-01-01T00:00:00Z"
repositories:
- cache: non-existing-index.yaml
name: non-existing
url: http://example.com/charts

View File

@@ -0,0 +1,12 @@
apiVersion: ""
generated: "0001-01-01T00:00:00Z"
- repositories:
- caFile: ""
certFile: ""
insecure_skip_tls_verify: false
keyFile: ""
name: charts
pass_credentials_all: false
password: ""
url: https://charts.helm.sh/stable
username: ""

View File

@@ -28,3 +28,6 @@ repositories:
password: "" password: ""
url: http://secondexample.com url: http://secondexample.com
username: "" username: ""
- cache: testing-index.yaml
name: testing
url: http://example.com/charts

View File

@@ -0,0 +1,100 @@
apiVersion: v1
entries:
alpine:
- name: alpine
url: https://charts.helm.sh/stable/alpine-0.1.0.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
created: "2018-06-27T10:00:18.230700509Z"
deprecated: true
home: https://helm.sh/helm
sources:
- https://github.com/helm/helm
version: 0.1.0
appVersion: 1.2.3
description: Deploy a basic Alpine Linux pod
keywords: []
maintainers: []
icon: ""
apiVersion: v2
- name: alpine
url: https://charts.helm.sh/stable/alpine-0.2.0.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
created: "2018-07-09T11:34:37.797864902Z"
home: https://helm.sh/helm
sources:
- https://github.com/helm/helm
version: 0.2.0
appVersion: 2.3.4
description: Deploy a basic Alpine Linux pod
keywords: []
maintainers: []
icon: ""
apiVersion: v2
- name: alpine
url: https://charts.helm.sh/stable/alpine-0.3.0-rc.1.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
created: "2020-11-12T08:44:58.872726222Z"
home: https://helm.sh/helm
sources:
- https://github.com/helm/helm
version: 0.3.0-rc.1
appVersion: 3.0.0
description: Deploy a basic Alpine Linux pod
keywords: []
maintainers: []
icon: ""
apiVersion: v2
mariadb:
- name: mariadb
url: https://charts.helm.sh/stable/mariadb-0.3.0.tgz
checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56
created: "2018-04-23T08:20:27.160959131Z"
home: https://mariadb.org
sources:
- https://github.com/bitnami/bitnami-docker-mariadb
version: 0.3.0
description: Chart for MariaDB
keywords:
- mariadb
- mysql
- database
- sql
maintainers:
- name: Bitnami
email: containers@bitnami.com
icon: ""
apiVersion: v2
traefik:
- apiVersion: v1
appVersion: 1.7.26
deprecated: true
description: A Traefik based Kubernetes ingress controller with Let's
Encrypt support
home: https://traefik.io/
icon: https://docs.traefik.io/assets/img/traefik.logo.png
keywords:
- traefik
- ingress
- acme
- letsencrypt
name: traefik
sources:
- https://github.com/containous/traefik
- https://github.com/helm/charts/tree/master/stable/traefik
version: 1.87.7-rc1
rabbitmq:
- apiVersion: v1
appVersion: 3.8.2
deprecated: true
description: DEPRECATED Open source message broker software that implements the Advanced
Message Queuing Protocol (AMQP)
home: https://www.rabbitmq.com
icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png
keywords:
- rabbitmq
- message queue
- AMQP
name: rabbitmq
sources:
- https://github.com/bitnami/bitnami-docker-rabbitmq
version: invalid-version

View File

@@ -4,16 +4,17 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"os"
"strings"
"time"
"github.com/joomcode/errorx" "github.com/joomcode/errorx"
"github.com/komodorio/helm-dashboard/pkg/dashboard/objects" "github.com/komodorio/helm-dashboard/pkg/dashboard/objects"
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc" "github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/registry"
"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"
@@ -28,20 +29,24 @@ type Server struct {
Address string Address string
Debug bool Debug bool
NoTracking bool NoTracking bool
Devel bool
LocalCharts []string
} }
func (s *Server) StartServer(ctx context.Context, cancel context.CancelFunc) (string, utils.ControlChan, error) { func (s *Server) StartServer(ctx context.Context, cancel context.CancelFunc) (string, utils.ControlChan, error) {
data, err := objects.NewDataLayer(s.Namespaces, s.Version, NewHelmConfig) data, err := objects.NewDataLayer(s.Namespaces, s.Version, NewHelmConfig, s.Devel)
if err != nil { if err != nil {
return "", nil, errorx.Decorate(err, "Failed to create data layer") return "", nil, errorx.Decorate(err, "Failed to create data layer")
} }
data.LocalCharts = s.LocalCharts
isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true" isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true"
data.StatusInfo.Analytics = (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics data.StatusInfo.Analytics = (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics
err = s.detectClusterMode(data) err = s.detectClusterMode(data)
if err != nil { if err != nil {
return "", nil, err return "", nil, errorx.Decorate(err, "Failed to detect cluster mode")
} }
go checkUpgrade(data.StatusInfo) go checkUpgrade(data.StatusInfo)
@@ -75,7 +80,7 @@ func (s *Server) detectClusterMode(data *objects.DataLayer) error {
} }
ns, err := app.K8s.GetNameSpaces() ns, err := app.K8s.GetNameSpaces()
if err != nil { // no point in continuing without kubectl context and k8s connection if err != nil { // no point in continuing without kubectl context and k8s connection
return err return errorx.InitializationFailed.Wrap(err, "No k8s cluster connection")
} }
log.Debugf("Got %d namespaces listed", len(ns.Items)) log.Debugf("Got %d namespaces listed", len(ns.Items))
data.StatusInfo.ClusterMode = true data.StatusInfo.ClusterMode = true

View File

@@ -25,7 +25,11 @@ function checkUpgradeable(name) {
if (!data || !data.length) { if (!data || !data.length) {
btnUpgradeCheck.prop("disabled", true) btnUpgradeCheck.prop("disabled", true)
btnUpgradeCheck.text("") btnUpgradeCheck.text("")
$("#btnAddRepository").text("Add repository for it") $("#btnAddRepository").text("Add repository for it").data("suggestRepo", "")
} else if (data[0].isSuggestedRepo) {
btnUpgradeCheck.prop("disabled", true)
btnUpgradeCheck.text("")
$("#btnAddRepository").text("Add repository for it: "+data[0].repository).data("suggestRepo", data[0].repository).data("suggestRepoUrl", data[0].urls[0])
} else { } else {
$("#btnAddRepository").text("") $("#btnAddRepository").text("")
btnUpgradeCheck.text("Check for new version") btnUpgradeCheck.text("Check for new version")
@@ -56,13 +60,7 @@ function checkUpgradeable(name) {
function popUpUpgrade(elm, ns, name, verCur, lastRev) { function popUpUpgrade(elm, ns, name, verCur, lastRev) {
$("#upgradeModal .btn-confirm").prop("disabled", true) $("#upgradeModal .btn-confirm").prop("disabled", true)
let chart = elm.repository + "/" + elm.name; $('#upgradeModal').data("initial", !verCur)
if (!elm.name) {
chart = ""
}
$('#upgradeModal').data("chart", chart).data("initial", !verCur)
$('#upgradeModal form .chart-name').val(chart)
$('#upgradeModal').data("newManifest", "") $('#upgradeModal').data("newManifest", "")
$("#upgradeModalLabel .name").text(elm.name) $("#upgradeModalLabel .name").text(elm.name)
@@ -93,23 +91,26 @@ function popUpUpgrade(elm, ns, name, verCur, lastRev) {
$.getJSON("/api/helm/repositories/versions?name=" + elm.name).fail(function (xhr) { $.getJSON("/api/helm/repositories/versions?name=" + elm.name).fail(function (xhr) {
reportError("Failed to find chart in repo", xhr) reportError("Failed to find chart in repo", xhr)
}).done(function (vers) { }).done(function (vers) {
vers.sort((b, a) => (a.version > b.version) - (a.version < b.version))
// fill versions // fill versions
$('#upgradeModal select').empty() $('#upgradeModal select').empty()
for (let i = 0; i < vers.length; i++) { for (let i = 0; i < vers.length; i++) {
const opt = $("<option value='" + vers[i].version + "'></option>"); const opt = $("<option value='" + vers[i].version + "'></option>").data("ver", vers[i]);
const label = vers[i].repository + " @ " + vers[i].version;
if (vers[i].version === verCur) { if (vers[i].version === verCur) {
opt.html(vers[i].version + " &middot;") opt.html(label + " ")
} else { } else {
opt.html(vers[i].version) opt.html(label)
} }
$('#upgradeModal select').append(opt) $('#upgradeModal select').append(opt)
} }
$('#upgradeModal select').val(elm.version).trigger("change").parent().show() $('#upgradeModal select').val(elm.version).parent().show()
upgrPopUpCommon(verCur, ns, lastRev, name) upgrPopUpCommon(verCur, ns, lastRev, name)
}) })
} else { // chart without repo reconfigure } else { // chart without repo reconfigure
$('#upgradeModal select').empty().trigger("change").parent().hide() $('#upgradeModal select').empty().parent().hide()
upgrPopUpCommon(verCur, ns, lastRev, name) upgrPopUpCommon(verCur, ns, lastRev, name)
} }
} }
@@ -124,9 +125,11 @@ function upgrPopUpCommon(verCur, ns, lastRev, name) {
reportError("Failed to get charts values info", xhr) reportError("Failed to get charts values info", xhr)
}).done(function (data) { }).done(function (data) {
$("#upgradeModal textarea").val(data).data("dirty", false) $("#upgradeModal textarea").val(data).data("dirty", false)
$('#upgradeModal select').trigger("change")
}) })
} else { } else {
$("#upgradeModal textarea").val("").data("dirty", true) $("#upgradeModal textarea").val("").data("dirty", true)
$('#upgradeModal select').trigger("change")
} }
} }
@@ -162,9 +165,7 @@ function changeTimer() {
if (reconfigTimeout) { if (reconfigTimeout) {
window.clearTimeout(reconfigTimeout) window.clearTimeout(reconfigTimeout)
} }
reconfigTimeout = window.setTimeout(function () { reconfigTimeout = window.setTimeout(requestChangeDiff, 500)
requestChangeDiff()
}, 500)
} }
$("#upgradeModal textarea").keyup(changeTimer) $("#upgradeModal textarea").keyup(changeTimer)
@@ -173,12 +174,25 @@ $("#upgradeModal .rel-ns").keyup(changeTimer)
$('#upgradeModal select').change(function () { $('#upgradeModal select').change(function () {
const self = $(this) const self = $(this)
const ver = self.find("option:selected").data("ver");
let chart = ""
if (ver) {
chart = ver.repository + "/" + ver.name;
// local chart case
if (ver.urls && ver.urls.length && ver.urls[0].startsWith("file://")) {
chart = ver.urls[0];
}
}
$('#upgradeModal').data("chart", chart)
$('#upgradeModal form .chart-name').val(chart)
requestChangeDiff() requestChangeDiff()
// fill reference values // fill reference values
$("#upgradeModal .ref-vals").html('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>') $("#upgradeModal .ref-vals").html('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
const chart = $("#upgradeModal").data("chart");
// TODO: if chart is empty, query different URL that will restore values without repo // TODO: if chart is empty, query different URL that will restore values without repo
if (chart) { if (chart) {
$.get("/api/helm/repositories/values?chart=" + chart + "&version=" + self.val()).fail(function (xhr) { $.get("/api/helm/repositories/values?chart=" + chart + "&version=" + self.val()).fail(function (xhr) {
@@ -231,7 +245,6 @@ $('#upgradeModal .btn-scan').click(function () {
}) })
function requestChangeDiff() { function requestChangeDiff() {
const self = $('#upgradeModal select');
const diffBody = $("#upgradeModalBody"); const diffBody = $("#upgradeModalBody");
diffBody.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Calculating diff...') diffBody.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Calculating diff...')
$("#upgradeModal .btn-confirm").prop("disabled", true) $("#upgradeModal .btn-confirm").prop("disabled", true)
@@ -390,7 +403,12 @@ $("#btnRollback").click(function () {
}) })
$("#btnAddRepository").click(function () { $("#btnAddRepository").click(function () {
const self=$(this)
setHashParam("section", "repository") setHashParam("section", "repository")
if (self.data("suggestRepo")) {
setHashParam("suggestRepo", self.data("suggestRepo"))
setHashParam("suggestRepoUrl", self.data("suggestRepoUrl"))
}
window.location.reload() window.location.reload()
}) })

View File

@@ -1,11 +1,28 @@
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
const TRACK_EVENT_TYPE = "track"
const IDENTIFY_EVENT_TYPE = "identify"
const BASE_ANALYTIC_MSG = {
method: "POST",
mode: "cors",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
"api-key": "komodor.analytics@admin.com",
},
redirect: "follow",
referrerPolicy: "no-referrer"
}
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);
const version = status.CurVer const version = status.CurVer
if (status.Analytics) { if (status.Analytics) {
enableDD(version) enableDD(version)
enableHeap(version, status.ClusterMode) enableHeap(version, status.ClusterMode)
enableSegmentBackend(version, status.ClusterMode)
} else {
console.log("Analytics is disabled in this session")
} }
} }
} }
@@ -67,3 +84,48 @@ function sendStats(name, prop){
window.heap.track(name, prop); window.heap.track(name, prop);
} }
} }
function enableSegmentBackend(version, ClusterMode) {
sendToSegmentThroughAPI("helm dashboard loaded", {version, 'installationMode': ClusterMode ? "cluster" : "local"}, TRACK_EVENT_TYPE)
}
function sendToSegmentThroughAPI(eventName, properties, segmentCallType) {
const userId = getUserId();
try {
sendData(properties, segmentCallType, userId, eventName);
} catch (e) {
console.log("failed sending data to segment", e);
}
}
function sendData(data, eventType, userId, eventName) {
const body = createBody(eventType, userId, data, eventName);
return fetch(`https://api.komodor.com/analytics/segment/${eventType}`, {
...BASE_ANALYTIC_MSG,
body: JSON.stringify(body),
});
}
function createBody(segmentCallType, userId, params, eventName) {
const data = {userId: userId};
if (segmentCallType === IDENTIFY_EVENT_TYPE) {
data["traits"] = params;
} else if (segmentCallType === TRACK_EVENT_TYPE) {
if (!eventName) {
throw new Error("no eventName parameter on segment track call");
}
data["properties"] = params;
data["eventName"] = eventName;
}
return data;
}
const getUserId = (() => {
let userId = null;
return () => {
if (!userId) {
userId = crypto.randomUUID();
}
return userId;
};
})();

View File

@@ -67,4 +67,5 @@
reqOas(); reqOas();
}); });
</script> </script>
<script src="analytics.js"></script>
</html> </html>

View File

@@ -149,7 +149,7 @@ function showResources(namespace, chart, revision) {
const resBody = $("#nav-resources .body"); const resBody = $("#nav-resources .body");
const interestingResources = ["STATEFULSET", "DEAMONSET", "DEPLOYMENT"]; 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 url = "/api/helm/releases/" + namespace + "/" + chart + "/resources" let url = "/api/helm/releases/" + namespace + "/" + chart + "/resources?health=true"
$.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) {
@@ -180,23 +180,32 @@ function showResources(namespace, chart, revision) {
resBody.append(resBlock) resBody.append(resBlock)
let ns = res.metadata.namespace ? res.metadata.namespace : namespace let ns = res.metadata.namespace ? res.metadata.namespace : namespace
$.getJSON("/api/k8s/" + res.kind.toLowerCase() + "/get?name=" + res.metadata.name + "&namespace=" + ns).fail(function () { for (let k = 0; k < res.status.conditions.length; k++) {
//reportError("Failed to get list of resources") if (res.status.conditions[k].type !== "hdHealth") { // it's our custom condition type
}).done(function (data) { continue
const badge = $("<span class='badge me-2 fw-normal'></span>").text(data.status.phase); }
if (["Available", "Active", "Established", "Bound", "Ready"].includes(data.status.phase)) {
const cond = res.status.conditions[k]
const badge = $("<span class='badge me-2 fw-normal'></span>").text(cond.reason);
if (cond.status === "Unknown") {
badge.addClass("bg-secondary text-danger")
} else if (cond.status === "Healthy") {
badge.addClass("bg-success text-dark") badge.addClass("bg-success text-dark")
} else if (["Exists"].includes(data.status.phase)) { } else if (cond.status === "Progressing") {
badge.addClass("bg-success text-dark bg-opacity-50")
} else if (["Progressing"].includes(data.status.phase)) {
badge.addClass("bg-warning") badge.addClass("bg-warning")
} else { } else {
badge.addClass("bg-danger") badge.addClass("bg-danger")
} }
if (["Exists"].includes(cond.reason)) {
badge.addClass("bg-opacity-50")
}
const statusBlock = resBlock.find(".res-status"); const statusBlock = resBlock.find(".res-status");
statusBlock.empty().append(badge).attr("title", data.status.phase) statusBlock.empty().append(badge).attr("title", cond.reason)
const statusMessage = getStatusMessage(data.status) const statusMessage = cond.message
resBlock.find(".res-statusmsg").html("<span class='text-muted small'>" + (statusMessage ? statusMessage : '') + "</span>") resBlock.find(".res-statusmsg").html("<span class='text-muted small me-2'>" + (statusMessage ? statusMessage : '') + "</span>")
if (badge.text() !== "NotFound" && revision == $("#specRev").data("last-rev")) { if (badge.text() !== "NotFound" && revision == $("#specRev").data("last-rev")) {
resBlock.find(".res-actions") resBlock.find(".res-actions")
@@ -215,19 +224,13 @@ function showResources(namespace, chart, revision) {
}) })
} }
} }
})
}
})
}
function getStatusMessage(status) { if (badge.hasClass("bg-danger") || badge.hasClass("bg-warning")) {
if (!status) { resBlock.find(".res-statusmsg").append("<a href='" + KomodorCTALink + "' class='btn btn-primary btn-sm fw-normal fs-80' target='_blank'>Troubleshoot in Komodor <i class='bi-box-arrow-up-right'></i></a>")
return
} }
if (status.conditions) {
return status.conditions[0].message || status.conditions[0].reason
} }
return status.message || status.reason }
})
} }
function showDescribe(ns, kind, name, badge) { function showDescribe(ns, kind, name, badge) {

View File

@@ -64,15 +64,20 @@
</li> </li>
<li class="nav-item mx-2 display-none upgrade-possible"> <li class="nav-item mx-2 display-none upgrade-possible">
<a class="nav-link position-relative text-danger" <a class="nav-link position-relative text-danger"
href="https://github.com/komodorio/helm-dashboard#installing" target="_blank"> href="https://github.com/komodorio/helm-dashboard/releases" target="_blank">
Upgrade to <span id="toolVersionUpgrade"></span> Upgrade to <span id="toolVersionUpgrade"></span>
</a></li> </a></li>
</ul> </ul>
<div> <div>
<a class="btn" href="https://komodor.com/?utm_campaign=Helm-Dash&utm_source=helm-dash"><img <div class="border-muted text-muted border rounded p-1 pe-2 me-3 d-flex">
src="static/komodor-logo.svg" alt="komodor.io" <img alt="Komodor" src="https://raw.githubusercontent.com/komodorio/helm-charts/master/k8s-watcher.svg" class="me-2" style="width: 42px; height: 42px"/>
style="height: 1.2rem; vertical-align: text-bottom; filter: grayscale(00%);"></a> <span class="text-nowrap">
<a href="https://www.komodor.com/helm-dash/?utm_campaign=Helm%20Dashboard%20%7C%20CTA&utm_source=helm-dash&utm_medium=cta&utm_content=helm-dash"
class="link text-primary fw-bold text-decoration-none" target="_blank">Upgrade your HELM experience - Free
<i class="bi-box-arrow-up-right ms-1"></i></a><br/>
Auth & RBAC, k8s events, troubleshooting and more
</span>
</div>
</div> </div>
<div class="separator-vertical"><span></span></div> <div class="separator-vertical"><span></span></div>
<i class="btn bi-power text-muted p-2 m-1 mx-2" title="Shut down the Helm Dashboard application"></i> <i class="btn bi-power text-muted p-2 m-1 mx-2" title="Shut down the Helm Dashboard application"></i>
@@ -90,6 +95,9 @@
<button class="btn btn-sm border-secondary text-muted"> <button class="btn btn-sm border-secondary text-muted">
<i class="bi-plus-lg"></i> Add Repository <i class="bi-plus-lg"></i> Add Repository
</button> </button>
<div class="mt-2 p-2 small">Charts developers: you can also add local directories as chart source. Use
<span class="font-monospace text-success">--local-chart</span> CLI switch to specify it.
</div>
</div> </div>
</div> </div>
<div class="col-9 repo-details bg-white b-shadow pt-4 px-5 overflow-auto rounded"> <div class="col-9 repo-details bg-white b-shadow pt-4 px-5 overflow-auto rounded">
@@ -154,14 +162,6 @@
placeholder="Filter..."/> placeholder="Filter..."/>
</div> </div>
</div> </div>
<div class="bg-secondary rounded-bottom m-0 row p-2">
<div class="col-4 hdr-name">Name</div>
<div class="col-3">Chart Status</div>
<div class="col-2">Chart</div>
<div class="col-1">Revision</div>
<div class="col-1">Namespace</div>
<div class="col-1">Updated</div>
</div>
</div> </div>
<div class="body"></div> <div class="body"></div>
@@ -305,6 +305,8 @@
<hr> <hr>
<p style="white-space: pre-wrap"></p> <p style="white-space: pre-wrap"></p>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<hr/>
<span class="small text-muted fs-80">Hint: Komodor has the same HELM capabilities, with enterprise features and support. <a href="https://www.komodor.com/helm-dash/?utm_campaign=Helm%20Dashboard%20%7C%20CTA&utm_source=helm-dash&utm_medium=cta&utm_content=helm-dash" target="_blank">Sign up for free.</a></span>
</div> </div>
<div class="offcanvas offcanvas-end rounded-start" tabindex="-1" id="describeModal" <div class="offcanvas offcanvas-end rounded-start" tabindex="-1" id="describeModal"
@@ -314,8 +316,12 @@
<h5 id="describeModalLabel"></h5> <h5 id="describeModalLabel"></h5>
<p class="m-0 mt-4">ResourceType</p> <p class="m-0 mt-4">ResourceType</p>
</div> </div>
<div>
<a href='https://www.komodor.com/helm-dash/?utm_campaign=Helm%20Dashboard%20%7C%20CTA&utm_source=helm-dash&utm_medium=cta&utm_content=helm-dash'
class='btn btn-primary btn-sm me-2' target='_blank'>See more details in Komodor <i
class='bi-box-arrow-up-right'></i></a>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button> <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
</div> </div>
<div class="offcanvas-body p-2 ps-4" id="describeModalBody"> <div class="offcanvas-body p-2 ps-4" id="describeModalBody">
</div> </div>
@@ -348,8 +354,26 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form enctype="application/x-www-form-urlencoded"> <form enctype="application/x-www-form-urlencoded">
<label class="form-label">Name: <input class="form-control" name="name"></label> <div class="row mb-4">
<label class="form-label">URL: <input class="form-control" name="url"></label> <div class="col">
<label class="form-label required">Name</label>
<input class="form-control" type="text" name="name" placeholder="Komodorio">
</div>
<div class="col">
<label class="form-label required">URL</label>
<input class="form-control" type="text" name="url" placeholder="https://helm-charts.komodor.io">
</div>
</div>
<div class="row">
<div class="col">
<label class="form-label">Username</label>
<input class="form-control" type="text" name="username">
</div>
<div class="col">
<label class="form-label">Password</label>
<input class="form-control" type="password" name="password">
</div>
</div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@@ -479,47 +503,5 @@
<script src="static/actions.js"></script> <script src="static/actions.js"></script>
<script src="static/scripts.js"></script> <script src="static/scripts.js"></script>
<!-- BANNER START -->
<a id="banner"
href="https://helm-dashboard-survey.komodor.com/"
class="display-none position-absolute top-0 start-50 translate-middle-x bg-primary text-light rounded px-2 mt-1 text-decoration-none py-1">Help
shaping the future by participating in user survey <b class="bi-x-lg"></b></a>
<script>
function setCookie(name, value, days) {
let expires = "";
if (days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
const c = ca[i].trim();
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length)
}
}
return null;
}
const cookie = getCookie("hideBanner");
if (cookie == null) {
console.log("show")
$("#banner").show()
}
$("#banner b").click(function (evt) {
evt.preventDefault()
setCookie("hideBanner", "1", 365);
$("#banner").hide()
})
</script>
<!-- /BANNER END -->
</body> </body>
</html> </html>

View File

@@ -79,6 +79,63 @@ function buildChartCard(elm) {
loadChartHistory(chart.namespace, chart.name, elm.chart_name) loadChartHistory(chart.namespace, chart.name, elm.chart_name)
}) })
// check if upgrade is possible
$.getJSON("/api/helm/repositories/latestver?name=" + elm.chartName).fail(function (xhr) {
reportError("Failed to find chart in repo", xhr)
}).done(function (data) {
if (!data || !data.length) {
return
}
if (isNewerVersion(elm.chartVersion, data[0].version) || data[0].isSuggestedRepo) {
const icon = $("<br/><span class='fw-bold' data-bs-toggle='tooltip' data-bs-placement='bottom'></span>")
if (data[0].isSuggestedRepo) {
icon.addClass("bi-plus-circle-fill text-primary")
icon.text(" ADD REPO")
icon.attr("data-bs-title", "Add '" + data[0].repository + "' to list of known repositories")
} else {
icon.addClass("bi-arrow-up-circle-fill text-primary")
icon.text(" UPGRADE")
icon.attr("data-bs-title", "Upgrade available: " + data[0].version + " from " + data[0].repository)
}
card.find(".rel-chart div").append(icon)
const tooltipTriggerList = card.find('.rel-chart [data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
sendStats('upgradeIconShown', {'isProbable': data[0].isSuggestedRepo})
}
})
// check resource health status
$.getJSON("/api/helm/releases/" + elm.namespace + "/" + elm.name + "/resources?health=true").fail(function (xhr) {
reportError("Failed to find chart in repo", xhr)
}).done(function (data) {
for (let i = 0; i < data.length; i++) {
const res = data[i]
for (let k = 0; k < res.status.conditions.length; k++) {
if (res.status.conditions[k].type !== "hdHealth") { // it's our custom condition type
continue
}
const cond = res.status.conditions[k]
const square=$("<span class='me-1 mb-1 square rounded rounded-1' data-bs-toggle='tooltip'>&nbsp;</span>")
if (cond.status === "Healthy") {
square.addClass("bg-success")
} else if (cond.status === "Progressing") {
square.addClass("bg-warning")
} else {
square.addClass("bg-danger")
}
square.attr("data-bs-title", cond.status+" "+res.kind+" '"+res.metadata.name+"'")
card.find(".rel-status div").append(square)
}
}
const tooltipTriggerList = card.find('.rel-status [data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
})
return card; return card;
} }

View File

@@ -302,6 +302,11 @@
"name": "name", "name": "name",
"in": "path", "in": "path",
"description": "Name of Helm release" "description": "Name of Helm release"
},
{
"name": "health",
"in": "query",
"description": "Flag to query k8s health status of resources"
} }
], ],
"get": { "get": {

View File

@@ -2,6 +2,13 @@ function loadRepoView() {
$("#sectionRepo .repo-details").hide() $("#sectionRepo .repo-details").hide()
$("#sectionRepo").show() $("#sectionRepo").show()
$("#repoAddModal input[name=name]").val(getHashParam("suggestRepo"))
$("#repoAddModal input[name=url]").val(getHashParam("suggestRepoUrl"))
if (getHashParam("suggestRepo")) {
$("#sectionRepo .repo-list .btn").click()
}
$.getJSON("/api/helm/repositories").fail(function (xhr) { $.getJSON("/api/helm/repositories").fail(function (xhr) {
reportError("Failed to get list of repositories", xhr) reportError("Failed to get list of repositories", xhr)
sendStats('Get repo', {'status': 'fail'}); sendStats('Get repo', {'status': 'fail'});
@@ -10,7 +17,7 @@ function loadRepoView() {
data.sort((a, b) => (a.name > b.name) - (a.name < b.name)) 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="repo" class="me-2"/><span></span></label></li>');
opt.attr('title', elm.url) opt.attr('title', elm.url)
opt.find("input").val(elm.name).text(elm.name).data("item", elm) opt.find("input").val(elm.name).text(elm.name).data("item", elm)
opt.find("span").text(elm.name) opt.find("span").text(elm.name)
@@ -30,6 +37,8 @@ function loadRepoView() {
$("#sectionRepo .repo-details h2").text(elm.name) $("#sectionRepo .repo-details h2").text(elm.name)
$("#sectionRepo .repo-details .url").text(elm.url) $("#sectionRepo .repo-details .url").text(elm.url)
$("#sectionRepo .btn-remove").prop("disabled", elm.url.startsWith('file://'))
$("#sectionRepo .repo-details ul").html('<span class="spinner-border spinner-border-sm mx-1" role="status" aria-hidden="true"></span>') $("#sectionRepo .repo-details ul").html('<span class="spinner-border spinner-border-sm mx-1" role="status" aria-hidden="true"></span>')
$.getJSON("/api/helm/repositories/" + elm.name).fail(function (xhr) { $.getJSON("/api/helm/repositories/" + elm.name).fail(function (xhr) {
reportError("Failed to get list of charts in repo", xhr) reportError("Failed to get list of charts in repo", xhr)
@@ -83,6 +92,8 @@ $("#inputSearch").keyup(function () {
}) })
$("#sectionRepo .repo-list .btn").click(function () { $("#sectionRepo .repo-list .btn").click(function () {
setHashParam("suggestRepo", null)
setHashParam("suggestRepoUrl", null)
const myModal = new bootstrap.Modal(document.getElementById('repoAddModal'), {}); const myModal = new bootstrap.Modal(document.getElementById('repoAddModal'), {});
myModal.show() myModal.show()
}) })

View File

@@ -117,8 +117,8 @@ $("#topNav ul a").click(function () {
initView() initView()
}) })
const myAlert = document.getElementById('errorAlert') const errAlert = document.getElementById('errorAlert')
myAlert.addEventListener('close.bs.alert', event => { errAlert.addEventListener('close.bs.alert', event => {
event.preventDefault() event.preventDefault()
$("#errorAlert").hide() $("#errorAlert").hide()
}) })
@@ -129,6 +129,7 @@ function reportError(err, xhr) {
$("#errorAlert p").text(xhr.responseText) $("#errorAlert p").text(xhr.responseText)
} }
$("#errorAlert").show() $("#errorAlert").show()
sendStats("errorReported", {"errMessage": err})
} }
@@ -357,3 +358,5 @@ function setFilteredNamespaces(filteredNamespaces) {
setHashParam("filteredNamespace", filteredNamespaces.join('+')) setHashParam("filteredNamespace", filteredNamespaces.join('+'))
} }
} }
const KomodorCTALink="https://www.komodor.com/helm-dash/?utm_campaign=Helm%20Dashboard%20%7C%20CTA&utm_source=helm-dash&utm_medium=cta&utm_content=helm-dash"

View File

@@ -79,3 +79,8 @@
.fs-80 { .fs-80 {
font-size: 0.8rem!important; font-size: 0.8rem!important;
} }
.required::after {
content: " *";
color: red;
}

View File

@@ -301,3 +301,26 @@ nav .nav-tabs .nav-link.active {
.test-result { .test-result {
font-size: 1rem; font-size: 1rem;
} }
.square {
width: 0.55rem;
height: 0.55rem;
display: inline-block;
border-radius: 0.1rem!important;
}
.square.bg-danger {
background-color: #ff0072!important;
}
.square.bg-warning {
background-color: #ffa800!important;
}
.square.bg-success {
background-color: #00c2ab!important;
}
.hljs {
overflow-x: inherit;
}

View File

@@ -1,5 +1,5 @@
name: "dashboard" name: "dashboard"
version: "0.3.1" version: "1.3.0"
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"

View File

@@ -2,7 +2,7 @@
# Copied w/ love from the chartmuseum/helm-push :) # Copied w/ love from the chartmuseum/helm-push :)
[ -z "$HELM_DEBUG" ] || set -x [ ! -z "$HELM_DEBUG" ] && set -x
name="helm-dashboard" name="helm-dashboard"
repo="https://github.com/komodorio/${name}" repo="https://github.com/komodorio/${name}"